package firewall import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "git.netcell-it.de/projekte/edgeguard-native/internal/models" ) var ErrNATRuleNotFound = errors.New("nat rule not found") type NATRulesRepo struct { Pool *pgxpool.Pool } func NewNATRulesRepo(pool *pgxpool.Pool) *NATRulesRepo { return &NATRulesRepo{Pool: pool} } const natRuleBaseSelect = ` SELECT id, name, priority, enabled, kind, in_zone, out_zone, proto, match_src_cidr, match_dst_cidr, match_dport_start, match_dport_end, target_addr, target_port_start, target_port_end, comment, created_at, updated_at FROM firewall_nat_rules ` func (r *NATRulesRepo) List(ctx context.Context) ([]models.FirewallNATRule, error) { rows, err := r.Pool.Query(ctx, natRuleBaseSelect+" ORDER BY priority DESC, id ASC") if err != nil { return nil, err } defer rows.Close() out := make([]models.FirewallNATRule, 0, 8) for rows.Next() { x, err := scanNATRule(rows) if err != nil { return nil, err } out = append(out, *x) } return out, rows.Err() } func (r *NATRulesRepo) Get(ctx context.Context, id int64) (*models.FirewallNATRule, error) { row := r.Pool.QueryRow(ctx, natRuleBaseSelect+" WHERE id = $1", id) x, err := scanNATRule(row) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNATRuleNotFound } return nil, err } return x, nil } func (r *NATRulesRepo) Create(ctx context.Context, x models.FirewallNATRule) (*models.FirewallNATRule, error) { row := r.Pool.QueryRow(ctx, ` INSERT INTO firewall_nat_rules ( name, priority, enabled, kind, in_zone, out_zone, proto, match_src_cidr, match_dst_cidr, match_dport_start, match_dport_end, target_addr, target_port_start, target_port_end, comment ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 ) RETURNING id, name, priority, enabled, kind, in_zone, out_zone, proto, match_src_cidr, match_dst_cidr, match_dport_start, match_dport_end, target_addr, target_port_start, target_port_end, comment, created_at, updated_at`, x.Name, x.Priority, x.Enabled, x.Kind, x.InZone, x.OutZone, x.Proto, x.MatchSrcCIDR, x.MatchDstCIDR, x.MatchDPortStart, x.MatchDPortEnd, x.TargetAddr, x.TargetPortStart, x.TargetPortEnd, x.Comment) return scanNATRule(row) } func (r *NATRulesRepo) Update(ctx context.Context, id int64, x models.FirewallNATRule) (*models.FirewallNATRule, error) { row := r.Pool.QueryRow(ctx, ` UPDATE firewall_nat_rules SET name = $1, priority = $2, enabled = $3, kind = $4, in_zone = $5, out_zone = $6, proto = $7, match_src_cidr = $8, match_dst_cidr = $9, match_dport_start = $10, match_dport_end = $11, target_addr = $12, target_port_start = $13, target_port_end = $14, comment = $15, updated_at = NOW() WHERE id = $16 RETURNING id, name, priority, enabled, kind, in_zone, out_zone, proto, match_src_cidr, match_dst_cidr, match_dport_start, match_dport_end, target_addr, target_port_start, target_port_end, comment, created_at, updated_at`, x.Name, x.Priority, x.Enabled, x.Kind, x.InZone, x.OutZone, x.Proto, x.MatchSrcCIDR, x.MatchDstCIDR, x.MatchDPortStart, x.MatchDPortEnd, x.TargetAddr, x.TargetPortStart, x.TargetPortEnd, x.Comment, id) out, err := scanNATRule(row) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNATRuleNotFound } return nil, err } return out, nil } func (r *NATRulesRepo) Delete(ctx context.Context, id int64) error { tag, err := r.Pool.Exec(ctx, `DELETE FROM firewall_nat_rules WHERE id = $1`, id) if err != nil { return err } if tag.RowsAffected() == 0 { return ErrNATRuleNotFound } return nil } func scanNATRule(row interface{ Scan(...any) error }) (*models.FirewallNATRule, error) { var x models.FirewallNATRule if err := row.Scan( &x.ID, &x.Name, &x.Priority, &x.Enabled, &x.Kind, &x.InZone, &x.OutZone, &x.Proto, &x.MatchSrcCIDR, &x.MatchDstCIDR, &x.MatchDPortStart, &x.MatchDPortEnd, &x.TargetAddr, &x.TargetPortStart, &x.TargetPortEnd, &x.Comment, &x.CreatedAt, &x.UpdatedAt, ); err != nil { return nil, err } return &x, nil }