package config import ( "encoding/json" "fmt" "github.com/alexei/tinyforge/internal/store" "gopkg.in/yaml.v3" ) // ExportConfig reads the current database state and produces a SeedConfig YAML // representation. Credential fields (tokens, passwords) are exported as placeholder // strings since they are encrypted in the database. func ExportConfig(db *store.Store) ([]byte, error) { cfg, err := buildSeedConfig(db) if err != nil { return nil, fmt.Errorf("build seed config: %w", err) } data, err := yaml.Marshal(cfg) if err != nil { return nil, fmt.Errorf("marshal yaml: %w", err) } return data, nil } // buildSeedConfig constructs a SeedConfig from the current database state. func buildSeedConfig(db *store.Store) (SeedConfig, error) { settings, err := db.GetSettings() if err != nil { return SeedConfig{}, fmt.Errorf("get settings: %w", err) } registries, err := db.GetAllRegistries() if err != nil { return SeedConfig{}, fmt.Errorf("get registries: %w", err) } projects, err := db.GetAllProjects() if err != nil { return SeedConfig{}, fmt.Errorf("get projects: %w", err) } cfg := SeedConfig{ Global: GlobalConfig{ Domain: settings.Domain, ServerIP: settings.ServerIP, Network: settings.Network, SubdomainPattern: settings.SubdomainPattern, NotificationURL: settings.NotificationURL, Npm: NpmConfig{ URL: settings.NpmURL, Email: settings.NpmEmail, Password: "CHANGE_ME", // Encrypted value, export placeholder. }, }, Registries: make(map[string]RegistryDef), Projects: make(map[string]ProjectDef), } for _, reg := range registries { cfg.Registries[reg.Name] = RegistryDef{ URL: reg.URL, Type: reg.Type, Token: "CHANGE_ME", // Encrypted value, export placeholder. } } for _, proj := range projects { stages, err := db.GetStagesByProjectID(proj.ID) if err != nil { return SeedConfig{}, fmt.Errorf("get stages for project %s: %w", proj.Name, err) } stageDefs := make(map[string]StageDef) for _, st := range stages { stageDefs[st.Name] = StageDef{ TagPattern: st.TagPattern, AutoDeploy: st.AutoDeploy, MaxInstances: st.MaxInstances, Confirm: st.Confirm, PromoteFrom: st.PromoteFrom, Subdomain: st.Subdomain, } } envMap := parseJSONMap(proj.Env) volMap := parseJSONMap(proj.Volumes) cfg.Projects[proj.Name] = ProjectDef{ Registry: proj.Registry, Image: proj.Image, Port: proj.Port, Healthcheck: proj.Healthcheck, Env: envMap, Volumes: volMap, Stages: stageDefs, } } return cfg, nil } // parseJSONMap safely parses a JSON-encoded map string. Returns nil on failure. func parseJSONMap(jsonStr string) map[string]string { if jsonStr == "" || jsonStr == "{}" { return nil } var m map[string]string if err := json.Unmarshal([]byte(jsonStr), &m); err != nil { return nil } if len(m) == 0 { return nil } return m }