CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 28s
CI / web (push) Successful in 57s
CI / go (push) Successful in 1m9s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 3m57s
Added a local demo token for development purposes and improved the API token management by normalizing input tokens. Updated the authentication flow to utilize the new token handling, allowing for better session management and user experience. Enhanced the settings component to support the demo token and provide clear instructions for its use in local development.
647 lines
18 KiB
Go
647 lines
18 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"evobgp/internal/store"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
func (p *Postgres) ListCDNSources(tenantID, moduleID string) ([]*store.CDNSource, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "CDN_CIDRS" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
rows, err := p.pool.Query(ctx, `
|
|
SELECT id::text, source_kind, url, COALESCE(prefix_path,''), COALESCE(etag,''), refresh_interval_sec, community_id::text, last_refreshed_at
|
|
FROM module_cdn_source WHERE module_id=$1 ORDER BY url`, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []*store.CDNSource
|
|
for rows.Next() {
|
|
var s store.CDNSource
|
|
s.ModuleID = moduleID
|
|
var ri *int32
|
|
var comm *string
|
|
var last *time.Time
|
|
if err := rows.Scan(&s.ID, &s.SourceKind, &s.URL, &s.PrefixPath, &s.Etag, &ri, &comm, &last); err != nil {
|
|
continue
|
|
}
|
|
if ri != nil {
|
|
v := int(*ri)
|
|
s.RefreshIntervalSec = &v
|
|
}
|
|
s.CommunityID = strOrNil(comm)
|
|
if last != nil {
|
|
t := last.UTC()
|
|
s.LastRefreshedAt = &t
|
|
}
|
|
out = append(out, &s)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) CreateCDNSource(tenantID, moduleID string, in *store.CDNSource) (*store.CDNSource, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "CDN_CIDRS" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
if in == nil || strings.TrimSpace(in.URL) == "" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
id := uuid.NewString()
|
|
_, err = p.pool.Exec(ctx, `
|
|
INSERT INTO module_cdn_source (id, module_id, source_kind, url, prefix_path, etag, refresh_interval_sec, community_id, last_refreshed_at)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7, NULLIF($8::uuid, '00000000-0000-0000-0000-000000000000'::uuid), $9)`,
|
|
id, moduleID, in.SourceKind, strings.TrimSpace(in.URL), strings.TrimSpace(in.PrefixPath), in.Etag, nullInt32Ptr(in.RefreshIntervalSec), uuidOrNilPtr(in.CommunityID), nullTimePtr(in.LastRefreshedAt))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getCDNSource(ctx, moduleID, id)
|
|
}
|
|
|
|
func (p *Postgres) getCDNSource(ctx context.Context, moduleID, id string) (*store.CDNSource, error) {
|
|
var s store.CDNSource
|
|
s.ModuleID = moduleID
|
|
var ri *int32
|
|
var comm *string
|
|
var last *time.Time
|
|
err := p.pool.QueryRow(ctx, `
|
|
SELECT id::text, source_kind, url, COALESCE(prefix_path,''), COALESCE(etag,''), refresh_interval_sec, community_id::text, last_refreshed_at
|
|
FROM module_cdn_source WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(&s.ID, &s.SourceKind, &s.URL, &s.PrefixPath, &s.Etag, &ri, &comm, &last)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ri != nil {
|
|
v := int(*ri)
|
|
s.RefreshIntervalSec = &v
|
|
}
|
|
s.CommunityID = strOrNil(comm)
|
|
if last != nil {
|
|
t := last.UTC()
|
|
s.LastRefreshedAt = &t
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
func uuidOrNilPtr(s *string) any {
|
|
if s == nil || strings.TrimSpace(*s) == "" {
|
|
return nil
|
|
}
|
|
return strings.TrimSpace(*s)
|
|
}
|
|
|
|
func (p *Postgres) UpdateCDNSource(tenantID, moduleID, sourceID string, patch *store.CDNSourcePatch) (*store.CDNSource, error) {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return nil, err
|
|
}
|
|
cur, err := p.getCDNSource(context.Background(), moduleID, sourceID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, store.ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
if patch.SourceKind != nil {
|
|
cur.SourceKind = *patch.SourceKind
|
|
}
|
|
if patch.URL != nil {
|
|
cur.URL = strings.TrimSpace(*patch.URL)
|
|
}
|
|
if patch.PrefixPath != nil {
|
|
cur.PrefixPath = strings.TrimSpace(*patch.PrefixPath)
|
|
}
|
|
if patch.Etag != nil {
|
|
cur.Etag = *patch.Etag
|
|
}
|
|
if patch.RefreshIntervalSec != nil {
|
|
cur.RefreshIntervalSec = patch.RefreshIntervalSec
|
|
}
|
|
if patch.CommunityID != nil {
|
|
v := strings.TrimSpace(*patch.CommunityID)
|
|
if v == "" {
|
|
cur.CommunityID = nil
|
|
} else {
|
|
cur.CommunityID = &v
|
|
}
|
|
}
|
|
if patch.LastRefreshedAt != nil {
|
|
t := patch.LastRefreshedAt.UTC()
|
|
cur.LastRefreshedAt = &t
|
|
}
|
|
ctx := context.Background()
|
|
_, err = p.pool.Exec(ctx, `
|
|
UPDATE module_cdn_source SET source_kind=$3, url=$4, prefix_path=$5, etag=$6, refresh_interval_sec=$7,
|
|
community_id=NULLIF($8::uuid, '00000000-0000-0000-0000-000000000000'::uuid), last_refreshed_at=$9, updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
sourceID, moduleID, cur.SourceKind, cur.URL, cur.PrefixPath, cur.Etag, nullInt32Ptr(cur.RefreshIntervalSec), uuidOrNilPtr(cur.CommunityID), nullTimePtr(cur.LastRefreshedAt))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getCDNSource(ctx, moduleID, sourceID)
|
|
}
|
|
|
|
func (p *Postgres) DeleteCDNSource(tenantID, moduleID, sourceID string) error {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
tag, err := p.pool.Exec(ctx, `DELETE FROM module_cdn_source WHERE id=$1 AND module_id=$2`, sourceID, moduleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return store.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) ListASEntries(tenantID, moduleID string) ([]*store.ASEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "AS_PREFIXES" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
rows, err := p.pool.Query(ctx, `
|
|
SELECT id::text, asn, community_id::text, asn_name, prefix_count, asn_resolved_at
|
|
FROM module_as_entry WHERE module_id=$1 ORDER BY asn`, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []*store.ASEntry
|
|
for rows.Next() {
|
|
var e store.ASEntry
|
|
e.ModuleID = moduleID
|
|
var comm, asnName *string
|
|
var pc *int64
|
|
var at *time.Time
|
|
if err := rows.Scan(&e.ID, &e.ASN, &comm, &asnName, &pc, &at); err != nil {
|
|
continue
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
if asnName != nil {
|
|
e.ASNName = *asnName
|
|
}
|
|
e.PrefixCount = pc
|
|
e.ASNResolvedAt = at
|
|
out = append(out, &e)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) CreateASEntry(tenantID, moduleID string, in *store.ASEntry) (*store.ASEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "AS_PREFIXES" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
if in == nil || !store.ValidASN(in.ASN) {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
id := uuid.NewString()
|
|
_, err = p.pool.Exec(ctx, `
|
|
INSERT INTO module_as_entry (id, module_id, asn, community_id)
|
|
VALUES ($1,$2,$3,NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid))`,
|
|
id, moduleID, in.ASN, uuidOrNilPtr(in.CommunityID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getASEntry(ctx, moduleID, id)
|
|
}
|
|
|
|
func (p *Postgres) getASEntry(ctx context.Context, moduleID, id string) (*store.ASEntry, error) {
|
|
var e store.ASEntry
|
|
e.ModuleID = moduleID
|
|
var comm, asnName *string
|
|
var pc *int64
|
|
var at *time.Time
|
|
err := p.pool.QueryRow(ctx, `
|
|
SELECT id::text, asn, community_id::text, asn_name, prefix_count, asn_resolved_at
|
|
FROM module_as_entry WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(
|
|
&e.ID, &e.ASN, &comm, &asnName, &pc, &at)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
if asnName != nil {
|
|
e.ASNName = *asnName
|
|
}
|
|
e.PrefixCount = pc
|
|
e.ASNResolvedAt = at
|
|
return &e, nil
|
|
}
|
|
|
|
func (p *Postgres) UpdateASEntry(tenantID, moduleID, entryID string, patch *store.ASEntryPatch) (*store.ASEntry, error) {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return nil, err
|
|
}
|
|
cur, err := p.getASEntry(context.Background(), moduleID, entryID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, store.ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
prevASN := cur.ASN
|
|
if patch.ASN != nil {
|
|
cur.ASN = *patch.ASN
|
|
}
|
|
if patch.CommunityID != nil {
|
|
v := strings.TrimSpace(*patch.CommunityID)
|
|
if v == "" {
|
|
cur.CommunityID = nil
|
|
} else {
|
|
cur.CommunityID = &v
|
|
}
|
|
}
|
|
if !store.ValidASN(cur.ASN) {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
clearResolve := patch.ASN != nil && cur.ASN != prevASN
|
|
ctx := context.Background()
|
|
if clearResolve {
|
|
_, err = p.pool.Exec(ctx, `
|
|
UPDATE module_as_entry SET asn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
asn_name=NULL, prefix_count=NULL, asn_resolved_at=NULL, updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
entryID, moduleID, cur.ASN, uuidOrNilPtr(cur.CommunityID))
|
|
} else {
|
|
_, err = p.pool.Exec(ctx, `
|
|
UPDATE module_as_entry SET asn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid), updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
entryID, moduleID, cur.ASN, uuidOrNilPtr(cur.CommunityID))
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getASEntry(ctx, moduleID, entryID)
|
|
}
|
|
|
|
func (p *Postgres) UpdateASEntryResolveMeta(tenantID, moduleID, entryID string, asnName string, prefixCount int64, resolvedAt time.Time) error {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
var nameArg any
|
|
sn := strings.TrimSpace(asnName)
|
|
if sn == "" {
|
|
nameArg = nil
|
|
} else {
|
|
nameArg = sn
|
|
}
|
|
tag, err := p.pool.Exec(ctx, `
|
|
UPDATE module_as_entry SET asn_name=$3, prefix_count=$4, asn_resolved_at=$5, updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
entryID, moduleID, nameArg, prefixCount, resolvedAt.UTC())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return store.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) UpdateASEntryResolveMetaBatch(tenantID, moduleID string, updates []store.ASEntryResolveMetaUpdate, resolvedAt time.Time) error {
|
|
if len(updates) == 0 {
|
|
return nil
|
|
}
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
batch := &pgx.Batch{}
|
|
for _, u := range updates {
|
|
var nameArg any
|
|
sn := strings.TrimSpace(u.ASNName)
|
|
if sn == "" {
|
|
nameArg = nil
|
|
} else {
|
|
nameArg = sn
|
|
}
|
|
batch.Queue(`
|
|
UPDATE module_as_entry SET asn_name=$3, prefix_count=$4, asn_resolved_at=$5, updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
u.EntryID, moduleID, nameArg, u.PrefixCount, resolvedAt.UTC())
|
|
}
|
|
br := p.pool.SendBatch(ctx, batch)
|
|
defer func() { _ = br.Close() }()
|
|
for range updates {
|
|
if _, err := br.Exec(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) DeleteASEntry(tenantID, moduleID, entryID string) error {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
tag, err := p.pool.Exec(ctx, `DELETE FROM module_as_entry WHERE id=$1 AND module_id=$2`, entryID, moduleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return store.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) ListDomainEntries(tenantID, moduleID string) ([]*store.DomainEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "DOMAINS" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
rows, err := p.pool.Query(ctx, `SELECT id::text, fqdn, community_id::text FROM module_domain_entry WHERE module_id=$1`, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []*store.DomainEntry
|
|
for rows.Next() {
|
|
var e store.DomainEntry
|
|
e.ModuleID = moduleID
|
|
var comm *string
|
|
if err := rows.Scan(&e.ID, &e.FQDN, &comm); err != nil {
|
|
continue
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
out = append(out, &e)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) CreateDomainEntry(tenantID, moduleID string, in *store.DomainEntry) (*store.DomainEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "DOMAINS" || in == nil || strings.TrimSpace(in.FQDN) == "" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
id := uuid.NewString()
|
|
_, err = p.pool.Exec(ctx, `
|
|
INSERT INTO module_domain_entry (id, module_id, fqdn, community_id)
|
|
VALUES ($1,$2,$3, NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid))`,
|
|
id, moduleID, strings.TrimSpace(in.FQDN), uuidOrNilPtr(in.CommunityID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getDomainEntry(ctx, moduleID, id)
|
|
}
|
|
|
|
func (p *Postgres) getDomainEntry(ctx context.Context, moduleID, id string) (*store.DomainEntry, error) {
|
|
var e store.DomainEntry
|
|
e.ModuleID = moduleID
|
|
var comm *string
|
|
err := p.pool.QueryRow(ctx, `SELECT id::text, fqdn, community_id::text FROM module_domain_entry WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(&e.ID, &e.FQDN, &comm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
return &e, nil
|
|
}
|
|
|
|
func (p *Postgres) UpdateDomainEntry(tenantID, moduleID, entryID string, patch *store.DomainEntryPatch) (*store.DomainEntry, error) {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return nil, err
|
|
}
|
|
cur, err := p.getDomainEntry(context.Background(), moduleID, entryID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, store.ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
if patch.FQDN != nil {
|
|
cur.FQDN = strings.TrimSpace(*patch.FQDN)
|
|
}
|
|
if patch.CommunityID != nil {
|
|
v := strings.TrimSpace(*patch.CommunityID)
|
|
if v == "" {
|
|
cur.CommunityID = nil
|
|
} else {
|
|
cur.CommunityID = &v
|
|
}
|
|
}
|
|
ctx := context.Background()
|
|
_, err = p.pool.Exec(ctx, `
|
|
UPDATE module_domain_entry SET fqdn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid), updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
entryID, moduleID, cur.FQDN, uuidOrNilPtr(cur.CommunityID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getDomainEntry(ctx, moduleID, entryID)
|
|
}
|
|
|
|
func (p *Postgres) DeleteDomainEntry(tenantID, moduleID, entryID string) error {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
tag, err := p.pool.Exec(ctx, `DELETE FROM module_domain_entry WHERE id=$1 AND module_id=$2`, entryID, moduleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return store.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) ListIPRangeEntries(tenantID, moduleID string) ([]*store.IPRangeEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "IP_RANGES" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
rows, err := p.pool.Query(ctx, `SELECT id::text, prefix::text, community_id::text FROM module_ip_range_entry WHERE module_id=$1`, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []*store.IPRangeEntry
|
|
for rows.Next() {
|
|
var e store.IPRangeEntry
|
|
e.ModuleID = moduleID
|
|
var comm *string
|
|
if err := rows.Scan(&e.ID, &e.Prefix, &comm); err != nil {
|
|
continue
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
out = append(out, &e)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) CreateIPRangeEntry(tenantID, moduleID string, in *store.IPRangeEntry) (*store.IPRangeEntry, error) {
|
|
mod, err := p.GetModule(tenantID, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mod.Type != "IP_RANGES" || in == nil || strings.TrimSpace(in.Prefix) == "" {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
id := uuid.NewString()
|
|
_, err = p.pool.Exec(ctx, `
|
|
INSERT INTO module_ip_range_entry (id, module_id, prefix, community_id)
|
|
VALUES ($1,$2,$3::cidr, NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid))`,
|
|
id, moduleID, strings.TrimSpace(in.Prefix), uuidOrNilPtr(in.CommunityID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getIPRangeEntry(ctx, moduleID, id)
|
|
}
|
|
|
|
func (p *Postgres) getIPRangeEntry(ctx context.Context, moduleID, id string) (*store.IPRangeEntry, error) {
|
|
var e store.IPRangeEntry
|
|
e.ModuleID = moduleID
|
|
var comm *string
|
|
err := p.pool.QueryRow(ctx, `SELECT id::text, prefix::text, community_id::text FROM module_ip_range_entry WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(&e.ID, &e.Prefix, &comm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.CommunityID = strOrNil(comm)
|
|
return &e, nil
|
|
}
|
|
|
|
func (p *Postgres) UpdateIPRangeEntry(tenantID, moduleID, entryID string, patch *store.IPRangePatch) (*store.IPRangeEntry, error) {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return nil, err
|
|
}
|
|
cur, err := p.getIPRangeEntry(context.Background(), moduleID, entryID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, store.ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
if patch.Prefix != nil {
|
|
cur.Prefix = strings.TrimSpace(*patch.Prefix)
|
|
}
|
|
if patch.CommunityID != nil {
|
|
v := strings.TrimSpace(*patch.CommunityID)
|
|
if v == "" {
|
|
cur.CommunityID = nil
|
|
} else {
|
|
cur.CommunityID = &v
|
|
}
|
|
}
|
|
ctx := context.Background()
|
|
_, err = p.pool.Exec(ctx, `
|
|
UPDATE module_ip_range_entry SET prefix=$3::cidr, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid), updated_at=now()
|
|
WHERE id=$1 AND module_id=$2`,
|
|
entryID, moduleID, cur.Prefix, uuidOrNilPtr(cur.CommunityID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return p.getIPRangeEntry(ctx, moduleID, entryID)
|
|
}
|
|
|
|
func (p *Postgres) DeleteIPRangeEntry(tenantID, moduleID, entryID string) error {
|
|
if _, err := p.GetModule(tenantID, moduleID); err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
tag, err := p.pool.Exec(ctx, `DELETE FROM module_ip_range_entry WHERE id=$1 AND module_id=$2`, entryID, moduleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return store.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Postgres) ListGlobalSettings(tenantID string) (map[string]any, error) {
|
|
if _, err := uuid.Parse(tenantID); err != nil {
|
|
return nil, store.ErrInvalidInput
|
|
}
|
|
ctx := context.Background()
|
|
rows, err := p.pool.Query(ctx, `SELECT key, value_json FROM global_settings WHERE tenant_id=$1`, tenantID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := make(map[string]any)
|
|
for rows.Next() {
|
|
var k string
|
|
var vj []byte
|
|
if err := rows.Scan(&k, &vj); err != nil {
|
|
continue
|
|
}
|
|
var v any
|
|
_ = json.Unmarshal(vj, &v)
|
|
out[k] = v
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) PatchGlobalSettings(tenantID string, patch map[string]any) error {
|
|
if _, err := uuid.Parse(tenantID); err != nil {
|
|
return store.ErrInvalidInput
|
|
}
|
|
if patch == nil {
|
|
return nil
|
|
}
|
|
ctx := context.Background()
|
|
for k, v := range patch {
|
|
if strings.TrimSpace(k) == "" {
|
|
continue
|
|
}
|
|
if v == nil {
|
|
_, _ = p.pool.Exec(ctx, `DELETE FROM global_settings WHERE tenant_id=$1 AND key=$2`, tenantID, k)
|
|
continue
|
|
}
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = p.pool.Exec(ctx, `
|
|
INSERT INTO global_settings (tenant_id, key, value_json) VALUES ($1,$2,$3::jsonb)
|
|
ON CONFLICT (tenant_id, key) DO UPDATE SET value_json = EXCLUDED.value_json, updated_at = now()`,
|
|
tenantID, k, string(b))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ store.Backend = (*Postgres)(nil)
|