package store import ( "context" "errors" "github.com/jackc/pgx/v5" "git.unkin.net/unkin/tomswallapi/internal/model" ) // ---- Hosts ----------------------------------------------------------------- func (s *Store) ListHosts(ctx context.Context) ([]model.Host, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, zone, interface, addresses, exclusions, dynamic FROM hosts ORDER BY id`) if err != nil { return nil, err } defer rows.Close() var out []model.Host for rows.Next() { h, err := scanHost(rows) if err != nil { return nil, err } out = append(out, h) } return out, rows.Err() } func (s *Store) GetHost(ctx context.Context, id int64) (model.Host, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, zone, interface, addresses, exclusions, dynamic FROM hosts WHERE id = $1`, id) if err != nil { return model.Host{}, err } defer rows.Close() if !rows.Next() { return model.Host{}, ErrNotFound } return scanHost(rows) } func scanHost(rows pgx.Rows) (model.Host, error) { var h model.Host var addrs, excl []byte if err := rows.Scan(&h.ID, &h.Device, &h.Zone, &h.Interface, &addrs, &excl, &h.Dynamic); err != nil { return h, err } if err := unmarshalStrings(addrs, &h.Addresses); err != nil { return h, err } return h, unmarshalStrings(excl, &h.Exclusions) } func (s *Store) CreateHost(ctx context.Context, h model.Host) (int64, error) { addrs, _ := jsonb(h.Addresses) excl, _ := jsonb(h.Exclusions) var id int64 err := pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error { if err := tx.QueryRow(ctx, ` INSERT INTO hosts (device, zone, interface, addresses, exclusions, dynamic) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`, h.Device, h.Zone, h.Interface, addrs, excl, h.Dynamic).Scan(&id); err != nil { return err } return bump(ctx, tx) }) return id, err } func (s *Store) DeleteHost(ctx context.Context, id int64) error { return s.deleteOne(ctx, `DELETE FROM hosts WHERE id = $1`, id) } // ---- Providers ------------------------------------------------------------- func (s *Store) ListProviders(ctx context.Context) ([]model.Provider, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, name, number, mark, duplicate, interface, gateway, copy FROM providers ORDER BY id`) if err != nil { return nil, err } defer rows.Close() var out []model.Provider for rows.Next() { p, err := scanProvider(rows) if err != nil { return nil, err } out = append(out, p) } return out, rows.Err() } func (s *Store) GetProvider(ctx context.Context, id int64) (model.Provider, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, name, number, mark, duplicate, interface, gateway, copy FROM providers WHERE id = $1`, id) if err != nil { return model.Provider{}, err } defer rows.Close() if !rows.Next() { return model.Provider{}, ErrNotFound } return scanProvider(rows) } func scanProvider(rows pgx.Rows) (model.Provider, error) { var p model.Provider var cp []byte if err := rows.Scan(&p.ID, &p.Device, &p.Name, &p.Number, &p.Mark, &p.Duplicate, &p.Interface, &p.Gateway, &cp); err != nil { return p, err } return p, unmarshalStrings(cp, &p.Copy) } func (s *Store) CreateProvider(ctx context.Context, p model.Provider) (int64, error) { cp, _ := jsonb(p.Copy) var id int64 err := pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error { if err := tx.QueryRow(ctx, ` INSERT INTO providers (device, name, number, mark, duplicate, interface, gateway, copy) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`, p.Device, p.Name, p.Number, p.Mark, p.Duplicate, p.Interface, p.Gateway, cp).Scan(&id); err != nil { return err } return bump(ctx, tx) }) return id, err } func (s *Store) DeleteProvider(ctx context.Context, id int64) error { return s.deleteOne(ctx, `DELETE FROM providers WHERE id = $1`, id) } // ---- Routes ---------------------------------------------------------------- func (s *Store) ListRoutes(ctx context.Context) ([]model.Route, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, provider, dest, gateway, oif, persistent, comment FROM routes ORDER BY id`) if err != nil { return nil, err } defer rows.Close() var out []model.Route for rows.Next() { var r model.Route if err := rows.Scan(&r.ID, &r.Device, &r.Provider, &r.Dest, &r.Gateway, &r.Oif, &r.Persistent, &r.Comment); err != nil { return nil, err } out = append(out, r) } return out, rows.Err() } func (s *Store) GetRoute(ctx context.Context, id int64) (model.Route, error) { var r model.Route err := s.pool.QueryRow(ctx, `SELECT id, device, provider, dest, gateway, oif, persistent, comment FROM routes WHERE id = $1`, id, ).Scan(&r.ID, &r.Device, &r.Provider, &r.Dest, &r.Gateway, &r.Oif, &r.Persistent, &r.Comment) if errors.Is(err, pgx.ErrNoRows) { return r, ErrNotFound } return r, err } func (s *Store) CreateRoute(ctx context.Context, r model.Route) (int64, error) { var id int64 err := pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error { if err := tx.QueryRow(ctx, ` INSERT INTO routes (device, provider, dest, gateway, oif, persistent, comment) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id`, r.Device, r.Provider, r.Dest, r.Gateway, r.Oif, r.Persistent, r.Comment).Scan(&id); err != nil { return err } return bump(ctx, tx) }) return id, err } func (s *Store) DeleteRoute(ctx context.Context, id int64) error { return s.deleteOne(ctx, `DELETE FROM routes WHERE id = $1`, id) } // ---- Routing rules --------------------------------------------------------- func (s *Store) ListRoutingRules(ctx context.Context) ([]model.RoutingRule, error) { rows, err := s.pool.Query(ctx, `SELECT id, device, source, dest, provider, priority, persistent, mark, comment FROM routing_rules ORDER BY priority, id`) if err != nil { return nil, err } defer rows.Close() var out []model.RoutingRule for rows.Next() { var r model.RoutingRule if err := rows.Scan(&r.ID, &r.Device, &r.Source, &r.Dest, &r.Provider, &r.Priority, &r.Persistent, &r.Mark, &r.Comment); err != nil { return nil, err } out = append(out, r) } return out, rows.Err() } func (s *Store) GetRoutingRule(ctx context.Context, id int64) (model.RoutingRule, error) { var r model.RoutingRule err := s.pool.QueryRow(ctx, `SELECT id, device, source, dest, provider, priority, persistent, mark, comment FROM routing_rules WHERE id = $1`, id, ).Scan(&r.ID, &r.Device, &r.Source, &r.Dest, &r.Provider, &r.Priority, &r.Persistent, &r.Mark, &r.Comment) if errors.Is(err, pgx.ErrNoRows) { return r, ErrNotFound } return r, err } func (s *Store) CreateRoutingRule(ctx context.Context, r model.RoutingRule) (int64, error) { var id int64 err := pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error { if err := tx.QueryRow(ctx, ` INSERT INTO routing_rules (device, source, dest, provider, priority, persistent, mark, comment) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`, r.Device, r.Source, r.Dest, r.Provider, r.Priority, r.Persistent, r.Mark, r.Comment).Scan(&id); err != nil { return err } return bump(ctx, tx) }) return id, err } func (s *Store) DeleteRoutingRule(ctx context.Context, id int64) error { return s.deleteOne(ctx, `DELETE FROM routing_rules WHERE id = $1`, id) }