Replace the Node prototype (server.mjs) with the stack the merge plan calls for: Go 1.25, Gin for routing, GORM + MariaDB for persistence. Same wire contract on /api/sidecar/* so the SvelteKit client doesn't change. - Marks move from a JSON file on disk to mule_sidecar.marks (auto- migrated by GORM on first boot). The Node prototype's marks.json was dev-only; not migrated. - Folder/rename/heap-convert/duplicates handlers reproduce the prototype's behaviour, including the path-traversal defence (resolveUnderRoot + EvalSymlinks), the size-bucket prefilter for the duplicate hasher, and the background reindex fire-and-forget pattern. - Auth model unchanged: requireSession middleware proxies the caller's X-Auth-Token to PhotoPrism's /api/v1/photos?count=1 before any destructive op. - Expose pp-mariadb on 127.0.0.1:3306 in docker-compose so the host Go process can reach mule_sidecar.* without joining the container network. - Archive the Node prototype under sidecar/legacy/server.mjs for one cycle as reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
1.8 KiB
Go
61 lines
1.8 KiB
Go
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
|
|
Port int // HTTP listen port (loopback only)
|
|
DSN string // GORM/MySQL connection string for mule_sidecar
|
|
}
|
|
|
|
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"
|
|
}
|
|
|
|
return &Config{
|
|
OriginalsRoot: abs,
|
|
PhotoprismBaseURL: envOr("PHOTOPRISM_BASE_URL", "http://localhost:2342"),
|
|
Port: port,
|
|
DSN: dsn,
|
|
}, nil
|
|
}
|
|
|
|
func envOr(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|