package store import ( "context" "database/sql" "errors" "fmt" "gitea.stevedudenhoeffer.com/steve/pansy/internal/domain" ) // The instance_settings row is seeded by migration 0010 and there is exactly one // (CHECK id = 1), so reads never branch on existence and writes never insert. const instanceSettingsColumns = `agent_model, agent_enabled, vision_model, version, updated_at` // scanInstanceSettings reads the single settings row. agent_enabled is a nullable // INTEGER (NULL = inherit env), so it is scanned through sql.NullInt64. func scanInstanceSettings(s scanner) (*domain.InstanceSettings, error) { var ( out domain.InstanceSettings enabled sql.NullInt64 ) if err := s.Scan(&out.AgentModel, &enabled, &out.VisionModel, &out.Version, &out.UpdatedAt); err != nil { return nil, err } if enabled.Valid { b := enabled.Int64 != 0 out.AgentEnabled = &b } return &out, nil } // GetInstanceSettings returns the single settings row. func (d *DB) GetInstanceSettings(ctx context.Context) (*domain.InstanceSettings, error) { s, err := scanInstanceSettings(d.sql.QueryRowContext(ctx, `SELECT `+instanceSettingsColumns+` FROM instance_settings WHERE id = 1`)) if errors.Is(err, sql.ErrNoRows) { // The migration seeds this row, so its absence is a broken database, not a // normal "not found" the caller should paper over. return nil, fmt.Errorf("store: instance_settings row missing (migration 0010 not applied?)") } if err != nil { return nil, fmt.Errorf("store: get instance settings: %w", err) } return s, nil } // UpdateInstanceSettings applies a version-guarded update to the single row, // following the same optimistic-concurrency contract as every mutable resource: // the updated row on success, (current row, ErrVersionConflict) on a version // mismatch. There is no ErrNotFound path — the row always exists. // // agentEnabled is nil to store SQL NULL (inherit env), or a pointer to store an // explicit 0/1. func (d *DB) UpdateInstanceSettings(ctx context.Context, s *domain.InstanceSettings) (*domain.InstanceSettings, error) { var enabled any if s.AgentEnabled != nil { enabled = boolToInt(*s.AgentEnabled) } updated, err := scanInstanceSettings(d.sql.QueryRowContext(ctx, `UPDATE instance_settings SET agent_model = ?, agent_enabled = ?, vision_model = ?, version = version + 1, updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = 1 AND version = ? RETURNING `+instanceSettingsColumns, s.AgentModel, enabled, s.VisionModel, s.Version, )) if errors.Is(err, sql.ErrNoRows) { current, gerr := d.GetInstanceSettings(ctx) if gerr != nil { return nil, gerr } return current, domain.ErrVersionConflict } if err != nil { return nil, fmt.Errorf("store: update instance settings: %w", err) } return updated, nil }