// Package ntp provides CRUD against ntp_settings (single-row) and // ntp_pools. Renderer in internal/chrony consumes the same data. package ntp import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "git.netcell-it.de/projekte/edgeguard-native/internal/models" ) var ErrPoolNotFound = errors.New("ntp pool not found") type Repo struct { Pool *pgxpool.Pool } func New(pool *pgxpool.Pool) *Repo { return &Repo{Pool: pool} } // ── Settings ─────────────────────────────────────────────────── func (r *Repo) GetSettings(ctx context.Context) (*models.NTPSettings, error) { row := r.Pool.QueryRow(ctx, ` SELECT id, listen_addresses, allow_acl, serve_clients, makestep_secs, makestep_limit, rtcsync, leapsectz, updated_at FROM ntp_settings WHERE id=1`) var s models.NTPSettings if err := row.Scan(&s.ID, &s.ListenAddresses, &s.AllowACL, &s.ServeClients, &s.MakestepSecs, &s.MakestepLimit, &s.RTCSync, &s.LeapsecTZ, &s.UpdatedAt); err != nil { return nil, err } return &s, nil } func (r *Repo) UpdateSettings(ctx context.Context, s models.NTPSettings) (*models.NTPSettings, error) { row := r.Pool.QueryRow(ctx, ` UPDATE ntp_settings SET listen_addresses=$1, allow_acl=$2, serve_clients=$3, makestep_secs=$4, makestep_limit=$5, rtcsync=$6, leapsectz=$7, updated_at=NOW() WHERE id=1 RETURNING id, listen_addresses, allow_acl, serve_clients, makestep_secs, makestep_limit, rtcsync, leapsectz, updated_at`, s.ListenAddresses, s.AllowACL, s.ServeClients, s.MakestepSecs, s.MakestepLimit, s.RTCSync, s.LeapsecTZ) var out models.NTPSettings if err := row.Scan(&out.ID, &out.ListenAddresses, &out.AllowACL, &out.ServeClients, &out.MakestepSecs, &out.MakestepLimit, &out.RTCSync, &out.LeapsecTZ, &out.UpdatedAt); err != nil { return nil, err } return &out, nil } // ── Pools ────────────────────────────────────────────────────── const poolSelect = ` SELECT id, kind, address, iburst, prefer, minpoll, maxpoll, active, description, created_at, updated_at FROM ntp_pools ` func (r *Repo) ListPools(ctx context.Context) ([]models.NTPPool, error) { rows, err := r.Pool.Query(ctx, poolSelect+" ORDER BY id ASC") if err != nil { return nil, err } defer rows.Close() out := make([]models.NTPPool, 0, 8) for rows.Next() { p, err := scanPool(rows) if err != nil { return nil, err } out = append(out, *p) } return out, rows.Err() } func (r *Repo) GetPool(ctx context.Context, id int64) (*models.NTPPool, error) { row := r.Pool.QueryRow(ctx, poolSelect+" WHERE id=$1", id) p, err := scanPool(row) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrPoolNotFound } return nil, err } return p, nil } func (r *Repo) CreatePool(ctx context.Context, p models.NTPPool) (*models.NTPPool, error) { row := r.Pool.QueryRow(ctx, ` INSERT INTO ntp_pools (kind, address, iburst, prefer, minpoll, maxpoll, active, description) VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id, kind, address, iburst, prefer, minpoll, maxpoll, active, description, created_at, updated_at`, p.Kind, p.Address, p.Iburst, p.Prefer, p.MinPoll, p.MaxPoll, p.Active, p.Description) return scanPool(row) } func (r *Repo) UpdatePool(ctx context.Context, id int64, p models.NTPPool) (*models.NTPPool, error) { row := r.Pool.QueryRow(ctx, ` UPDATE ntp_pools SET kind=$1, address=$2, iburst=$3, prefer=$4, minpoll=$5, maxpoll=$6, active=$7, description=$8, updated_at=NOW() WHERE id=$9 RETURNING id, kind, address, iburst, prefer, minpoll, maxpoll, active, description, created_at, updated_at`, p.Kind, p.Address, p.Iburst, p.Prefer, p.MinPoll, p.MaxPoll, p.Active, p.Description, id) out, err := scanPool(row) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrPoolNotFound } return nil, err } return out, nil } func (r *Repo) DeletePool(ctx context.Context, id int64) error { tag, err := r.Pool.Exec(ctx, `DELETE FROM ntp_pools WHERE id=$1`, id) if err != nil { return err } if tag.RowsAffected() == 0 { return ErrPoolNotFound } return nil } func scanPool(row interface{ Scan(...any) error }) (*models.NTPPool, error) { var p models.NTPPool if err := row.Scan( &p.ID, &p.Kind, &p.Address, &p.Iburst, &p.Prefer, &p.MinPoll, &p.MaxPoll, &p.Active, &p.Description, &p.CreatedAt, &p.UpdatedAt, ); err != nil { return nil, err } return &p, nil }