linstrom/config/config.go

247 lines
7.8 KiB
Go
Raw Normal View History

2024-05-31 15:21:29 +00:00
package config
2024-08-28 15:20:38 +00:00
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
"github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/goutils/other"
)
2024-05-31 15:21:29 +00:00
type ConfigSSL struct {
2024-08-28 15:20:38 +00:00
HandleSSL bool `toml:"handle_ssl"` // Whether Linstrom should handle SSL encryption itself
2024-05-31 15:21:29 +00:00
// If Linstrom is to handle SSL, whether it should use LetsEncrypt for certificates
2024-08-28 15:20:38 +00:00
UseLetsEncrypt *bool `toml:"use_lets_encrypt"`
2024-05-31 15:21:29 +00:00
// Path to the certificate if Linstrom is to handle SSL while not using LetsEncrypt
2024-08-28 15:20:38 +00:00
CertificateFile *string `toml:"certificate_file"`
2024-05-31 15:21:29 +00:00
// Mail adress to use in case of using LetsEncrypt
2024-08-28 15:20:38 +00:00
AdminMail *string `toml:"admin_mail"`
2024-05-31 15:21:29 +00:00
}
type ConfigGeneral struct {
2024-08-28 15:20:38 +00:00
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"`
2024-09-12 06:56:57 +00:00
// File to write structured logs to (structured being formatted as json)
// If not set, Linstrom won't write structured logs
2024-09-17 08:13:57 +00:00
StructuredLogFile *string `toml:"structured_log_file"`
2024-08-28 15:20:38 +00:00
}
type ConfigWebAuthn struct {
2024-09-17 08:13:57 +00:00
DisplayName string `toml:"display_name"`
// HashingSecret string `toml:"hashing_secret"`
2024-05-31 15:21:29 +00:00
}
type ConfigAdmin struct {
2024-08-28 15:20:38 +00:00
// 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"`
2024-10-15 19:26:48 +00:00
// Password for protecting profiling data from unauthorised access
// An empty string equals to no password
// The password has to be supplied in the `password` GET form value for all requests
// to /profiling/*
ProfilingPassword string `toml:"profiling_password"`
2024-05-31 15:21:29 +00:00
}
type ConfigStorage struct {
2024-09-15 13:18:05 +00:00
Host string `toml:"host"`
Username string `toml:"username"`
Password string `toml:"password"`
DbName string `toml:"db_name"`
Port int `toml:"port"`
SslMode *string `toml:"ssl_mode"`
TimeZone *string `toml:"time_zone"`
2024-09-12 06:56:57 +00:00
// Url to redis server. If empty, no redis is used
RedisUrl *string `toml:"redis_url,omitempty"`
// The maximum size of the in-memory cache in bytes
MaxInMemoryCacheSize int64 `toml:"max_in_memory_cache_size"`
2024-09-15 13:18:05 +00:00
// The time to live for in app in memory cache items, in seconds
2024-09-17 08:13:57 +00:00
MaxInMemoryCacheTTL int `toml:"max_in_memory_cache_ttl"`
2024-09-15 13:18:05 +00:00
// The time to live for items in redis, in seconds
2024-09-17 08:13:57 +00:00
MaxRedisCacheTTL *int `toml:"max_redis_cache_ttl"`
}
2024-08-22 17:57:53 +00:00
type ConfigMail struct {
Host string `toml:"host"`
Port int `toml:"port"`
Username string `toml:"username"`
Password string `toml:"password"`
EncryptionOverwrite *string `toml:"encryption_overwrite,omitempty"`
KeepAliveOverwrite *bool `toml:"keep_alive_overwrite,omitempty"`
ConnectTimeoutSecondsOverwrite *int `toml:"connect_timeout_seconds_overwrite,omitempty"`
SendTimeoutSecondsOverwrite *int `toml:"send_timeout_seconds_overwrite,omitempty"`
TemplateOverwriteDirectory *string `toml:"template_overwrite_directory,omitempty"`
2024-05-31 15:21:29 +00:00
}
type ConfigSelf struct {
ServerActorDisplayName string `toml:"server_actor_display_name"`
ServerDisplayName string `toml:"server_display_name"`
}
2024-05-31 15:21:29 +00:00
type Config struct {
2024-08-28 15:20:38 +00:00
General ConfigGeneral `toml:"general"`
SSL ConfigSSL `toml:"ssl"`
Admin ConfigAdmin `toml:"admin"`
Webauthn ConfigWebAuthn `toml:"webauthn"`
Storage ConfigStorage `toml:"storage"`
2024-09-15 13:18:05 +00:00
Mail ConfigMail `toml:"mail"`
Self ConfigSelf `toml:"self"`
2024-08-28 15:20:38 +00:00
}
var GlobalConfig Config
var defaultConfig Config = Config{
General: ConfigGeneral{
2024-09-17 08:13:57 +00:00
Protocol: "http",
Subdomain: nil,
Domain: "localhost",
PrivatePort: 8080,
PublicPort: nil,
StructuredLogFile: nil,
2024-08-28 15:20:38 +00:00
},
SSL: ConfigSSL{
HandleSSL: false,
UseLetsEncrypt: nil,
CertificateFile: nil,
AdminMail: nil,
},
Admin: ConfigAdmin{
Username: "server-admin",
FirstTimeSetupOTP: "Example otp password",
2024-10-15 19:26:48 +00:00
ProfilingPassword: "Example profiling password",
2024-08-28 15:20:38 +00:00
},
Webauthn: ConfigWebAuthn{
2024-09-17 08:13:57 +00:00
DisplayName: "Linstrom",
// HashingSecret: "some super secure secret that should never be changed or else password storage breaks",
2024-08-28 15:20:38 +00:00
},
Storage: ConfigStorage{
2024-09-15 13:18:05 +00:00
Host: "localhost",
Username: "linstrom",
Password: "linstrom",
DbName: "linstrom",
Port: 5432,
SslMode: other.IntoPointer("disable"),
TimeZone: other.IntoPointer("Europe/Berlin"),
RedisUrl: nil,
MaxInMemoryCacheSize: 1e6, // 1 Megabyte
2024-09-15 13:18:05 +00:00
MaxInMemoryCacheTTL: 5,
MaxRedisCacheTTL: nil,
},
2024-09-17 08:13:57 +00:00
Mail: ConfigMail{
Host: "localhost",
Port: 587,
Username: "linstrom",
Password: "linstrom",
KeepAliveOverwrite: nil,
EncryptionOverwrite: nil,
ConnectTimeoutSecondsOverwrite: nil,
SendTimeoutSecondsOverwrite: nil,
TemplateOverwriteDirectory: nil,
},
2024-08-28 15:20:38 +00:00
}
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
}
2024-09-15 13:18:05 +00:00
func (sc *ConfigStorage) BuildPostgresDSN() string {
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%d",
sc.Host,
sc.Username,
sc.Password,
sc.DbName,
sc.Port,
)
if sc.SslMode != nil {
dsn += fmt.Sprintf(" sslmode=%s", *sc.SslMode)
}
if sc.TimeZone != nil {
dsn += fmt.Sprintf(" TimeZone=%s", *sc.TimeZone)
}
return dsn
}
2024-08-28 15:20:38 +00:00
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
2024-05-31 15:21:29 +00:00
}