167 lines
4.3 KiB
Go
167 lines
4.3 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
EnvListenAddr = "SUB2API_CRM_LISTEN_ADDR"
|
|
EnvSQLiteDSN = "SUB2API_CRM_SQLITE_DSN"
|
|
EnvAdminToken = "SUB2API_CRM_ADMIN_TOKEN"
|
|
EnvAdminUsername = "SUB2API_CRM_ADMIN_USERNAME"
|
|
EnvAdminPassword = "SUB2API_CRM_ADMIN_PASSWORD"
|
|
EnvAdminSessionTTL = "SUB2API_CRM_ADMIN_SESSION_TTL"
|
|
EnvRepoRoot = "SUB2API_CRM_REPO_ROOT"
|
|
EnvReconcileWorkerEnabled = "SUB2API_CRM_RECONCILE_WORKER_ENABLED"
|
|
EnvReconcilePollInterval = "SUB2API_CRM_RECONCILE_POLL_INTERVAL"
|
|
|
|
DefaultListenAddr = ":8080"
|
|
DefaultSQLiteDSN = "file:sub2api-cn-relay-manager.db?_foreign_keys=on&_busy_timeout=5000"
|
|
DefaultAdminUsername = "admin"
|
|
DefaultAdminSessionTTL = 12 * time.Hour
|
|
DefaultReconcilePollInterval = 10 * time.Minute
|
|
)
|
|
|
|
type ServerConfig struct {
|
|
ListenAddr string
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
SQLiteDSN string
|
|
}
|
|
|
|
type ReconcileConfig struct {
|
|
WorkerEnabled bool
|
|
PollInterval time.Duration
|
|
}
|
|
|
|
type RepositoryConfig struct {
|
|
RepoRoot string
|
|
}
|
|
|
|
type StartupConfig struct {
|
|
Server ServerConfig
|
|
Database DatabaseConfig
|
|
Repository RepositoryConfig
|
|
Reconcile ReconcileConfig
|
|
}
|
|
|
|
type AdminSessionConfig struct {
|
|
Username string
|
|
Password string
|
|
SessionTTL time.Duration
|
|
}
|
|
|
|
func LoadStartupFromEnv() (StartupConfig, error) {
|
|
return loadStartupFromLookupEnv(os.LookupEnv)
|
|
}
|
|
|
|
func loadStartupFromLookupEnv(lookup func(string) (string, bool)) (StartupConfig, error) {
|
|
reconcilePollInterval, err := readOptionalDurationEnv(lookup, EnvReconcilePollInterval, DefaultReconcilePollInterval)
|
|
if err != nil {
|
|
return StartupConfig{}, err
|
|
}
|
|
cfg := StartupConfig{
|
|
Server: ServerConfig{
|
|
ListenAddr: readOptionalEnv(lookup, EnvListenAddr, DefaultListenAddr),
|
|
},
|
|
Database: DatabaseConfig{
|
|
SQLiteDSN: readOptionalEnv(lookup, EnvSQLiteDSN, DefaultSQLiteDSN),
|
|
},
|
|
Repository: RepositoryConfig{
|
|
RepoRoot: readOptionalEnv(lookup, EnvRepoRoot, ""),
|
|
},
|
|
Reconcile: ReconcileConfig{
|
|
WorkerEnabled: readOptionalBoolEnv(lookup, EnvReconcileWorkerEnabled, false),
|
|
PollInterval: reconcilePollInterval,
|
|
},
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func LoadAdminTokenFromEnv() (string, error) {
|
|
return loadAdminTokenFromLookupEnv(os.LookupEnv)
|
|
}
|
|
|
|
func LoadAdminSessionFromEnv() (AdminSessionConfig, error) {
|
|
return loadAdminSessionFromLookupEnv(os.LookupEnv)
|
|
}
|
|
|
|
func loadAdminTokenFromLookupEnv(lookup func(string) (string, bool)) (string, error) {
|
|
token := strings.TrimSpace(readRequiredEnv(lookup, EnvAdminToken))
|
|
if token == "" {
|
|
return "", fmt.Errorf("%s is required", EnvAdminToken)
|
|
}
|
|
|
|
return token, nil
|
|
}
|
|
|
|
func loadAdminSessionFromLookupEnv(lookup func(string) (string, bool)) (AdminSessionConfig, error) {
|
|
ttl, err := readOptionalDurationEnv(lookup, EnvAdminSessionTTL, DefaultAdminSessionTTL)
|
|
if err != nil {
|
|
return AdminSessionConfig{}, err
|
|
}
|
|
return AdminSessionConfig{
|
|
Username: readOptionalEnv(lookup, EnvAdminUsername, DefaultAdminUsername),
|
|
Password: strings.TrimSpace(readOptionalEnv(lookup, EnvAdminPassword, "")),
|
|
SessionTTL: ttl,
|
|
}, nil
|
|
}
|
|
|
|
func readOptionalEnv(lookup func(string) (string, bool), key string, defaultValue string) string {
|
|
value, ok := lookup(key)
|
|
if !ok {
|
|
return defaultValue
|
|
}
|
|
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return defaultValue
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
func readRequiredEnv(lookup func(string) (string, bool), key string) string {
|
|
value, ok := lookup(key)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
func readOptionalBoolEnv(lookup func(string) (string, bool), key string, defaultValue bool) bool {
|
|
value, ok := lookup(key)
|
|
if !ok {
|
|
return defaultValue
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
case "1", "true", "yes", "on":
|
|
return true
|
|
case "0", "false", "no", "off", "":
|
|
return false
|
|
default:
|
|
return defaultValue
|
|
}
|
|
}
|
|
|
|
func readOptionalDurationEnv(lookup func(string) (string, bool), key string, defaultValue time.Duration) (time.Duration, error) {
|
|
value, ok := lookup(key)
|
|
if !ok || strings.TrimSpace(value) == "" {
|
|
return defaultValue, nil
|
|
}
|
|
duration, err := time.ParseDuration(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: parse duration: %w", key, err)
|
|
}
|
|
if duration <= 0 {
|
|
return 0, fmt.Errorf("%s: duration must be positive", key)
|
|
}
|
|
return duration, nil
|
|
}
|