package admin import ( "os" "strings" "github.com/ultisuite/ulti-backend/internal/config" ) type envVarSpec struct { Name string Group string Secret bool } var orgEnvVarSpecs = []envVarSpec{ // Authentik / OIDC {Name: "ULTID_OIDC_ISSUER", Group: "authentik", Secret: false}, {Name: "ULTID_OIDC_CLIENT_ID", Group: "authentik", Secret: false}, {Name: "ULTID_OIDC_CLIENT_SECRET", Group: "authentik", Secret: true}, {Name: "AUTHENTIK_API_URL", Group: "authentik", Secret: false}, {Name: "AUTHENTIK_API_TOKEN", Group: "authentik", Secret: true}, // Nextcloud {Name: "NEXTCLOUD_ENABLED", Group: "nextcloud", Secret: false}, {Name: "NEXTCLOUD_URL", Group: "nextcloud", Secret: false}, {Name: "NC_PUBLIC_URL", Group: "nextcloud", Secret: false}, {Name: "NC_ADMIN_USER", Group: "nextcloud", Secret: false}, {Name: "NC_ADMIN_PASSWORD", Group: "nextcloud", Secret: true}, // OnlyOffice {Name: "ONLYOFFICE_ENABLED", Group: "onlyoffice", Secret: false}, {Name: "ONLYOFFICE_URL", Group: "onlyoffice", Secret: false}, {Name: "ONLYOFFICE_PUBLIC_URL", Group: "onlyoffice", Secret: false}, {Name: "ONLYOFFICE_JWT_SECRET", Group: "onlyoffice", Secret: true}, // AI assistant {Name: "AI_ASSISTANT_ENABLED", Group: "ai_assistant", Secret: false}, {Name: "OPENWEBUI_URL", Group: "ai_assistant", Secret: false}, {Name: "AI_ASSISTANT_PUBLIC_PATH", Group: "ai_assistant", Secret: false}, {Name: "ULTIMAIL_MCP_URL", Group: "ai_assistant", Secret: false}, // Search {Name: "SEARCH_ENGINE", Group: "search", Secret: false}, {Name: "MEILISEARCH_URL", Group: "search", Secret: false}, {Name: "MEILISEARCH_API_KEY", Group: "search", Secret: true}, {Name: "TYPESENSE_URL", Group: "search", Secret: false}, {Name: "TYPESENSE_API_KEY", Group: "search", Secret: true}, // Immich / Jitsi (docker compose modules) {Name: "IMMICH_ENABLED", Group: "immich", Secret: false}, {Name: "IMMICH_API_URL", Group: "immich", Secret: false}, {Name: "JITSI_ENABLED", Group: "jitsi", Secret: false}, {Name: "JITSI_PUBLIC_URL", Group: "jitsi", Secret: false}, {Name: "JITSI_APP_SECRET", Group: "jitsi", Secret: true}, // Object storage (mail attachments) {Name: "ULTID_RUSTFS_ENDPOINT", Group: "storage", Secret: false}, {Name: "ULTID_RUSTFS_ACCESS_KEY", Group: "storage", Secret: true}, {Name: "ULTID_RUSTFS_SECRET_KEY", Group: "storage", Secret: true}, {Name: "MAIL_ATTACHMENTS_BUCKET", Group: "storage", Secret: false}, } func buildOrgEnvVars() []map[string]any { out := make([]map[string]any, 0, len(orgEnvVarSpecs)) for _, spec := range orgEnvVarSpecs { raw, set := os.LookupEnv(spec.Name) entry := map[string]any{ "name": spec.Name, "group": spec.Group, "set": set && strings.TrimSpace(raw) != "", "secret": spec.Secret, } if set && !spec.Secret && strings.TrimSpace(raw) != "" { entry["value"] = raw } out = append(out, entry) } return out } func buildOrgDeployLocked(cfg *config.Config) map[string]any { // Services deployed via Docker Compose: runtime flags come from env, not admin policy toggles. return map[string]any{ "nextcloud": map[string]any{ "locked": true, "reason": "docker_compose", "fields": []string{"enabled", "base_url", "admin_user", "admin_password"}, }, "onlyoffice": map[string]any{ "locked": true, "reason": "docker_compose", "fields": []string{"enabled", "document_server_url", "jwt_secret", "jwt_header"}, }, "ai_assistant": map[string]any{ "locked": true, "reason": "docker_compose", "fields": []string{"enabled", "openwebui_internal_url", "public_path"}, }, "search": map[string]any{ "locked": true, "reason": "docker_compose", "fields": []string{"suite_engine", "meilisearch_url", "meilisearch_api_key", "typesense_url", "typesense_api_key"}, }, "authentik": map[string]any{ "locked": envAuthentikLocked(cfg), "reason": "docker_compose", "fields": []string{"enabled", "api_url", "client_id"}, }, "plugins": map[string]any{ "locked": true, "reason": "docker_compose", "fields": []string{"office-editor"}, }, } } func envAuthentikLocked(cfg *config.Config) bool { if cfg == nil { return false } return strings.TrimSpace(cfg.OIDCIssuer) != "" || strings.TrimSpace(cfg.OIDCClientID) != "" || strings.TrimSpace(cfg.AuthentikAPIToken) != "" }