package main import ( "os" "path/filepath" "strconv" ) // Config aggregates every runtime knob the sidecar reads from the // environment. Held in one struct so the rest of the package can take a // pointer instead of poking os.Getenv at use-sites. type Config struct { OriginalsRoot string // absolute path to PhotoPrism's originals dir PhotoprismBaseURL string // e.g. http://localhost:2342 ListenAddr string // bind interface — 127.0.0.1 for host mode, 0.0.0.0 in containers Port int // HTTP listen port DSN string // GORM/MySQL connection string for mule_sidecar // PpDSN is a second DB connection string pointed at PhotoPrism's own // schema (`photoprism.*`). Sidecar code that needs to mutate // PhotoPrism-managed rows (e.g. auth_users.base_path) opens its own // connection with these creds rather than asking for grants on the // mule_sidecar user. Empty if PP_DB_PASSWORD isn't provided, in // which case PP-touching features (user-basepath reconciler) stay // dormant. PpDSN string // UserBasepaths is the parsed `USER_BASEPATHS` env. Maps PhotoPrism // usernames to originals-relative base paths so OIDC-provisioned // users land with the right library scope without any admin // touching `photoprism users mod`. UserBasepaths map[string]string } func loadConfig() (*Config, error) { root := envOr("ORIGINALS_ROOT", "/photoprism/originals") abs, err := filepath.Abs(root) if err != nil { return nil, err } portStr := envOr("SIDECAR_PORT", "8000") port, err := strconv.Atoi(portStr) if err != nil { return nil, err } dsn := os.Getenv("SIDECAR_DSN") if dsn == "" { // Default matches the user that mariadb/init/01-sidecar.sql provisions // on first boot. The literal placeholder password is intentional: the // SQL ships with it and an env-templating step is left for whoever // runs this in a non-local context. user := envOr("SIDECAR_DB_USER", "sidecar") pass := envOr("SIDECAR_DB_PASSWORD", "replace-at-m4-bringup") host := envOr("SIDECAR_DB_HOST", "127.0.0.1") dbPort := envOr("SIDECAR_DB_PORT", "3306") name := envOr("SIDECAR_DB_NAME", "mule_sidecar") dsn = user + ":" + pass + "@tcp(" + host + ":" + dbPort + ")/" + name + "?charset=utf8mb4&parseTime=true&loc=Local" } // PhotoPrism schema connection — only used by the user-basepath // reconciler. Stays empty if PP_DB_PASSWORD isn't set, and callers // gate behaviour on that. We use PhotoPrism's own DB user rather // than the sidecar's because `mule_sidecar` has no grants on // `photoprism.*` (see mariadb/init/01-sidecar.sql). ppDSN := "" if ppPass := os.Getenv("PP_DB_PASSWORD"); ppPass != "" { ppUser := envOr("PP_DB_USER", "photoprism") ppHost := envOr("PP_DB_HOST", envOr("SIDECAR_DB_HOST", "mariadb")) ppPort := envOr("PP_DB_PORT", envOr("SIDECAR_DB_PORT", "3306")) ppName := envOr("PP_DB_NAME", "photoprism") ppDSN = ppUser + ":" + ppPass + "@tcp(" + ppHost + ":" + ppPort + ")/" + ppName + "?charset=utf8mb4&parseTime=true&loc=Local" } return &Config{ OriginalsRoot: abs, PhotoprismBaseURL: envOr("PHOTOPRISM_BASE_URL", "http://localhost:2342"), ListenAddr: envOr("SIDECAR_LISTEN_ADDR", "127.0.0.1"), Port: port, DSN: dsn, PpDSN: ppDSN, UserBasepaths: parseUserBasepaths(os.Getenv("USER_BASEPATHS")), }, nil } func envOr(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback }