feat(api): enhance IP list management with new entry operations and improved error handling
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m50s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Added endpoints for adding and deleting entries in IP lists.
- Refactored list creation logic to handle manual list types more effectively.
- Updated refresh logic to rebuild manual list entries.
- Improved error handling for entry operations to ensure data integrity.
- Enhanced response structure for list detail retrieval.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-20 23:01:23 +07:00
co-authored by Cursor
parent 7f0c1009de
commit 3f7672ab7c
11 changed files with 805 additions and 205 deletions
+5 -39
View File
@@ -1,7 +1,8 @@
import { createHash } from 'node:crypto'
import { resolve4, resolve6 } from 'node:dns/promises'
import type { Db } from '@evofw/db'
import { repos } from '@evofw/db'
import { refreshAllHostnameRules } from '../policy/resolve-hostname.js'
import { rebuildManualListEntries } from './entries.js'
function uniq(cidrs: string[]): string[] {
return [...new Set(cidrs.map((c) => c.trim()).filter(Boolean))].sort()
@@ -46,27 +47,6 @@ async function fetchJsonUrl(url: string): Promise<string[]> {
return uniq(out)
}
async function resolveDomains(domains: string[]): Promise<string[]> {
const out: string[] = []
for (const d of domains) {
const host = d.trim().replace(/\.$/, '')
if (!host) continue
try {
const a = await resolve4(host)
out.push(...a.map((ip) => `${ip}/32`))
} catch {
/* ignore */
}
try {
const aaaa = await resolve6(host)
out.push(...aaaa.map((ip) => `${ip}/128`))
} catch {
/* ignore */
}
}
return uniq(out)
}
async function fetchEvobgpCommunity(
apiUrl: string,
token: string,
@@ -118,21 +98,13 @@ export async function refreshIpList(db: Db, listId: string): Promise<void> {
try {
let cidrs: string[] = []
if (list.type === 'static') {
cidrs = repos.listIpListEntries(db, listId).map((e) => e.cidr)
if (list.type === 'static' || list.type === 'domains') {
cidrs = await rebuildManualListEntries(db, listId)
} else if (list.type === 'json_url') {
const url = String(config.url ?? '')
if (!url) throw new Error('config.url required')
cidrs = await fetchJsonUrl(url)
repos.replaceIpListEntries(db, listId, cidrs)
} else if (list.type === 'domains') {
const domains = Array.isArray(config.domains)
? (config.domains as string[])
: String(config.domains ?? '')
.split(/[\s,]+/)
.filter(Boolean)
cidrs = await resolveDomains(domains)
repos.replaceIpListEntries(db, listId, cidrs)
} else if (list.type === 'evobgp_community') {
const apiUrl =
String(config.api_url ?? '') || repos.getSetting(db, 'evobgp_api_url')
@@ -154,10 +126,7 @@ export async function refreshIpList(db: Db, listId: string): Promise<void> {
lastError: null,
})
// Bump all agents so they re-fetch policy
for (const a of repos.listAgents(db)) {
if (a.status === 'approved') repos.bumpAgentGeneration(db, a.id)
}
repos.bumpAgentsForList(db, listId)
} catch (err) {
repos.updateIpList(db, listId, {
lastError: err instanceof Error ? err.message : String(err),
@@ -166,11 +135,8 @@ export async function refreshIpList(db: Db, listId: string): Promise<void> {
}
}
import { refreshAllHostnameRules } from '../policy/resolve-hostname.js'
export async function refreshAllLists(db: Db): Promise<void> {
for (const list of repos.listIpLists(db)) {
if (list.type === 'static') continue
await refreshIpList(db, list.id)
}
await refreshAllHostnameRules(db)