//nolint:forbidigo // Allow printing for prettyprint package config import ( "fmt" "net" "net/url" "time" errs "git.maronato.dev/maronato/goshort/internal/errs" ) const ( DBTypeMemory = "memory" DBTypeSQLite = "sqlite" ) func GetDBTypeList() []string { return []string{ DBTypeMemory, DBTypeSQLite, } } type VerboseLevel int const ( VerboseLevelQuiet VerboseLevel = iota - 1 VerboseLevelInfo VerboseLevelAccessLogs VerboseLevelDebug ) const ( // DefaultProd is the default mode. DefaultProd = false // DefaultDBType is the default type of database to use. DefaultDBType = DBTypeSQLite // DefaultDBURL is the default connection string for the database. DefaultDBURL = "goshort.db" // DefaultPort is the default port to listen on. DefaultPort = "8080" // DefaultHost is the default host to listen on. This is set to 0.0.0.0 when running in Docker. DefaultHost = "localhost" // DefaultUIPort is the default port to listen on for the UI. DefaultUIPort = "3000" // DefaultDebug is the default debug mode. DefaultDebug = false // DefaultSessionDuration is the default session duration. DefaultSessionDuration = 7 * 24 * time.Hour // DefaultDisableRegistration is the default value for diable registration. DefaultDisableRegistration = false // DefaultVerbose is the default verbosity level. DefaultVerbose = VerboseLevelInfo // DefaultQuiet is the default quiet mode. DefaultQuiet = false // DefaultDisableCredentialsLogin is the default value for diable credential login. DefaultDisableCredentialsLogin = false // DefaultOIDCIssuerName is the default name of the OIDC issuer. DefaultOIDCIssuerName = "OpenID" ) const ( // ReadTimeout is the maximum duration for reading the entire // request, including the body. ReadTimeout = 5 * time.Second // WriteTimeout is the maximum duration before timing out // writes of the response. WriteTimeout = 10 * time.Second // IdleTimeout is the maximum amount of time to wait for the // next request when keep-alives are enabled. IdleTimeout = 30 * time.Second // ReadHeaderTimeout is the amount of time allowed to read // request headers. ReadHeaderTimeout = 2 * time.Second // RequestTimeout is the maximum duration for the entire // request. RequestTimeout = 7 * 24 * time.Hour ) // Config defines the default configuration for the backend. type Config struct { // Prod is a flag that indicates if the server is running in production mode. Prod bool `json:"prod"` // Debug is a flag that indicates if the server is running in debug mode. Debug bool // Host is the host to listen on. Host string // Port is the port to listen on. Port string // UiPort is the port to listen on for the UI. UIPort string // DBType is the type of database to use. DBType string // DBURL is the connection string for the database. DBURL string // SessionDuration is the duration of the session. SessionDuration time.Duration // DisableRegistration defines whether or not registration are disabled. DisableRegistration bool `json:"disableRegistration"` // Verbose defines the verbosity level. Verbose VerboseLevel `json:"verbose"` // Quiet defines whether or not the server should be quiet. Quiet bool // DisableCredentialsLogin defines whether or not the server should disable credential login. DisableCredentialsLogin bool `json:"disableCredentialLogin"` // OIDCIssuerURL is the URL of the OIDC issuer. OIDCIssuerURL string `json:"oidcIssuerUrl"` // OIDCIssueName is the name of the OIDC issuer. OIDCIssuerName string // OIDCClientID is the client ID for OIDC. OIDCClientID string // OIDCClientSecret is the client secret for OIDC. OIDCClientSecret string // OIDCRedirectURL is the redirect URL for OIDC. OIDCRedirectURL string } func NewConfig() *Config { return &Config{ Prod: DefaultProd, Debug: DefaultDebug, Host: DefaultHost, Port: DefaultPort, UIPort: DefaultUIPort, DBType: DefaultDBType, DBURL: DefaultDBURL, SessionDuration: DefaultSessionDuration, DisableRegistration: DefaultDisableRegistration, Verbose: DefaultVerbose, Quiet: DefaultQuiet, DisableCredentialsLogin: DefaultDisableCredentialsLogin, OIDCIssuerName: DefaultOIDCIssuerName, } } // Validate validates the configuration modifies user-inaccessible fields. func Validate(cfg *Config) error { // Host and port have to be valid. if _, err := url.ParseRequestURI("http://" + net.JoinHostPort(cfg.Host, cfg.Port)); err != nil { return errs.Errorf(fmt.Sprintf("invalid host and/or port: %s", err), errs.ErrInvalidConfig) } // UI port has to be valid. if cfg.UIPort != "" { if _, err := url.ParseRequestURI("http://" + net.JoinHostPort(cfg.Host, cfg.UIPort)); err != nil { return errs.Errorf(fmt.Sprintf("invalid UI port: %s", err), errs.ErrInvalidConfig) } } // Verbose has to be valid. if cfg.Verbose < VerboseLevelInfo || cfg.Verbose > VerboseLevelDebug { return errs.Errorf(fmt.Sprintf("invalid verbosity level: %d", cfg.Verbose), errs.ErrInvalidConfig) } if cfg.Verbose != VerboseLevelInfo && cfg.Quiet { return errs.Errorf("cannot set both verbose and quiet", errs.ErrInvalidConfig) } if cfg.DBType != "" { // DB type has to be valid. valid := false for _, dbType := range GetDBTypeList() { if cfg.DBType == dbType { valid = true break } } if !valid { return errs.Errorf(fmt.Sprintf("invalid database type: %s", cfg.DBType), errs.ErrInvalidConfig) } } // If verbose == 2, set debug to true for backwards compatibility. if cfg.Verbose >= VerboseLevelDebug { cfg.Debug = true } // If quiet is true, set verbose to -1. if cfg.Quiet { cfg.Verbose = VerboseLevelQuiet } // Either OIDC or credential login has to be enabled. if cfg.DisableCredentialsLogin && cfg.OIDCIssuerURL == "" { return errs.Errorf("either OIDC or credential login has to be enabled", errs.ErrInvalidConfig) } if cfg.OIDCIssuerURL != "" { // If OIDC is enabled, all OIDC fields have to be set. if cfg.OIDCClientID == "" || cfg.OIDCClientSecret == "" || cfg.OIDCRedirectURL == "" { return errs.Errorf("all OIDC fields have to be set", errs.ErrInvalidConfig) } // OIDC fields must be valid if _, err := url.ParseRequestURI(cfg.OIDCIssuerURL); err != nil { return errs.Errorf(fmt.Sprintf("invalid OIDC issuer URL: %s", err), errs.ErrInvalidConfig) } if _, err := url.ParseRequestURI(cfg.OIDCRedirectURL); err != nil { return errs.Errorf(fmt.Sprintf("invalid OIDC redirect URL: %s", err), errs.ErrInvalidConfig) } } return nil } func (c *Config) PrettyPrint() { fmt.Println("Configuration:") fmt.Println(" - Production mode:", c.Prod) fmt.Println(" - Debug mode:", c.Debug) fmt.Println(" - Host:", c.Host) fmt.Println(" - Port:", c.Port) fmt.Println(" - UI Port:", c.UIPort) fmt.Println(" - Database type:", c.DBType) fmt.Println(" - Database URL:", c.DBURL) fmt.Println(" - Session duration:", c.SessionDuration) fmt.Println(" - Disable registration:", c.DisableRegistration) fmt.Println(" - Verbose level:", c.Verbose) fmt.Println(" - Quiet mode:", c.Quiet) }