feat: enhance DoH profile management and resolver policy in modules
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 1m2s
CI / go (push) Successful in 27s
CI / docker-web (push) Successful in 1m27s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go (push) Successful in 8m5s

- Added `DohResolverPolicy` schema to OpenAPI documentation, defining policies for domain resolution.
- Updated module handling to support multiple DoH profiles via `doh_profile_ids` and introduced `doh_resolver_policy` in the API.
- Refactored related functions to accommodate the new DoH profile structure, ensuring backward compatibility with existing `doh_profile_id`.
- Enhanced UI components to allow selection and management of DoH profiles and policies in the web interface.
- Updated database interactions to handle new fields and ensure proper data normalization.
This commit is contained in:
Denozordec
2026-05-19 14:57:50 +07:00
parent a536a2d5eb
commit 34ecc5c235
21 changed files with 810 additions and 114 deletions
+57 -44
View File
@@ -113,7 +113,8 @@ func (p *Postgres) PeerSessionCountsByState() map[string]int {
func (p *Postgres) ListModules(tenantID string) []*store.Module {
ctx := context.Background()
rows, err := p.pool.Query(ctx, `
SELECT id, type, name, enabled, priority, doh_profile_id::text, refresh_interval_sec, cron_expr, default_community_id::text, last_refreshed_at
SELECT id, type, name, enabled, priority, doh_profile_id::text, doh_resolver_policy,
refresh_interval_sec, cron_expr, default_community_id::text, last_refreshed_at
FROM module WHERE tenant_id = $1 AND deleted_at IS NULL ORDER BY priority, name`, tenantID)
if err != nil {
return nil
@@ -126,9 +127,10 @@ func (p *Postgres) ListModules(tenantID string) []*store.Module {
var doh, dc, cron *string
var refresh *int32
var last *time.Time
if err := rows.Scan(&m.ID, &m.Type, &m.Name, &m.Enabled, &m.Priority, &doh, &refresh, &cron, &dc, &last); err != nil {
if err := rows.Scan(&m.ID, &m.Type, &m.Name, &m.Enabled, &m.Priority, &doh, &m.DohResolverPolicy, &refresh, &cron, &dc, &last); err != nil {
continue
}
m.DohResolverPolicy = store.NormalizeDohResolverPolicy(m.DohResolverPolicy)
if refresh != nil {
m.RefreshIntervalSec = int(*refresh)
}
@@ -145,6 +147,9 @@ func (p *Postgres) ListModules(tenantID string) []*store.Module {
t := last.UTC()
m.LastRefreshedAt = &t
}
if err := p.fillModuleDohFields(ctx, &m); err != nil {
continue
}
out = append(out, &m)
}
return out
@@ -158,9 +163,10 @@ func (p *Postgres) GetModule(tenantID, moduleID string) (*store.Module, error) {
var refresh *int32
var last *time.Time
err := p.pool.QueryRow(ctx, `
SELECT id, type, name, enabled, priority, doh_profile_id::text, refresh_interval_sec, cron_expr, default_community_id::text, last_refreshed_at
SELECT id, type, name, enabled, priority, doh_profile_id::text, doh_resolver_policy,
refresh_interval_sec, cron_expr, default_community_id::text, last_refreshed_at
FROM module WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL`, moduleID, tenantID).Scan(
&m.ID, &m.Type, &m.Name, &m.Enabled, &m.Priority, &doh, &refresh, &cron, &dc, &last)
&m.ID, &m.Type, &m.Name, &m.Enabled, &m.Priority, &doh, &m.DohResolverPolicy, &refresh, &cron, &dc, &last)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, store.ErrNotFound
@@ -183,6 +189,10 @@ func (p *Postgres) GetModule(tenantID, moduleID string) (*store.Module, error) {
t := last.UTC()
m.LastRefreshedAt = &t
}
m.DohResolverPolicy = store.NormalizeDohResolverPolicy(m.DohResolverPolicy)
if err := p.fillModuleDohFields(ctx, &m); err != nil {
return nil, err
}
return &m, nil
}
@@ -190,6 +200,7 @@ func (p *Postgres) CreateModule(tenantID string, in *store.Module) (*store.Modul
if in == nil {
return nil, store.ErrInvalidInput
}
store.NormalizeModuleDoh(in)
ctx := context.Background()
id := uuid.NewString()
var doh, dc any
@@ -211,13 +222,17 @@ func (p *Postgres) CreateModule(tenantID string, in *store.Module) (*store.Modul
if in.LastRefreshedAt != nil {
lastArg = in.LastRefreshedAt.UTC()
}
policy := store.NormalizeDohResolverPolicy(in.DohResolverPolicy)
_, err := p.pool.Exec(ctx, `
INSERT INTO module (id, tenant_id, type, name, enabled, priority, doh_profile_id, refresh_interval_sec, cron_expr, default_community_id, last_refreshed_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`,
id, tenantID, in.Type, in.Name, in.Enabled, in.Priority, doh, ri, cronArg, dc, lastArg)
INSERT INTO module (id, tenant_id, type, name, enabled, priority, doh_profile_id, doh_resolver_policy, refresh_interval_sec, cron_expr, default_community_id, last_refreshed_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)`,
id, tenantID, in.Type, in.Name, in.Enabled, in.Priority, doh, policy, ri, cronArg, dc, lastArg)
if err != nil {
return nil, err
}
if err := p.setModuleDohProfiles(ctx, id, in.DohProfileIDs); err != nil {
return nil, err
}
return p.GetModule(tenantID, id)
}
@@ -230,77 +245,68 @@ func (p *Postgres) UpdateModule(tenantID, moduleID string, patch *store.ModulePa
if err != nil {
return nil, err
}
name := base.Name
en := base.Enabled
pr := base.Priority
ri := base.RefreshIntervalSec
cron := base.CronExpr
var dc, doh *string
dc = base.DefaultCommunityID
doh = base.DohProfileID
last := base.LastRefreshedAt
work := *base
if patch.Name != nil {
name = strings.TrimSpace(*patch.Name)
work.Name = strings.TrimSpace(*patch.Name)
}
if patch.Enabled != nil {
en = *patch.Enabled
work.Enabled = *patch.Enabled
}
if patch.Priority != nil {
pr = *patch.Priority
work.Priority = *patch.Priority
}
if patch.RefreshIntervalSec != nil {
ri = *patch.RefreshIntervalSec
work.RefreshIntervalSec = *patch.RefreshIntervalSec
}
if patch.CronExpr != nil {
cron = *patch.CronExpr
work.CronExpr = *patch.CronExpr
}
if patch.DefaultCommunityID != nil {
v := strings.TrimSpace(*patch.DefaultCommunityID)
if v == "" {
dc = nil
work.DefaultCommunityID = nil
} else {
dc = &v
}
}
if patch.DohProfileID != nil {
v := strings.TrimSpace(*patch.DohProfileID)
if v == "" {
doh = nil
} else {
doh = &v
work.DefaultCommunityID = &v
}
}
store.ApplyModuleDohPatch(&work, patch)
if patch.LastRefreshedAt != nil {
t := patch.LastRefreshedAt.UTC()
last = &t
work.LastRefreshedAt = &t
}
var dcArg, dohArg any
if dc != nil {
dcArg = *dc
if work.DefaultCommunityID != nil {
dcArg = *work.DefaultCommunityID
}
if doh != nil {
dohArg = *doh
if work.DohProfileID != nil {
dohArg = *work.DohProfileID
}
var riArg any
if ri != 0 {
riArg = ri
if work.RefreshIntervalSec != 0 {
riArg = work.RefreshIntervalSec
}
var cronArg any
if strings.TrimSpace(cron) != "" {
cronArg = strings.TrimSpace(cron)
if strings.TrimSpace(work.CronExpr) != "" {
cronArg = strings.TrimSpace(work.CronExpr)
}
var lastArg any
if last != nil {
lastArg = last.UTC()
if work.LastRefreshedAt != nil {
lastArg = work.LastRefreshedAt.UTC()
}
policy := store.NormalizeDohResolverPolicy(work.DohResolverPolicy)
_, err = p.pool.Exec(ctx, `
UPDATE module SET name=$3, enabled=$4, priority=$5, refresh_interval_sec=$6, cron_expr=$7,
default_community_id=$8, doh_profile_id=$9, last_refreshed_at=$10, updated_at=now()
default_community_id=$8, doh_profile_id=$9, doh_resolver_policy=$10, last_refreshed_at=$11, updated_at=now()
WHERE id=$1 AND tenant_id=$2 AND deleted_at IS NULL`,
moduleID, tenantID, name, en, pr, riArg, cronArg, dcArg, dohArg, lastArg)
moduleID, tenantID, work.Name, work.Enabled, work.Priority, riArg, cronArg, dcArg, dohArg, policy, lastArg)
if err != nil {
return nil, err
}
if patch.DohProfileIDs != nil || patch.DohProfileID != nil {
if err := p.setModuleDohProfiles(ctx, moduleID, work.DohProfileIDs); err != nil {
return nil, err
}
}
return p.GetModule(tenantID, moduleID)
}
@@ -1041,6 +1047,13 @@ func (p *Postgres) UpdateDohProfile(tenantID, id string, patch *store.DohProfile
func (p *Postgres) DeleteDohProfile(tenantID, id string) error {
ctx := context.Background()
inUse, err := p.moduleDohProfileInUse(ctx, id)
if err != nil {
return err
}
if inUse {
return store.ErrInvalidInput
}
var n int
_ = p.pool.QueryRow(ctx, `SELECT COUNT(*) FROM module WHERE doh_profile_id=$1::uuid AND deleted_at IS NULL`, id).Scan(&n)
if n > 0 {