Files
EvoFirewall/packages/shared/src/contracts.ts
T
DenozordecandCursor 90d50c2556
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m46s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped
feat(api, web): implement policy mode management for agents and rules
- Added support for policy modes ('blacklist' and 'whitelist') in agent and policy set management.
- Updated API endpoints to handle policy mode during agent assignment and rule operations.
- Enhanced the web UI to display and manage policy modes for agents and rules, ensuring all assigned sets share a consistent mode.
- Introduced new validation to enforce single policy mode across assigned sets for agents.
- Improved error handling for policy mode conflicts and updated documentation accordingly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 02:16:51 +07:00

246 lines
7.5 KiB
TypeScript

import { z } from 'zod'
export const agentPlatformSchema = z.enum(['linux', 'mikrotik'])
export const agentStatusSchema = z.enum([
'invited',
'pending',
'approved',
'revoked',
])
export const policyModeSchema = z.enum(['blacklist', 'whitelist'])
export const policyActionSchema = z.enum(['allow', 'deny'])
export const ipListTypeSchema = z.enum([
'static',
'json_url',
'domains',
'evobgp_community',
])
export const agentSchema = z.object({
id: z.string(),
name: z.string(),
hostname: z.string().nullable().optional(),
platform: agentPlatformSchema,
token_prefix: z.string(),
status: agentStatusSchema,
policy_mode: policyModeSchema,
policy_generation: z.number().int(),
last_seen_at: z.string().nullable().optional(),
last_seen_ip: z.string().nullable().optional(),
last_apply_at: z.string().nullable().optional(),
last_apply_status: z.string().nullable().optional(),
last_apply_error: z.string().nullable().optional(),
last_apply_prefix_count: z.number().int().nullable().optional(),
last_apply_packets_dropped: z.number().int().optional(),
last_apply_packets_accepted: z.number().int().optional(),
last_apply_kernel_method: z.string().nullable().optional(),
client_version: z.string().nullable().optional(),
settings_json: z.string().optional(),
created_at: z.string(),
approved_at: z.string().nullable().optional(),
revoked_at: z.string().nullable().optional(),
install_curl: z.string().nullable().optional(),
install_link_id: z.string().nullable().optional(),
})
export const ipListSchema = z.object({
id: z.string(),
name: z.string(),
type: ipListTypeSchema,
config_json: z.string(),
content_hash: z.string().nullable().optional(),
refreshed_at: z.string().nullable().optional(),
last_error: z.string().nullable().optional(),
entry_count: z.number().int().optional(),
created_at: z.string(),
updated_at: z.string(),
})
export const policyRuleSchema = z.object({
id: z.string(),
set_id: z.string(),
priority: z.number().int(),
action: policyActionSchema,
enabled: z.boolean().optional().default(true),
list_id: z.string().nullable().optional(),
cidr: z.string().nullable().optional(),
hostname: z.string().nullable().optional(),
resolved_count: z.number().int().optional(),
comment: z.string().nullable().optional(),
created_at: z.string(),
updated_at: z.string(),
})
export const policySetSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
enabled: z.boolean(),
policy_mode: policyModeSchema,
rules_count: z.number().int().optional(),
agents_count: z.number().int().optional(),
created_at: z.string(),
updated_at: z.string(),
})
export const ipOverrideSchema = z.object({
id: z.string(),
agent_id: z.string(),
cidr: z.string(),
action: policyActionSchema,
comment: z.string().nullable().optional(),
created_at: z.string(),
})
export const createIpListBodySchema = z.object({
name: z.string().min(1),
/** Prefer static | json_url | evobgp_community; domains accepted for legacy. */
type: ipListTypeSchema,
config: z.record(z.string(), z.unknown()).optional(),
entries: z.array(z.string()).optional(),
})
export const createPolicySetBodySchema = z.object({
name: z.string().min(1),
description: z.string().nullable().optional(),
enabled: z.boolean().optional().default(true),
policy_mode: policyModeSchema.optional().default('blacklist'),
})
export const patchPolicySetBodySchema = z.object({
name: z.string().min(1).optional(),
description: z.string().nullable().optional(),
enabled: z.boolean().optional(),
policy_mode: policyModeSchema.optional(),
})
export const createPolicyRuleBodySchema = z
.object({
set_id: z.string().min(1),
priority: z.number().int().min(1).max(10000).optional(),
action: policyActionSchema,
enabled: z.boolean().optional().default(true),
list_id: z.string().nullable().optional(),
cidr: z.string().nullable().optional(),
hostname: z.string().nullable().optional(),
comment: z.string().nullable().optional(),
})
.superRefine((v, ctx) => {
const sources = [v.list_id, v.cidr, v.hostname].filter(
(x) => typeof x === 'string' && x.trim().length > 0,
)
if (sources.length !== 1) {
ctx.addIssue({
code: 'custom',
message: 'Укажите ровно один источник: list_id, cidr или hostname',
})
}
})
export const patchPolicyRuleBodySchema = z.object({
enabled: z.boolean().optional(),
action: policyActionSchema.optional(),
comment: z.string().nullable().optional(),
priority: z.number().int().min(1).max(10000).optional(),
})
export const reorderPolicyRulesBodySchema = z.object({
ordered_ids: z.array(z.string()).min(1),
})
export const putAgentPolicySetsBodySchema = z.object({
set_ids: z.array(z.string()),
})
export const createOverrideBodySchema = z.object({
cidr: z.string().min(1),
action: policyActionSchema,
comment: z.string().nullable().optional(),
})
export const patchAgentBodySchema = z.object({
name: z.string().min(1).optional(),
policy_mode: policyModeSchema.optional(),
settings: z.record(z.string(), z.unknown()).optional(),
})
export const cloneFromBodySchema = z.object({
include_overrides: z.boolean().optional().default(false),
})
export const enrollBodySchema = z.object({
name: z.string().min(1),
hostname: z.string().optional(),
platform: agentPlatformSchema.optional().default('linux'),
token: z.string().min(16),
client_version: z.string().optional(),
install_link_id: z.string().optional(),
})
export const applyReportBodySchema = z.object({
status: z.string(),
prefix_count: z.number().int().optional(),
packets_dropped: z.number().int().optional(),
packets_accepted: z.number().int().optional(),
kernel_method: z.string().optional(),
error: z.string().optional(),
source: z.string().optional(),
})
export const agentPolicySchema = z.object({
generation: z.number().int(),
hash: z.string(),
policy_mode: policyModeSchema,
deny_cidrs: z.array(z.string()),
allow_cidrs: z.array(z.string()),
sync_interval_sec: z.number().int(),
})
export const dashboardStatsSchema = z.object({
agents_total: z.number().int(),
agents_approved: z.number().int(),
agents_online: z.number().int(),
agents_pending: z.number().int(),
packets_dropped: z.number().int(),
packets_accepted: z.number().int(),
lists_total: z.number().int(),
})
export const createInstallLinkBodySchema = z.object({
name: z.string().min(1),
platform: agentPlatformSchema.optional().default('linux'),
})
export const installLinkSchema = z.object({
id: z.string(),
slug: z.string(),
client_name: z.string(),
platform: agentPlatformSchema,
agent_id: z.string().nullable().optional(),
created_at: z.string(),
revoked_at: z.string().nullable().optional(),
last_used_at: z.string().nullable().optional(),
use_count: z.number().int(),
urls: z
.object({
by_id: z.string(),
by_slug: z.string(),
})
.optional(),
curl: z
.object({
by_id: z.string(),
by_slug: z.string(),
})
.optional(),
})
export type Agent = z.infer<typeof agentSchema>
export type IpList = z.infer<typeof ipListSchema>
export type PolicyRule = z.infer<typeof policyRuleSchema>
export type PolicySet = z.infer<typeof policySetSchema>
export type IpOverride = z.infer<typeof ipOverrideSchema>
export type AgentPolicy = z.infer<typeof agentPolicySchema>
export type DashboardStats = z.infer<typeof dashboardStatsSchema>
export type InstallLink = z.infer<typeof installLinkSchema>