feat(api): enhance IP list management with new entry operations and improved error handling
- 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:
@@ -9,9 +9,17 @@ import {
|
||||
putAgentPolicySetsBodySchema,
|
||||
patchAgentBodySchema,
|
||||
cloneFromBodySchema,
|
||||
listEntriesBodySchema,
|
||||
deleteListEntryBodySchema,
|
||||
isManualListType,
|
||||
} from '@evofw/shared'
|
||||
import { AppError } from '../plugins/error-handler.js'
|
||||
import { refreshIpList } from '../services/lists/refresh.js'
|
||||
import {
|
||||
addListEntries,
|
||||
deleteListEntry,
|
||||
mapListDetail,
|
||||
} from '../services/lists/entries.js'
|
||||
import { evaluateAgentPolicy } from '../services/policy/evaluate.js'
|
||||
import {
|
||||
resolveAndStoreHostnameRule,
|
||||
@@ -279,19 +287,29 @@ export const controlRoutes: FastifyPluginAsync<{ config: AppConfig }> = async (
|
||||
|
||||
app.post('/lists', async (req) => {
|
||||
const body = createIpListBodySchema.parse(req.body)
|
||||
const type =
|
||||
body.type === 'domains' ? 'static' : body.type
|
||||
const id = crypto.randomUUID()
|
||||
const list = repos.insertIpList(app.db, {
|
||||
id,
|
||||
name: body.name,
|
||||
type: body.type,
|
||||
type,
|
||||
configJson: JSON.stringify(body.config ?? {}),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
if (body.entries?.length) {
|
||||
repos.replaceIpListEntries(app.db, id, body.entries)
|
||||
}
|
||||
if (body.type !== 'static') {
|
||||
if (body.entries?.length && isManualListType(type)) {
|
||||
try {
|
||||
await addListEntries(app.db, id, body.entries)
|
||||
} catch (err) {
|
||||
repos.deleteIpList(app.db, id)
|
||||
throw new AppError(
|
||||
'VALIDATION_ERROR',
|
||||
err instanceof Error ? err.message : String(err),
|
||||
400,
|
||||
)
|
||||
}
|
||||
} else if (!isManualListType(type)) {
|
||||
await refreshIpList(app.db, id)
|
||||
}
|
||||
return {
|
||||
@@ -305,33 +323,54 @@ export const controlRoutes: FastifyPluginAsync<{ config: AppConfig }> = async (
|
||||
})
|
||||
|
||||
app.get<{ Params: { id: string } }>('/lists/:id', async (req) => {
|
||||
const l = repos.getIpList(app.db, req.params.id)
|
||||
if (!l) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
return {
|
||||
id: l.id,
|
||||
name: l.name,
|
||||
type: l.type,
|
||||
config_json: l.configJson,
|
||||
content_hash: l.contentHash,
|
||||
refreshed_at: l.refreshedAt,
|
||||
last_error: l.lastError,
|
||||
entries: repos.listIpListEntries(app.db, l.id).map((e) => e.cidr),
|
||||
created_at: l.createdAt,
|
||||
updated_at: l.updatedAt,
|
||||
}
|
||||
const detail = mapListDetail(app.db, req.params.id)
|
||||
if (!detail) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
return detail
|
||||
})
|
||||
|
||||
app.post<{ Params: { id: string } }>(
|
||||
'/lists/:id/entries',
|
||||
async (req) => {
|
||||
const l = repos.getIpList(app.db, req.params.id)
|
||||
if (!l) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
const body = listEntriesBodySchema.parse(req.body)
|
||||
try {
|
||||
const result = await addListEntries(app.db, l.id, body.values)
|
||||
return mapListDetail(app.db, l.id) ?? result
|
||||
} catch (err) {
|
||||
throw new AppError(
|
||||
'VALIDATION_ERROR',
|
||||
err instanceof Error ? err.message : String(err),
|
||||
400,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
app.delete<{ Params: { id: string } }>(
|
||||
'/lists/:id/entries',
|
||||
async (req) => {
|
||||
const l = repos.getIpList(app.db, req.params.id)
|
||||
if (!l) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
const body = deleteListEntryBodySchema.parse(req.body)
|
||||
try {
|
||||
await deleteListEntry(app.db, l.id, body.value)
|
||||
return mapListDetail(app.db, l.id)
|
||||
} catch (err) {
|
||||
throw new AppError(
|
||||
'VALIDATION_ERROR',
|
||||
err instanceof Error ? err.message : String(err),
|
||||
400,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
app.post<{ Params: { id: string } }>('/lists/:id/refresh', async (req) => {
|
||||
await refreshIpList(app.db, req.params.id)
|
||||
const l = repos.getIpList(app.db, req.params.id)
|
||||
if (!l) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
return {
|
||||
id: l.id,
|
||||
content_hash: l.contentHash,
|
||||
refreshed_at: l.refreshedAt,
|
||||
last_error: l.lastError,
|
||||
entry_count: repos.listIpListEntries(app.db, l.id).length,
|
||||
}
|
||||
const detail = mapListDetail(app.db, req.params.id)
|
||||
if (!detail) throw new AppError('NOT_FOUND', 'List not found', 404)
|
||||
return detail
|
||||
})
|
||||
|
||||
app.delete<{ Params: { id: string } }>('/lists/:id', async (req) => {
|
||||
|
||||
Reference in New Issue
Block a user