Merge branch 'main' of gitlab.com:mstarongitlab/linstrom
also delete server-old with all its problems
This commit is contained in:
commit
d99efca667
47 changed files with 2200 additions and 694 deletions
196
config/config.go
196
config/config.go
|
@ -1,11 +1,12 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gitlab.com/mstarongitlab/goutils/other"
|
||||
)
|
||||
|
||||
type ConfigSSL struct {
|
||||
|
@ -19,25 +20,44 @@ type ConfigSSL struct {
|
|||
}
|
||||
|
||||
type ConfigGeneral struct {
|
||||
Domain string `toml:"domain"` // The domain this server operates under
|
||||
FullDomain string `toml:"full_domain"` // The full url the server operates under (without port), eg "http://localhost", the public port will be appended as needed
|
||||
PublicPort int `toml:"public_port"` // The public facing port, usually either 80 or 443
|
||||
PrivatePort int `toml:"private_port"` // The port the server should launch at
|
||||
// Explanation:
|
||||
// The public port is the port to connect to from outside the server to access it.
|
||||
// The private port is where the server will open for itself on launch
|
||||
// Important for reverse proxies like nginx or traeffik
|
||||
// Where you the public port would be either 80 (http) or 443 (https), but the private one could be 3000 for example
|
||||
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"`
|
||||
// File to write structured logs to (structured being formatted as json)
|
||||
// If not set, Linstrom won't write structured logs
|
||||
StructuredLogFile *string
|
||||
}
|
||||
|
||||
type ConfigWebAuthn struct {
|
||||
DisplayName string `toml:"display_name"`
|
||||
HashingSecret string `toml:"hashing_secret"`
|
||||
}
|
||||
|
||||
type ConfigAdmin struct {
|
||||
Username string `toml:"username"`
|
||||
PasswordHash string `toml:"password_hash"`
|
||||
// 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 {
|
||||
IsPostgres bool `toml:"is_postgres"`
|
||||
Uri string `toml:"uri"`
|
||||
// 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"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type ConfigMail struct {
|
||||
|
@ -53,27 +73,23 @@ type ConfigMail struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
General ConfigGeneral `toml:"general"`
|
||||
SSL ConfigSSL `toml:"ssl"`
|
||||
Admin ConfigAdmin `toml:"admin"`
|
||||
Storage ConfigStorage `toml:"storage"`
|
||||
Mail ConfigMail `toml:"mail"`
|
||||
General ConfigGeneral `toml:"general"`
|
||||
SSL ConfigSSL `toml:"ssl"`
|
||||
Admin ConfigAdmin `toml:"admin"`
|
||||
Webauthn ConfigWebAuthn `toml:"webauthn"`
|
||||
Storage ConfigStorage `toml:"storage"`
|
||||
Mail ConfigMail `toml:"mail"`
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_FILE_PATH = "config.toml"
|
||||
var GlobalConfig Config
|
||||
|
||||
// "Global" variable for accessing the config
|
||||
// If nil, no config has been loaded yet
|
||||
var Global *Config
|
||||
|
||||
var ErrGlobalConfigNotSet = errors.New("global config not set")
|
||||
|
||||
// The default config is for a local debug environment
|
||||
var defaultConfig = Config{
|
||||
var defaultConfig Config = Config{
|
||||
General: ConfigGeneral{
|
||||
Protocol: "http",
|
||||
Subdomain: nil,
|
||||
Domain: "localhost",
|
||||
PublicPort: 8080,
|
||||
PrivatePort: 8080,
|
||||
PublicPort: nil,
|
||||
},
|
||||
SSL: ConfigSSL{
|
||||
HandleSSL: false,
|
||||
|
@ -82,54 +98,94 @@ var defaultConfig = Config{
|
|||
AdminMail: nil,
|
||||
},
|
||||
Admin: ConfigAdmin{
|
||||
Username: "admin",
|
||||
PasswordHash: "", // No password
|
||||
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{
|
||||
IsPostgres: false,
|
||||
Uri: "db.sqlite",
|
||||
DatabaseUrl: "db.sqlite",
|
||||
DbIsPostgres: other.IntoPointer(false),
|
||||
RedisUrl: nil,
|
||||
MaxInMemoryCacheSize: 1e6, // 1 Megabyte
|
||||
},
|
||||
}
|
||||
|
||||
func LoadConfigFromFile(file string, tryDefaultPath, saveIntoGlobal bool) (*Config, error) {
|
||||
conf := &Config{}
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil && !tryDefaultPath {
|
||||
return nil, fmt.Errorf("failed to read config %s: %w", file, err)
|
||||
func (gc *ConfigGeneral) GetFullDomain() string {
|
||||
if gc.Subdomain != nil {
|
||||
return *gc.Subdomain + gc.Domain
|
||||
}
|
||||
if err != nil && tryDefaultPath {
|
||||
conf, err = loadFromDefaultPath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read custom and default config: %w", err)
|
||||
}
|
||||
}
|
||||
err = toml.Unmarshal(data, &conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert from toml: %w", err)
|
||||
}
|
||||
if saveIntoGlobal {
|
||||
Global = conf
|
||||
}
|
||||
return conf, nil
|
||||
return gc.Domain
|
||||
}
|
||||
|
||||
func loadFromDefaultPath() (*Config, error) {
|
||||
data, err := os.ReadFile(DEFAULT_CONFIG_FILE_PATH)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to load default config path %s: %w",
|
||||
DEFAULT_CONFIG_FILE_PATH,
|
||||
err,
|
||||
)
|
||||
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)
|
||||
}
|
||||
conf := Config{}
|
||||
err = toml.Unmarshal(data, &conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to parse file content of %s as toml: %w",
|
||||
DEFAULT_CONFIG_FILE_PATH,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return &conf, nil
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue