Migration 0017 fügt backends.websocket BOOL. Wenn aktiv emittiert der HAProxy-Renderer `timeout tunnel 1h` IM Backend-Block; defaults-Section hat den Global-Timeout dafür verloren. Backends ohne WS-Workload bleiben bei strikten HTTP-Timeouts (Connection-Hygiene). Migrations-Heuristik schaltet vm-pool/proxmox/console/vnc-Namen auto auf true damit Proxmox- Konsole nach Deploy weiterhin durchläuft. UI: Switch im Backend-Modal + WS-Tag in der Übersichtstabelle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
122 lines
3.1 KiB
Go
122 lines
3.1 KiB
Go
// Package backends implements CRUD against the `backends` table.
|
|
// Ein Backend ist ein Pool — Name, Scheme, Healthcheck, LB-Algorithm,
|
|
// WebSocket-Flag. Die konkreten Upstream-Server liegen in
|
|
// backend_servers (siehe services/backendservers).
|
|
package backends
|
|
|
|
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("backend not found")
|
|
|
|
type Repo struct {
|
|
Pool *pgxpool.Pool
|
|
}
|
|
|
|
func New(pool *pgxpool.Pool) *Repo { return &Repo{Pool: pool} }
|
|
|
|
const baseSelect = `
|
|
SELECT id, name, scheme, health_check_path, lb_algorithm, websocket, active,
|
|
created_at, updated_at
|
|
FROM backends
|
|
`
|
|
|
|
func (r *Repo) List(ctx context.Context) ([]models.Backend, error) {
|
|
rows, err := r.Pool.Query(ctx, baseSelect+" ORDER BY name ASC")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := make([]models.Backend, 0, 16)
|
|
for rows.Next() {
|
|
b, err := scanBackend(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, *b)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (r *Repo) Get(ctx context.Context, id int64) (*models.Backend, error) {
|
|
row := r.Pool.QueryRow(ctx, baseSelect+" WHERE id = $1", id)
|
|
b, err := scanBackend(row)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (r *Repo) Create(ctx context.Context, b models.Backend) (*models.Backend, error) {
|
|
if b.LBAlgorithm == "" {
|
|
b.LBAlgorithm = "roundrobin"
|
|
}
|
|
row := r.Pool.QueryRow(ctx, `
|
|
INSERT INTO backends (name, scheme, health_check_path, lb_algorithm, websocket, active)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
RETURNING id, name, scheme, health_check_path, lb_algorithm, websocket, active,
|
|
created_at, updated_at`,
|
|
b.Name, b.Scheme, b.HealthCheckPath, b.LBAlgorithm, b.WebSocket, b.Active)
|
|
return scanBackend(row)
|
|
}
|
|
|
|
func (r *Repo) Update(ctx context.Context, id int64, b models.Backend) (*models.Backend, error) {
|
|
if b.LBAlgorithm == "" {
|
|
b.LBAlgorithm = "roundrobin"
|
|
}
|
|
row := r.Pool.QueryRow(ctx, `
|
|
UPDATE backends SET
|
|
name = $1,
|
|
scheme = $2,
|
|
health_check_path = $3,
|
|
lb_algorithm = $4,
|
|
websocket = $5,
|
|
active = $6,
|
|
updated_at = NOW()
|
|
WHERE id = $7
|
|
RETURNING id, name, scheme, health_check_path, lb_algorithm, websocket, active,
|
|
created_at, updated_at`,
|
|
b.Name, b.Scheme, b.HealthCheckPath, b.LBAlgorithm, b.WebSocket, b.Active, id)
|
|
out, err := scanBackend(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 backends WHERE id = $1`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func scanBackend(row interface{ Scan(...any) error }) (*models.Backend, error) {
|
|
var b models.Backend
|
|
if err := row.Scan(
|
|
&b.ID, &b.Name, &b.Scheme,
|
|
&b.HealthCheckPath, &b.LBAlgorithm, &b.WebSocket, &b.Active,
|
|
&b.CreatedAt, &b.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &b, nil
|
|
}
|