Files
canon/internal/config/config.go
T
cdnutterandClaude Opus 5.5 f3185ba7bd Add scheduled and manual backups
Backups are .tar.gz archives of a consistent database snapshot (VACUUM
INTO) plus the images folder. They run on a cron schedule, daily at 3:00
by default, and the newest N scheduled ones are kept (7 by default).
Manual backups are never pruned.

Settings → Backups offers schedule presets or a custom cron expression,
retention, "Back up now", and download/delete for each backup. A small
built-in parser handles standard 5-field cron syntax with names, ranges,
steps and @daily-style shorthands.

Backups go to CANON_BACKUP_DIR (default data/backups). Compose mounts a
separate canon-backups volume at /backups, overridable with
CANON_BACKUP_PATH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:30:40 -07:00

77 lines
2.1 KiB
Go

// Package config loads runtime configuration from environment variables.
package config
import (
"net"
"os"
"path/filepath"
"strconv"
"strings"
)
// Config holds process-level settings. Anything the owner can change from the
// web UI lives in the database instead (see store.Settings); the API-key
// fields here only act as overrides when set.
type Config struct {
ListenAddr string
Host string
Port string
DataDir string
BackupDir string
AdminUsername string
AdminPassword string
SecureCookies bool
TrustProxy bool
// Optional overrides for provider credentials. When set, the matching
// setting is locked in the web UI.
TMDBKey string
TVDBKey string
TVDBPin string
IGDBClientID string
IGDBClientSecret string
}
func Load() Config {
// 0.0.0.0 accepts connections from other devices on the network, not
// just this machine. Use 127.0.0.1 to keep it local.
host := env("CANON_HOST", "0.0.0.0")
port := env("CANON_PORT", "4242")
dataDir := env("CANON_DATA_DIR", "./data")
return Config{
ListenAddr: env("CANON_LISTEN", net.JoinHostPort(host, port)),
Host: host,
Port: port,
DataDir: dataDir,
BackupDir: env("CANON_BACKUP_DIR", filepath.Join(dataDir, "backups")),
AdminUsername: env("CANON_ADMIN_USERNAME", ""),
AdminPassword: env("CANON_ADMIN_PASSWORD", ""),
SecureCookies: envBool("CANON_SECURE_COOKIES", false),
TrustProxy: envBool("CANON_TRUST_PROXY", false),
TMDBKey: env("CANON_TMDB_API_KEY", ""),
TVDBKey: env("CANON_TVDB_API_KEY", ""),
TVDBPin: env("CANON_TVDB_PIN", ""),
IGDBClientID: env("CANON_IGDB_CLIENT_ID", ""),
IGDBClientSecret: env("CANON_IGDB_CLIENT_SECRET", ""),
}
}
func env(key, def string) string {
if v, ok := os.LookupEnv(key); ok && strings.TrimSpace(v) != "" {
return strings.TrimSpace(v)
}
return def
}
func envBool(key string, def bool) bool {
v, ok := os.LookupEnv(key)
if !ok {
return def
}
b, err := strconv.ParseBool(strings.TrimSpace(v))
if err != nil {
return def
}
return b
}