package db import ( "context" "encoding/json" "fmt" ) // SettingsRepository provides access to module_state and settings_overrides tables. type SettingsRepository struct { db *DB } // NewSettingsRepository creates a new SettingsRepository. func NewSettingsRepository(db *DB) *SettingsRepository { return &SettingsRepository{db: db} } // GetModuleStates returns all module enabled/disabled states from the database. func (r *SettingsRepository) GetModuleStates(ctx context.Context) (map[string]bool, error) { rows, err := r.db.pool.Query(ctx, `SELECT module_id, enabled FROM module_state`) if err != nil { return nil, fmt.Errorf("querying module states: %w", err) } defer rows.Close() states := make(map[string]bool) for rows.Next() { var id string var enabled bool if err := rows.Scan(&id, &enabled); err != nil { return nil, fmt.Errorf("scanning module state: %w", err) } states[id] = enabled } return states, rows.Err() } // SetModuleState persists a module's enabled state. Uses upsert semantics. func (r *SettingsRepository) SetModuleState(ctx context.Context, moduleID string, enabled bool, updatedBy string) error { _, err := r.db.pool.Exec(ctx, `INSERT INTO module_state (module_id, enabled, updated_by, updated_at) VALUES ($1, $2, $3, now()) ON CONFLICT (module_id) DO UPDATE SET enabled = EXCLUDED.enabled, updated_by = EXCLUDED.updated_by, updated_at = now()`, moduleID, enabled, updatedBy) if err != nil { return fmt.Errorf("setting module state: %w", err) } return nil } // GetOverrides returns all settings overrides from the database. func (r *SettingsRepository) GetOverrides(ctx context.Context) (map[string]json.RawMessage, error) { rows, err := r.db.pool.Query(ctx, `SELECT key, value FROM settings_overrides`) if err != nil { return nil, fmt.Errorf("querying settings overrides: %w", err) } defer rows.Close() overrides := make(map[string]json.RawMessage) for rows.Next() { var key string var value json.RawMessage if err := rows.Scan(&key, &value); err != nil { return nil, fmt.Errorf("scanning settings override: %w", err) } overrides[key] = value } return overrides, rows.Err() } // SetOverride persists a settings override. Uses upsert semantics. func (r *SettingsRepository) SetOverride(ctx context.Context, key string, value any, updatedBy string) error { jsonVal, err := json.Marshal(value) if err != nil { return fmt.Errorf("marshaling override value: %w", err) } _, err = r.db.pool.Exec(ctx, `INSERT INTO settings_overrides (key, value, updated_by, updated_at) VALUES ($1, $2, $3, now()) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_by = EXCLUDED.updated_by, updated_at = now()`, key, jsonVal, updatedBy) if err != nil { return fmt.Errorf("setting override: %w", err) } return nil } // DeleteOverride removes a settings override. func (r *SettingsRepository) DeleteOverride(ctx context.Context, key string) error { _, err := r.db.pool.Exec(ctx, `DELETE FROM settings_overrides WHERE key = $1`, key) if err != nil { return fmt.Errorf("deleting override: %w", err) } return nil }