package config import ( "fmt" "os" "gopkg.in/yaml.v3" ) // APIConfig holds configuration for the HTTP API server. type APIConfig struct { Bind string `yaml:"bind"` Secret string `yaml:"secret"` // SecureCookies sets the Secure flag on session cookies. // Enable when TLS is terminated at this server or at a trusted reverse proxy. SecureCookies bool `yaml:"secure_cookies"` // TrustedProxies is a list of IP addresses or CIDR ranges whose // X-Forwarded-For header is trusted. Empty = trust no proxy (use r.RemoteAddr). TrustedProxies []string `yaml:"trusted_proxies"` } // Config is the full application configuration loaded from YAML. type Config struct { Server ServerConfig `yaml:"server"` Storage StorageConfig `yaml:"storage"` Database DatabaseConfig `yaml:"database"` SMTP SMTPConfig `yaml:"smtp"` SMTPOut SMTPOutConfig `yaml:"smtp_out"` API APIConfig `yaml:"api"` Index IndexConfig `yaml:"index"` Audit AuditConfig `yaml:"audit"` Logging LoggingConfig `yaml:"logging"` IMAPServer IMAPServerConfig `yaml:"imap_server"` } // IMAPServerConfig holds settings for the embedded read-only IMAP archive server. type IMAPServerConfig struct { Enabled bool `yaml:"enabled"` Bind string `yaml:"bind"` // plain: ":1143", TLS: ":993" TLSCert string `yaml:"tls_cert"` // path to PEM certificate; if set, TLS is enabled TLSKey string `yaml:"tls_key"` // path to PEM private key FQDN string `yaml:"-"` // set at runtime from server.fqdn } // ServerConfig holds port settings for the main services. type ServerConfig struct { FQDN string `yaml:"fqdn"` // Fully Qualified Domain Name — used in SMTP EHLO, IMAP greeting, and generated links APIPort int `yaml:"api_port"` SMTPPort int `yaml:"smtp_port"` } // SMTPOutConfig holds settings for outgoing email (password reset, invitations). type SMTPOutConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` User string `yaml:"user"` Password string `yaml:"password"` TLS bool `yaml:"tls"` From string `yaml:"from"` // e.g. "archivmail " } // StorageConfig holds file system paths for email storage. type StorageConfig struct { StorePath string `yaml:"store_path"` AStorePath string `yaml:"astore_path"` Keyfile string `yaml:"keyfile"` RetentionDays int `yaml:"retention_days"` // 0 = kein Lock (GoBD-Compliance: z.B. 3650 für 10 Jahre) Compress bool `yaml:"compress"` // gzip-Kompression vor AES-256-GCM (spart ~40-60% Disk) } // DatabaseConfig holds PostgreSQL connection settings. type DatabaseConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Name string `yaml:"name"` User string `yaml:"user"` Password string `yaml:"password"` SSLMode string `yaml:"sslmode"` } // DSN builds a PostgreSQL connection string from the config fields. func (d DatabaseConfig) DSN() string { return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s", d.User, d.Password, d.Host, d.Port, d.Name, d.SSLMode) } // SMTPConfig holds settings for the embedded SMTP server. // SEC-26: AllowedIPs uses fail-closed logic. An empty list means NO IP is // allowed to connect. To accept from any IP, explicitly set: // allowed_ips: ["0.0.0.0/0", "::/0"] type SMTPConfig struct { Enabled bool `yaml:"enabled"` Bind string `yaml:"bind"` Domain string `yaml:"domain"` TLSCert string `yaml:"tls_cert"` TLSKey string `yaml:"tls_key"` MaxSizeMB int `yaml:"max_size_mb"` AllowedIPs []string `yaml:"allowed_ips"` TenantRouting string `yaml:"tenant_routing"` // "domain" or "default" DefaultTenantID int64 `yaml:"default_tenant_id"` // used when routing is "default" or domain lookup fails } // IndexConfig holds full-text index settings. type IndexConfig struct { Path string `yaml:"path"` Backend string `yaml:"backend"` BatchSize int `yaml:"batch_size"` AsyncQueueSize int `yaml:"async_queue_size"` ManticoreDSN string `yaml:"manticore_dsn"` // DSN for Manticore backend (default: "manticore@tcp(127.0.0.1:9306)/") } // AuditConfig holds audit log settings. type AuditConfig struct { LogPath string `yaml:"log_path"` RetentionDays int `yaml:"retention_days"` } // LoggingConfig holds application logging settings. type LoggingConfig struct { Path string `yaml:"path"` Level string `yaml:"level"` } // Load reads a YAML config file from path and returns a parsed Config. func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil }