config

package
v0.15.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package config provides configuration loading, validation, persistence, and application for the server.

Index

Constants

View Source
const DefaultDQueMaxDiskBytes int64 = 50 << 30

DefaultDQueMaxDiskBytes is the default maximum disk usage in bytes for the write batcher's dque overflow queue: 50 GiB.

Variables

View Source
var ErrNilConfig = errors.New("config cannot be nil")

ErrNilConfig is returned when a nil config is passed to Save or Validate.

Functions

func ApplyImageDirectory

func ApplyImageDirectory(imageDirectory string) (string, string, error)

ApplyImageDirectory applies image directory configuration and returns the resolved directory values along with any validation error encountered.

func EnsureBootstrapDefaults added in v0.8.0

func EnsureBootstrapDefaults(ctx context.Context, rootDir string, queries gallerydb.Querier) error

EnsureBootstrapDefaults ensures the database contains the minimum default configuration: admin credentials, logging settings, and any missing default keys. It operates purely against the sqlc-generated Querier interface so it can be tested without a full ConfigService.

func EnsureDefaults

func EnsureDefaults(ctx context.Context, rootDir string, admin ConfigAdmin, pool any)

EnsureDefaults makes sure the database has at least the default config and admin user.

func FileExists

func FileExists(path string) bool

FileExists checks if a path exists and is a readable file.

func IncrementETagVersion

func IncrementETagVersion(current string) string

IncrementETagVersion increments an ETag version string following these rules:

  • Format: [prefix]YYYYMMDD-NN (e.g., "20260129-01" or "v20260129-01")
  • If date < today: reset to today-01
  • If date == today: increment NN
  • If invalid format: default to today-01

func ValidateImageDirectory

func ValidateImageDirectory(path string) error

ValidateImageDirectory checks if the path is valid for use.

Types

type ApplyPersistenceError added in v0.3.0

type ApplyPersistenceError struct {
	// contains filtered or unexported fields
}

ApplyPersistenceError indicates the candidate config could not be persisted.

func (*ApplyPersistenceError) Error added in v0.3.0

func (e *ApplyPersistenceError) Error() string

Error returns the persistence failure message.

func (*ApplyPersistenceError) Unwrap added in v0.3.0

func (e *ApplyPersistenceError) Unwrap() error

Unwrap returns the underlying persistence error.

type ApplyResult added in v0.3.0

type ApplyResult struct {
	Config              *Config
	RestartRequired     bool
	RestartRequiredKeys []string
}

ApplyResult captures the outcome of applying a config update.

func ApplyConfig added in v0.3.0

func ApplyConfig(ctx context.Context, svc ConfigStore, current, candidate *Config) (*ApplyResult, error)

ApplyConfig validates, persists, and computes restart requirements for a new configuration.

type ApplyValidationError added in v0.3.0

type ApplyValidationError struct {
	// contains filtered or unexported fields
}

ApplyValidationError indicates the candidate config failed validation.

func (*ApplyValidationError) Error added in v0.3.0

func (e *ApplyValidationError) Error() string

Error returns the validation failure message.

func (*ApplyValidationError) Unwrap added in v0.3.0

func (e *ApplyValidationError) Unwrap() error

Unwrap returns the underlying validation error.

type Config

type Config struct {
	// Server settings (restart required)
	ListenerAddress string
	ListenerPort    int

	// Logging settings (restart required)
	LogDirectory      string
	LogLevel          string // "debug", "info", "warn", "error"
	LogRollover       string // "daily", "weekly", "monthly"
	LogRetentionCount int

	// Application settings (runtime)
	SiteName       string
	Themes         []string
	CurrentTheme   string
	ImageDirectory string

	// ETag version for cache busting (runtime changeable)
	ETagVersion string

	// SessionMaxAge is the session lifetime in seconds.
	// Defines how long a session remains valid before requiring re-authentication.
	// Security implications: Shorter sessions (e.g., 1-7 days) reduce the window for
	// session hijacking attacks. Defaults to 7 days (604800 seconds).
	// Recommended: 1-7 days depending on use case.
	SessionMaxAge int

	// SessionHttpOnly prevents JavaScript access to session cookies.
	// When true, the HttpOnly flag is set on the session cookie, protecting against
	// Cross-Site Scripting (XSS) attacks by preventing malicious scripts from reading
	// or stealing the session token.
	// Defaults to true (recommended for production).
	// Security note: Always keep true unless specifically required for XSS-vulnerable
	// legacy applications.
	SessionHttpOnly bool

	// SessionSecure restricts session cookies to HTTPS connections only.
	// When true, the Secure flag is set on the session cookie, ensuring that
	// the cookie is only transmitted over encrypted HTTPS connections.
	// Prevents session hijacking via man-in-the-middle (MITM) attacks on unencrypted HTTP.
	// Defaults to true (required for production).
	// Security note: Must be true in production with HTTPS. Can be false only for
	// development over HTTP, but is dangerous in production.
	SessionSecure bool

	// SessionSameSite controls the SameSite cookie attribute for CSRF protection.
	// Valid values: "Lax", "Strict", or "None".
	//
	// - "Lax" (default, recommended): Cookies are sent with same-site requests and
	//   top-level navigations (safe for most applications). Provides strong CSRF
	//   protection while maintaining reasonable user experience.
	//
	// - "Strict": Cookies are sent only with same-site requests. Most restrictive,
	//   blocking cookies even on top-level navigations from external sites.
	//   Best for highly sensitive applications (e.g., banking), but may inconvenience
	//   users following external links to your site.
	//
	// - "None": Cookies are sent with all requests (cross-site included).
	//   Only use with Secure=true if cross-site requests require authentication.
	//   Essentially disables SameSite CSRF protection; use only with careful consideration.
	//
	// Security implications: SameSite is a modern defense against CSRF attacks.
	// Lax provides excellent protection without sacrificing usability. Combined with
	// http.CrossOriginProtection on unsafe methods, provides defense-in-depth.
	SessionSameSite string

	// Performance settings (restart required)
	EnableHTTPCache       bool
	CacheMaxSize          int64         // in bytes
	CacheMaxTime          time.Duration // TTL
	CacheMaxEntrySize     int64         // in bytes
	CacheCleanupInterval  time.Duration
	DBMaxPoolSize         int
	DBMinIdleConnections  int
	DBOptimizeInterval    time.Duration
	WorkerPoolMax         int // 0 means auto-calculate
	WorkerPoolMinIdle     int // 0 means no idle workers
	WorkerPoolMaxIdleTime time.Duration
	DBPoolMonitorInterval time.Duration
	QueueSize             int
	EnableCachePreload    bool // Pre-load HTTP cache when folders are opened (runtime, no restart)
	// MaxHTTPCacheEntryInsertPerTransaction is the max number of cache entries to insert in one transaction (batch size). Default: 10.
	MaxHTTPCacheEntryInsertPerTransaction int

	// HTTPCacheBodyCodec selects the write codec for new http_cache rows.
	// Valid: "zstd-1", "gzip-6", "identity". Hot-reloadable.
	HTTPCacheBodyCodec string

	// DQueMaxDiskBytes is the maximum disk usage in bytes for the write
	// batcher's dque overflow queue. When the dque directory reaches this
	// threshold, Submit returns ErrQuotaExceeded instead of overflowing to
	// disk. 0 means unlimited at runtime. The default is non-zero
	// (DefaultDQueMaxDiskBytes, 50 GiB). Hot-reloadable without restart.
	DQueMaxDiskBytes int64

	// Discovery settings (runtime)
	RunFileDiscovery bool

	// RestartAfterDiscovery, when true, requests a process restart after the
	// startup TriggerDiscovery returns (walk, drain, and file_folder_index
	// rebuild). Default false. Hot-reloadable; read after that return.
	// Does not apply to POST /server/discovery.
	RestartAfterDiscovery bool

	// DiscoveryQueueMax is currently a no-op: the discovery work queue is a
	// dedicated disk-backed dque (wipe-on-start), so SubsystemManager.Start
	// ignores this field. It is retained for later removal and no longer bounds
	// the production discovery queue.
	DiscoveryQueueMax int

	// Security settings
	// LockoutDuration is the account lockout period in seconds after
	// LockoutThreshold failed login attempts.
	LockoutDuration int

	// LockoutThreshold is the number of failed login attempts before
	// account lockout is triggered. Default: 3.
	LockoutThreshold int

	// LoginRateLimitPerIP is the maximum number of login POST requests
	// allowed per IP address per 60-second window. 0 disables IP rate limiting.
	LoginRateLimitPerIP int

	// HelpText and ExampleValues are populated by LoadFromDatabase from the
	// metadata columns stored alongside each config row. Nil when loaded
	// through other paths (DefaultConfig, YAML, CLI). Used by the config UI
	// to render inline tooltips; not serialised into exports or saved to DB.
	HelpText      map[string]string
	ExampleValues map[string]string
}

Config represents the complete application configuration. All settings are stored as strings in the database and converted as needed.

func BuildImportedConfig added in v0.3.0

func BuildImportedConfig(base *Config, yamlContent string) (*Config, error)

BuildImportedConfig parses and applies YAML content on top of the current base config. Fields absent from the imported YAML retain their current (live) value. It rejects session-secret because it is memory-only.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a Config with all default values.

func FromMap

func FromMap(m map[string]string) (*Config, error)

FromMap creates a Config from a map of string values. This is used for loading from database or other key-value sources.

func Load

func Load(ctx context.Context, rootDir string, store ConfigStore, opt getopt.Opt) (*Config, error)

Load handles the full configuration loading precedence: Defaults -> Database -> YAML -> CLI/Env

func (*Config) ExportToYAML

func (c *Config) ExportToYAML() (string, error)

ExportToYAML exports the current configuration to YAML format. Excludes sensitive values like session secret.

func (*Config) GetLastKnownGoodDiff

func (c *Config) GetLastKnownGoodDiff(ctx context.Context, q ConfigQueries) (*ConfigDiff, error)

GetLastKnownGoodDiff returns a diff showing current config vs last known good config. Returns an error if last known good config is not found or cannot be loaded.

func (*Config) IdentifyChanges

func (c *Config) IdentifyChanges(other *Config) []string

IdentifyChanges compares two configs and returns a list of changed keys.

func (*Config) ImportFromYAML

func (c *Config) ImportFromYAML(yamlContent string, ctx context.Context, q ConfigSaver) error

ImportFromYAML imports configuration from YAML content and saves to database. Validates the YAML and rejects session-secret.

func (*Config) LoadFromDatabase

func (c *Config) LoadFromDatabase(ctx context.Context, q ConfigQueries) error

LoadFromDatabase loads configuration values from the database. Only loads values that exist in the database; missing keys keep their current values. Also populates HelpText and ExampleValues maps from each row's metadata columns.

func (*Config) LoadFromOpt

func (c *Config) LoadFromOpt(opt getopt.Opt)

LoadFromOpt loads configuration values from getopt.Opt (CLI arguments and environment variables). This applies the highest precedence configuration source, overriding database and YAML values. Only values that were explicitly set (IsSet=true) override the current config. This ensures that default/zero values from getopt.Opt do not override database values.

func (*Config) LoadFromOptExcluding

func (c *Config) LoadFromOptExcluding(opt getopt.Opt, exclude []string)

LoadFromOptExcluding applies CLI/env values except for fields in the exclude list. The exclude list contains config field names that should NOT be overridden (e.g., user-changed fields). This supports the use case where CLI values should override unchanged fields, but user changes persist.

func (*Config) LoadFromYAML

func (c *Config) LoadFromYAML() error

LoadFromYAML loads configuration values from YAML files. It loads from platform config dir first (lower precedence), then exe dir (higher precedence). Only values present in YAML files are applied; missing keys keep their current values.

func (*Config) MergeDefaults

func (c *Config) MergeDefaults(defaults *Config)

MergeDefaults applies default values to any zero-value non-boolean fields in the config. Boolean fields are intentionally excluded because Go's zero value for bool (false) is indistinguishable from an explicit false setting; their defaults are applied during YAML import where presence can be detected.

func (*Config) PreviewImport

func (c *Config) PreviewImport(yamlContent string) (*ConfigDiff, error)

PreviewImport parses YAML content and returns a diff showing what would change if the import were applied. Does not modify the current configuration.

func (*Config) RecoverFromCorruption

func (c *Config) RecoverFromCorruption(defaults *Config)

RecoverFromCorruption restores a corrupted configuration by copying values from defaults. This is used when configuration is corrupted and needs to be restored to safe defaults.

func (*Config) RestoreLastKnownGood

func (c *Config) RestoreLastKnownGood(ctx context.Context, q ConfigQueries) (*Config, error)

RestoreLastKnownGood loads and returns the last known good configuration from the database. Returns an error if last known good config is not found or invalid. Note: This method only loads and parses the config - it does NOT save it back to the database.

func (*Config) SaveToDatabase

func (c *Config) SaveToDatabase(ctx context.Context, q ConfigSaver) error

SaveToDatabase saves all configuration values to the database. It converts the Config struct to a map and saves each key-value pair. Also saves a copy as "LastKnownGoodConfig" for recovery purposes. Note: This method calls ExportToYAML which will be moved to config/exporter.go in Task 6.7.

func (*Config) SetValueFromString

func (c *Config) SetValueFromString(key, value string) error

SetValueFromString sets a config field value from a string representation. This is used when loading from database or parsing from YAML.

func (*Config) ToMap

func (c *Config) ToMap() map[string]string

ToMap converts the Config to a map of key-value pairs for database storage.

func (*Config) Validate

func (c *Config) Validate() error

Validate validates all configuration values and returns an error if any are invalid.

func (*Config) ValidateGuardrails added in v0.3.0

func (c *Config) ValidateGuardrails() []GuardrailWarning

ValidateGuardrails returns non-fatal warnings for dangerous or contradictory configuration combinations. These warnings are intended for startup diagnostics and operator visibility.

func (*Config) ValidateSetting

func (c *Config) ValidateSetting(key, value string) error

ValidateSetting validates a single configuration setting by key and value. Returns an error if the value is invalid for that setting.

type ConfigAdmin added in v0.5.0

type ConfigAdmin interface {
	// Export exports the configuration as a YAML string.
	Export(ctx context.Context) (string, error)

	// Import imports configuration from a YAML string and saves it to the database.
	Import(yamlContent string, ctx context.Context) error

	// RestoreLastKnownGood restores the last known good configuration from the database.
	RestoreLastKnownGood(ctx context.Context) (*Config, error)

	// EnsureDefaults ensures default config (admin creds, default keys) exists in the database.
	// rootDir is used for default paths (e.g. log_directory).
	EnsureDefaults(ctx context.Context, rootDir string) error

	// GetConfigValue returns the value for key from the config table, or error if not found.
	GetConfigValue(ctx context.Context, key string) (string, error)

	// IncrementETag increments the ETag version in the database and returns the new value.
	IncrementETag(ctx context.Context) (string, error)
}

ConfigAdmin provides admin-level configuration operations.

type ConfigDiff

type ConfigDiff struct {
	CurrentYAML string
	NewYAML     string
	Changes     []string // List of changed keys
}

ConfigDiff represents the differences between two configurations. It contains the current and new YAML representations and a list of changed keys.

type ConfigManager added in v0.9.0

type ConfigManager struct {
	ConfigMu      sync.RWMutex
	Config        *Config
	ConfigService ConfigService
}

ConfigManager owns application configuration state and ConfigService.

func NewConfigManager added in v0.9.0

func NewConfigManager() *ConfigManager

NewConfigManager constructs an empty configuration manager.

func (*ConfigManager) GetConfig added in v0.9.0

func (m *ConfigManager) GetConfig() *Config

GetConfig returns the current in-memory configuration snapshot.

func (*ConfigManager) GetETagVersion added in v0.9.0

func (m *ConfigManager) GetETagVersion() string

GetETagVersion returns the current ETag cache-busting version.

func (*ConfigManager) LogLoadedConfigDiagnostics added in v0.9.0

func (m *ConfigManager) LogLoadedConfigDiagnostics(cfg *Config)

LogLoadedConfigDiagnostics emits startup diagnostics for loaded configuration.

func (*ConfigManager) SetConfig added in v0.9.0

func (m *ConfigManager) SetConfig(cfg *Config)

SetConfig replaces the in-memory configuration snapshot.

func (*ConfigManager) SetConfigService added in v0.9.0

func (m *ConfigManager) SetConfigService(svc ConfigService)

SetConfigService wires the configuration service implementation.

func (*ConfigManager) UpdateConfigWithPrecedence added in v0.9.0

func (m *ConfigManager) UpdateConfigWithPrecedence(cfg *Config, changedFields []string, opt getopt.Opt)

UpdateConfigWithPrecedence stores cfg and reapplies CLI/env overrides except on changedFields.

type ConfigQueries

type ConfigQueries interface {
	GetConfigs(ctx context.Context) ([]gallerydb.Config, error)
}

ConfigQueries is an interface for loading config from database. This allows us to work with both Queries and CustomQueries types.

type ConfigSaver

type ConfigSaver interface {
	UpsertConfigValueOnly(ctx context.Context, arg gallerydb.UpsertConfigValueOnlyParams) error
}

ConfigSaver is an interface for saving configuration values to the database. It provides a minimal interface for config persistence operations.

type ConfigService

type ConfigService interface {
	ConfigStore
	ConfigAdmin
}

ConfigService is the union of ConfigStore and ConfigAdmin for backward compatibility.

func NewService

func NewService(dbRwPool, dbRoPool *dbconnpool.DbSQLConnPool) ConfigService

NewService creates a new ConfigService instance. It accepts database connection pools for read-write and read-only operations.

type ConfigStore added in v0.5.0

type ConfigStore interface {
	// Load loads the current configuration from the database.
	Load(ctx context.Context) (*Config, error)

	// Save saves the configuration to the database.
	Save(ctx context.Context, cfg *Config) error

	// Validate validates the configuration and returns an error if invalid.
	Validate(cfg *Config) error
}

ConfigStore provides loading, saving, and validating configuration data.

type FieldInfo added in v0.5.0

type FieldInfo struct {
	DBKey      string
	Set        func(c *Config, v string) error
	IsCheckbox bool // render as HTML checkbox
}

FieldInfo exposes a config field's key, setter, and form presentation metadata to external packages (primarily the handlers package).

func Fields added in v0.5.0

func Fields() []FieldInfo

Fields returns metadata for all config fields, for use by handler packages.

type GuardrailWarning added in v0.3.0

type GuardrailWarning struct {
	Check      string
	Configured string
	Effective  string
	Hint       string
}

GuardrailWarning represents a non-fatal configuration anomaly that should be visible at startup because effective runtime behavior may differ from the configured value(s).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL