linstrom/config/config.go
mStar 8709238859 Added goals, cache and func to get any remote obj
Cache is for storage, also includes pooled encoders and decoders
goals are things to eventually add to Linstrom
2024-09-06 23:01:57 +02:00

178 lines
5.4 KiB
Go

package config
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
"github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/goutils/other"
)
type ConfigSSL struct {
HandleSSL bool `toml:"handle_ssl"` // Whether Linstrom should handle SSL encryption itself
// If Linstrom is to handle SSL, whether it should use LetsEncrypt for certificates
UseLetsEncrypt *bool `toml:"use_lets_encrypt"`
// Path to the certificate if Linstrom is to handle SSL while not using LetsEncrypt
CertificateFile *string `toml:"certificate_file"`
// Mail adress to use in case of using LetsEncrypt
AdminMail *string `toml:"admin_mail"`
}
type ConfigGeneral struct {
Protocol string `toml:"protocol"` // The protocol with which to access the server publicly (http/https)
// The subdomain under which the server lives (example: "linstrom" if the full domain is linstrom.example.com)
Subdomain *string `toml:"subdomain"`
// The root domain under which the server lives (example: "example.com" if the full domain is linstrom.example.com)
Domain string `toml:"domain"`
// The port on which the server runs on
PrivatePort int `toml:"private_port"`
// The port under which the public can reach the server (useful if running behind a reverse proxy)
PublicPort *int `toml:"public_port"`
}
type ConfigWebAuthn struct {
DisplayName string `toml:"display_name"`
HashingSecret string `toml:"hashing_secret"`
}
type ConfigAdmin struct {
// Name of the server's root admin account
Username string `toml:"username"`
// A one time password used to verify account access to the root admin
// after a server has been created and before the account could be linked to a passkey
FirstTimeSetupOTP string `toml:"first_time_setup_otp"`
}
type ConfigStorage struct {
// Url to the database to use
// If DbIsPostgres is either not set or false, the url is expected to be a path to a sqlite file
// Otherwise, it's expected to be an url to a postgres server
DatabaseUrl string `toml:"database_url"`
// Whether the target of the database url is a postgres server
DbIsPostgres *bool `toml:"db_is_postgres,omitempty"`
// Whether to use Redis for caching in addition to an in memory one
UseRedis bool `toml:"use_redis"`
// Url to redis server. Expected to be set if UseRedis is true
RedisUrl *string `toml:"redis_url,omitempty"`
// The maximum size of the in-memory cache in bytes
MaxInMemoryCacheSize int64 `toml:"max_in_memory_cache_size"`
}
type Config struct {
General ConfigGeneral `toml:"general"`
SSL ConfigSSL `toml:"ssl"`
Admin ConfigAdmin `toml:"admin"`
Webauthn ConfigWebAuthn `toml:"webauthn"`
Storage ConfigStorage `toml:"storage"`
}
var GlobalConfig Config
var defaultConfig Config = Config{
General: ConfigGeneral{
Protocol: "http",
Subdomain: nil,
Domain: "localhost",
PrivatePort: 8080,
PublicPort: nil,
},
SSL: ConfigSSL{
HandleSSL: false,
UseLetsEncrypt: nil,
CertificateFile: nil,
AdminMail: nil,
},
Admin: ConfigAdmin{
Username: "server-admin",
FirstTimeSetupOTP: "Example otp password",
},
Webauthn: ConfigWebAuthn{
DisplayName: "Linstrom",
HashingSecret: "some super secure secret that should never be changed or else password storage breaks",
},
Storage: ConfigStorage{
DatabaseUrl: "db.sqlite",
DbIsPostgres: other.IntoPointer(false),
UseRedis: false,
RedisUrl: nil,
MaxInMemoryCacheSize: 1e6, // 1 Megabyte
},
}
func (gc *ConfigGeneral) GetFullDomain() string {
if gc.Subdomain != nil {
return *gc.Subdomain + gc.Domain
}
return gc.Domain
}
func (gc *ConfigGeneral) GetFullPublicUrl() string {
str := gc.Protocol + gc.GetFullDomain()
if gc.PublicPort != nil {
str += fmt.Sprint(*gc.PublicPort)
} else {
str += fmt.Sprint(gc.PrivatePort)
}
return str
}
func WriteDefaultConfig(toFile string) error {
log.Trace().Caller().Send()
log.Info().Str("config-file", toFile).Msg("Writing default config to file")
file, err := os.Create(toFile)
if err != nil {
log.Error().
Err(err).
Str("config-file", toFile).
Msg("Failed to create file for default config")
return err
}
defer file.Close()
data, err := toml.Marshal(&defaultConfig)
if err != nil {
log.Error().Err(err).Msg("Failed to marshal default config to toml")
return err
}
_, err = file.Write(data)
if err != nil {
log.Error().Err(err).Str("config-file", toFile).Msg("Failed to write default config")
return err
}
log.Info().Str("config-file", toFile).Msg("Wrote default config")
return nil
}
func ReadAndWriteToGlobal(fileName string) error {
log.Trace().Caller().Send()
log.Debug().Str("config-file", fileName).Msg("Attempting to read config file")
data, err := os.ReadFile(fileName)
if err != nil {
log.Warn().
Str("config-file", fileName).
Err(err).
Msg("Failed to read config file, attempting to write default config")
err = WriteDefaultConfig(fileName)
if err != nil {
log.Error().
Err(err).
Str("config-file", fileName).
Msg("Failed to create default config file")
return err
}
GlobalConfig = defaultConfig
return nil
}
config := Config{}
log.Debug().Str("config-file", fileName).Msg("Read config file, attempting to unmarshal")
err = toml.Unmarshal(data, &config)
if err != nil {
log.Error().Err(err).Bytes("config-data-raw", data).Msg("Failed to unmarshal config file")
return err
}
GlobalConfig = config
log.Info().Str("config-file", fileName).Msg("Read and applied config file")
return nil
}