package bootstrap import ( "context" "errors" "fmt" "path/filepath" "strings" "github.com/jackc/pgx/v5" ) const defaultThemePresetID = "moku-midnight" func personalSettingsPathForDisplayName(displayName string) string { personalName := strings.TrimSpace(displayName) if personalName == "" { personalName = defaultPersonalDisplayName } personalSlug := normalizePOSIXSlug(personalName) return filepath.ToSlash(filepath.Join("users", "personals", slugDir("personal", personalSlug), posixSettingsFileName)) } func extractThemePresetID(settings map[string]any) string { theme, ok := settings["theme"].(map[string]any) if !ok { return "" } presetID, _ := theme["presetId"].(string) return strings.TrimSpace(presetID) } func applyThemePresetToSettings(settings map[string]any, presetID string) map[string]any { normalized := settings if normalized == nil { normalized = map[string]any{} } theme, ok := normalized["theme"].(map[string]any) if !ok || theme == nil { theme = map[string]any{} } theme["presetId"] = presetID normalized["theme"] = theme return normalized } func (service *Service) SaveThemePreset(ctx context.Context, input SaveThemePresetInput) (*AdminRecord, error) { admin, err := service.GetAdmin(ctx) if err != nil { return nil, err } if admin == nil { return nil, ErrAdminNotConfigured } presetID := strings.TrimSpace(input.PresetID) if presetID == "" { return nil, fmt.Errorf("theme preset id is required") } rootPath := strings.TrimSpace(service.posixRoot) if rootPath == "" { return nil, fmt.Errorf("posix root is not configured") } relativeSettingsPath := personalSettingsPathForDisplayName(admin.DisplayName) absoluteSettingsPath := filepath.Join(rootPath, filepath.FromSlash(relativeSettingsPath)) settings := readStructuredFileMap(absoluteSettingsPath) if len(settings) == 0 { return nil, fmt.Errorf("personal settings file is missing") } updatedSettings := applyThemePresetToSettings(settings, presetID) if err := writeCBORFile(absoluteSettingsPath, updatedSettings); err != nil { return nil, fmt.Errorf("write personal %s: %w", posixSettingsFileName, err) } if err := service.rebuildProjection(ctx); err != nil { return nil, err } return service.GetAdmin(ctx) } func (service *Service) loadAdminThemePresetID(ctx context.Context, displayName string) (string, error) { path := personalSettingsPathForDisplayName(displayName) var presetID *string err := service.db.Pool.QueryRow(ctx, ` SELECT content_json->'theme'->>'presetId' FROM posix_nodes WHERE path = $1 LIMIT 1; `, path).Scan(&presetID) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return "", nil } return "", err } if presetID == nil { return "", nil } return strings.TrimSpace(*presetID), nil }