package db import ( "context" "encoding/json" "fmt" ) // GetInboundChecks returns the singleton inbound-checks settings row. // BootstrapFromConfig guarantees it exists before any other code path can // observe it, so sql.ErrNoRows here would indicate a bootstrap bug, not a // normal condition. func (d *DB) GetInboundChecks(ctx context.Context) (InboundChecksSettings, error) { var s InboundChecksSettings var portsJSON, createdAt, updatedAt string err := d.QueryRowContext(ctx, ` SELECT ports, icmp, created_at, updated_at FROM inbound_checks_settings WHERE id=1 `).Scan(&portsJSON, &s.ICMP, &createdAt, &updatedAt) if err != nil { return InboundChecksSettings{}, err } if err := json.Unmarshal([]byte(portsJSON), &s.Ports); err != nil { return InboundChecksSettings{}, fmt.Errorf("decode inbound check ports: %w", err) } if s.Ports == nil { s.Ports = []int{} } if s.CreatedAt, err = dbToTime(createdAt); err != nil { return InboundChecksSettings{}, err } if s.UpdatedAt, err = dbToTime(updatedAt); err != nil { return InboundChecksSettings{}, err } return s, nil } // SetInboundChecks persists a new prober check configuration. Validation is // purely local (port range, no duplicates) — unlike fip_settle_seconds, // there's no cross-field dependency on other orchestrator config, so this // is called directly by the HTTP handler without going through // orchestrator.Orchestrator. func (d *DB) SetInboundChecks(ctx context.Context, ports []int, icmp bool) error { seen := make(map[int]bool, len(ports)) for _, p := range ports { if p < 1 || p > 65535 { return fmt.Errorf("port %d out of range 1..65535: %w", p, ErrValidation) } if seen[p] { return fmt.Errorf("duplicate port %d: %w", p, ErrValidation) } seen[p] = true } if ports == nil { ports = []int{} } payload, err := json.Marshal(ports) if err != nil { return err } now := timeToDB(Now()) _, err = d.ExecContext(ctx, ` UPDATE inbound_checks_settings SET ports=?, icmp=?, updated_at=? WHERE id=1 `, string(payload), icmp, now) return err }