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
+96
View File
@@ -0,0 +1,96 @@
import { resolve } from 'node:path'
import Fastify from 'fastify'
import {
serializerCompiler,
validatorCompiler,
type ZodTypeProvider,
} from '@fastify/type-provider-zod'
import { AsyncTask, CronJob } from 'toad-scheduler'
import type { AppConfig } from './config.js'
import { loadConfig } from './config.js'
import authPlugin from './plugins/auth.js'
import corsPlugin from './plugins/cors.js'
import dbPlugin from './plugins/db.js'
import errorHandlerPlugin from './plugins/error-handler.js'
import { healthRoutes } from './routes/health.js'
import { controlRoutes } from './routes/control.js'
import { agentRoutes } from './routes/agent.js'
import { refreshAllLists } from './services/lists/refresh.js'
import { repos } from '@evofw/db'
export interface BuildAppOptions {
config?: AppConfig
memory?: boolean
}
export async function buildApp(opts: BuildAppOptions = {}) {
const config = opts.config ?? loadConfig()
const app = Fastify({
logger: { level: config.logLevel },
}).withTypeProvider<ZodTypeProvider>()
app.setValidatorCompiler(validatorCompiler)
app.setSerializerCompiler(serializerCompiler)
await app.register(import('@fastify/sensible'))
await app.register(import('@fastify/helmet'), {
contentSecurityPolicy: false,
})
await app.register(import('@fastify/rate-limit'), {
max: 300,
timeWindow: '1 minute',
})
await app.register(corsPlugin)
await app.register(errorHandlerPlugin)
await app.register(dbPlugin, { config, memory: opts.memory })
await app.register(authPlugin, { config })
// Seed enroll_seed into settings if empty
if (!repos.getSetting(app.db, 'enroll_seed')) {
repos.setSetting(app.db, 'enroll_seed', config.enrollSeed)
}
await app.register(healthRoutes)
await app.register(agentRoutes, { config })
await app.register(
async (protectedApi) => {
protectedApi.addHook('onRequest', app.requireAuth)
await protectedApi.register(controlRoutes, { config })
},
{ prefix: '/api/v1' },
)
const staticDir = config.staticDir ?? resolve(process.cwd(), 'static')
if (config.staticDir !== null) {
await app.register(import('@fastify/static'), {
root: staticDir,
wildcard: false,
})
app.setNotFoundHandler(async (_request, reply) => {
return reply.sendFile('index.html')
})
}
if (!opts.memory) {
await app.register(import('@fastify/schedule'))
const task = new AsyncTask(
'list-refresh',
async () => {
await refreshAllLists(app.db)
app.log.info('list refresh completed')
},
(err) => {
app.log.warn({ err }, 'list refresh failed')
},
)
app.scheduler.addCronJob(
new CronJob({ cronExpression: '0 */5 * * * *' }, task, {
preventOverrun: true,
}),
)
}
return app
}