feat: реализовать EvoFirewall V1 control plane
Build and Push EvoFirewall Docker Image / build-and-push (push) Failing after 25s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

API, UI, Linux/MikroTik agents, IP lists, политики, stats, CI и интеграция с auth-portal/EvoBGP.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-20 19:50:54 +07:00
co-authored by Cursor
parent d71b45d86f
commit ebadf70e2b
107 changed files with 15196 additions and 99 deletions
+57
View File
@@ -0,0 +1,57 @@
export type PermissionAction = 'read' | 'write' | 'admin'
/** Hierarchy: admin ⊃ write ⊃ read within the same section. */
export function hasPermission(
granted: readonly string[],
required: string,
): boolean {
if (granted.includes(required)) return true
const parts = required.split(':')
if (parts.length !== 3) return false
const [app, section, action] = parts
if (action === 'read') {
return (
granted.includes(`${app}:${section}:write`) ||
granted.includes(`${app}:${section}:admin`)
)
}
if (action === 'write') {
return granted.includes(`${app}:${section}:admin`)
}
return false
}
export function permissionForRequest(
method: string,
url: string,
): string | null {
const path = url.split('?')[0] ?? url
const m = method.toUpperCase()
const write = m !== 'GET' && m !== 'HEAD' && m !== 'OPTIONS'
if (path.startsWith('/api/v1/agents')) {
return write ? 'fw:agents:write' : 'fw:agents:read'
}
if (path.startsWith('/api/v1/lists')) {
return write ? 'fw:lists:write' : 'fw:lists:read'
}
if (path.startsWith('/api/v1/rules') || path.startsWith('/api/v1/policies')) {
return write ? 'fw:policies:write' : 'fw:policies:read'
}
if (path.startsWith('/api/v1/stats') || path.startsWith('/api/v1/dashboard')) {
return 'fw:stats:read'
}
if (path.startsWith('/api/v1/settings') || path.startsWith('/api/v1/install-context')) {
return write ? 'fw:settings:admin' : 'fw:settings:read'
}
return null
}
export type AuthUser = {
id: string
email: string
name: string
apps: string[]
permissions: string[]
isAdmin: boolean
}