721f4c1af3
Storage + CRUD (migration 0006, model, id-keyed store, REST handlers) plus compiler rendering: each section is owned by a device and projected into that device's rendered config (hosts/providers/routes/routing_rules).
235 lines
7.1 KiB
Go
235 lines
7.1 KiB
Go
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)
|
|
}
|