package postgres import ( "context" "database/sql" "encoding/json" "switchboard-core/models" ) type GlobalConfigStore struct{} func NewGlobalConfigStore() *GlobalConfigStore { return &GlobalConfigStore{} } func (s *GlobalConfigStore) Get(ctx context.Context, key string) (models.JSONMap, error) { var valueJSON []byte err := DB.QueryRowContext(ctx, "SELECT value FROM global_settings WHERE key = $1", key).Scan(&valueJSON) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } var result models.JSONMap json.Unmarshal(valueJSON, &result) return result, nil } func (s *GlobalConfigStore) Set(ctx context.Context, key string, value models.JSONMap, updatedBy string) error { valueJSON := ToJSON(value) _, err := DB.ExecContext(ctx, ` INSERT INTO global_settings (key, value, updated_by, updated_at) VALUES ($1, $2, $3, NOW()) ON CONFLICT (key) DO UPDATE SET value = $2, updated_by = $3, updated_at = NOW()`, key, valueJSON, updatedBy) return err } func (s *GlobalConfigStore) GetAll(ctx context.Context) (map[string]models.JSONMap, error) { rows, err := DB.QueryContext(ctx, "SELECT key, value FROM global_settings") if err != nil { return nil, err } defer rows.Close() result := make(map[string]models.JSONMap) for rows.Next() { var key string var valueJSON []byte if err := rows.Scan(&key, &valueJSON); err != nil { continue } var m models.JSONMap json.Unmarshal(valueJSON, &m) result[key] = m } return result, rows.Err() } // ── OIDC state ──────────────────────────────────────────── func (s *GlobalConfigStore) SaveOIDCState(ctx context.Context, state, nonce, redirectTo string) error { _, err := DB.ExecContext(ctx, ` INSERT INTO oidc_auth_state (state, nonce, redirect_to) VALUES ($1, $2, $3) `, state, nonce, redirectTo) return err } func (s *GlobalConfigStore) ConsumeOIDCState(ctx context.Context, state string) (string, string, error) { var nonce, redirectTo string err := DB.QueryRowContext(ctx, ` SELECT nonce, COALESCE(redirect_to, '') FROM oidc_auth_state WHERE state = $1 `, state).Scan(&nonce, &redirectTo) if err != nil { return "", "", err } // Delete (one-time use) _, _ = DB.ExecContext(ctx, `DELETE FROM oidc_auth_state WHERE state = $1`, state) return nonce, redirectTo, nil } func (s *GlobalConfigStore) CleanupOIDCState(ctx context.Context) error { _, err := DB.ExecContext(ctx, `DELETE FROM oidc_auth_state WHERE created_at < NOW() - INTERVAL '10 minutes'`) return err } // ── CS6 additions ───────────────────────────────────────────── func (s *GlobalConfigStore) GetString(ctx context.Context, key string) (string, error) { var val string err := DB.QueryRowContext(ctx, "SELECT value FROM global_settings WHERE key = $1", key).Scan(&val) return val, err }