- keepalived: pg_role='standby' hat Vorrang vor role für BACKUP-Bestimmung - keepalived-master.sh: gecrasht Dienste beim MASTER-Übergang starten (nicht nur reload) - confighash: ip_addresses per Interface-Name hashen statt per FK (Cross-Node-Drift-Fix) - TOTP/2FA: RFC 6238 — Setup-Flow, QR-Code, Admin-Disable; two-step Login - Firewall-UI: Enterprise-Design — auto-Beschreibung, icon-only Actions, zero-hit Indikator - fe80-Filter: Link-local IPv6 aus NTP/DNS Listen-Dropdowns entfernen - VIP-Dashboard, Dual-Path VRRP, GW-Tracking (Migrations 0033/0034) - Forward Proxy + DNS erweiterte Einstellungen (Migrations 0031/0032) - unbound-control: edgeguard in unbound-Gruppe via postinst Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
157 lines
4.6 KiB
Go
157 lines
4.6 KiB
Go
// Package forwardproxy provides CRUD against the forward_proxy_acls
|
|
// table and settings in forward_proxy_settings. Renderer in internal/squid
|
|
// consumes both tables to emit /etc/edgeguard/squid/squid.conf.
|
|
package forwardproxy
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/models"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("forward-proxy ACL not found")
|
|
|
|
type Repo struct {
|
|
Pool *pgxpool.Pool
|
|
}
|
|
|
|
func New(pool *pgxpool.Pool) *Repo { return &Repo{Pool: pool} }
|
|
|
|
const baseSelect = `
|
|
SELECT id, name, acl_type, value, action, priority, active, comment,
|
|
created_at, updated_at
|
|
FROM forward_proxy_acls
|
|
`
|
|
|
|
func (r *Repo) List(ctx context.Context) ([]models.ForwardProxyACL, error) {
|
|
rows, err := r.Pool.Query(ctx, baseSelect+" ORDER BY priority DESC, id ASC")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := make([]models.ForwardProxyACL, 0, 8)
|
|
for rows.Next() {
|
|
a, err := scan(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, *a)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (r *Repo) Get(ctx context.Context, id int64) (*models.ForwardProxyACL, error) {
|
|
row := r.Pool.QueryRow(ctx, baseSelect+" WHERE id = $1", id)
|
|
a, err := scan(row)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
func (r *Repo) Create(ctx context.Context, a models.ForwardProxyACL) (*models.ForwardProxyACL, error) {
|
|
row := r.Pool.QueryRow(ctx, `
|
|
INSERT INTO forward_proxy_acls (name, acl_type, value, action, priority, active, comment)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING id, name, acl_type, value, action, priority, active, comment,
|
|
created_at, updated_at`,
|
|
a.Name, a.ACLType, a.Value, a.Action, a.Priority, a.Active, a.Comment)
|
|
return scan(row)
|
|
}
|
|
|
|
func (r *Repo) Update(ctx context.Context, id int64, a models.ForwardProxyACL) (*models.ForwardProxyACL, error) {
|
|
row := r.Pool.QueryRow(ctx, `
|
|
UPDATE forward_proxy_acls SET
|
|
name = $1, acl_type = $2, value = $3, action = $4,
|
|
priority = $5, active = $6, comment = $7,
|
|
updated_at = NOW()
|
|
WHERE id = $8
|
|
RETURNING id, name, acl_type, value, action, priority, active, comment,
|
|
created_at, updated_at`,
|
|
a.Name, a.ACLType, a.Value, a.Action, a.Priority, a.Active, a.Comment, id)
|
|
out, err := scan(row)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (r *Repo) Delete(ctx context.Context, id int64) error {
|
|
tag, err := r.Pool.Exec(ctx, `DELETE FROM forward_proxy_acls WHERE id = $1`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Settings returns the singleton forward_proxy_settings row.
|
|
func (r *Repo) GetSettings(ctx context.Context) (*models.ForwardProxySettings, error) {
|
|
var s models.ForwardProxySettings
|
|
if err := r.Pool.QueryRow(ctx, `
|
|
SELECT id, listen_addresses, listen_port,
|
|
cache_mem_mb, cache_dir_mb, max_obj_size_mb,
|
|
connect_timeout, read_timeout, request_timeout,
|
|
created_at, updated_at
|
|
FROM forward_proxy_settings WHERE id=1`).Scan(
|
|
&s.ID, &s.ListenAddresses, &s.ListenPort,
|
|
&s.CacheMemMB, &s.CacheDirMB, &s.MaxObjSizeMB,
|
|
&s.ConnectTimeout, &s.ReadTimeout, &s.RequestTimeout,
|
|
&s.CreatedAt, &s.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
func (r *Repo) UpdateSettings(ctx context.Context, s models.ForwardProxySettings) (*models.ForwardProxySettings, error) {
|
|
var out models.ForwardProxySettings
|
|
if err := r.Pool.QueryRow(ctx, `
|
|
UPDATE forward_proxy_settings SET
|
|
listen_addresses=$1, listen_port=$2,
|
|
cache_mem_mb=$3, cache_dir_mb=$4, max_obj_size_mb=$5,
|
|
connect_timeout=$6, read_timeout=$7, request_timeout=$8,
|
|
updated_at=NOW()
|
|
WHERE id=1
|
|
RETURNING id, listen_addresses, listen_port,
|
|
cache_mem_mb, cache_dir_mb, max_obj_size_mb,
|
|
connect_timeout, read_timeout, request_timeout,
|
|
created_at, updated_at`,
|
|
s.ListenAddresses, s.ListenPort,
|
|
s.CacheMemMB, s.CacheDirMB, s.MaxObjSizeMB,
|
|
s.ConnectTimeout, s.ReadTimeout, s.RequestTimeout,
|
|
).Scan(
|
|
&out.ID, &out.ListenAddresses, &out.ListenPort,
|
|
&out.CacheMemMB, &out.CacheDirMB, &out.MaxObjSizeMB,
|
|
&out.ConnectTimeout, &out.ReadTimeout, &out.RequestTimeout,
|
|
&out.CreatedAt, &out.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
func scan(row interface{ Scan(...any) error }) (*models.ForwardProxyACL, error) {
|
|
var a models.ForwardProxyACL
|
|
if err := row.Scan(
|
|
&a.ID, &a.Name, &a.ACLType, &a.Value, &a.Action,
|
|
&a.Priority, &a.Active, &a.Comment,
|
|
&a.CreatedAt, &a.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &a, nil
|
|
}
|