feat: реализовать EvoFirewall V1 control plane
API, UI, Linux/MikroTik agents, IP lists, политики, stats, CI и интеграция с auth-portal/EvoBGP. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user