- прод-режим отказывается стартовать без AUTH_REQUIRED и реальных секретов (opt-out через EVOFW_ALLOW_UNSAFE) - CORS: whitelist через CORS_ORIGINS вместо origin:true; CSP для раздаваемого SPA - транзакции для setAgentPolicySets, reorderPolicyRules, replaceResolvedForRule, replaceIpListEntries - install-скрипты: Zod-валидация имени ссылки, экранирование $ и контрольных символов в RouterOS-рендере - constant-time сравнение enroll-seed - опциональное шифрование токена EvoBGP в БД (EVOFW_SECRET_KEY, AES-256-GCM) и маскирование per-list api_token в ответах - graceful shutdown (SIGTERM/SIGINT) + тесты
55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import { readFileSync, existsSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
import { buildApp } from './app.js'
|
|
import { loadConfig } from './config.js'
|
|
|
|
for (const path of [
|
|
resolve(import.meta.dirname, '../../../.env'),
|
|
'.env',
|
|
'../.env',
|
|
]) {
|
|
if (!existsSync(path)) continue
|
|
const content = readFileSync(path, 'utf-8')
|
|
for (const line of content.split('\n')) {
|
|
const trimmed = line.trim()
|
|
if (!trimmed || trimmed.startsWith('#')) continue
|
|
const eq = trimmed.indexOf('=')
|
|
if (eq === -1) continue
|
|
const key = trimmed.slice(0, eq).trim()
|
|
let value = trimmed.slice(eq + 1).trim()
|
|
if (
|
|
(value.startsWith('"') && value.endsWith('"')) ||
|
|
(value.startsWith("'") && value.endsWith("'"))
|
|
) {
|
|
value = value.slice(1, -1)
|
|
}
|
|
if (!(key in process.env)) process.env[key] = value
|
|
}
|
|
break
|
|
}
|
|
|
|
const config = loadConfig()
|
|
const app = await buildApp({ config })
|
|
|
|
try {
|
|
await app.listen({ port: config.serverPort, host: '0.0.0.0' })
|
|
app.log.info(`listening on ${config.serverPort}`)
|
|
} catch (err) {
|
|
app.log.error(err)
|
|
process.exit(1)
|
|
}
|
|
|
|
// Drain in-flight requests and close SQLite on docker stop / SIGINT.
|
|
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
|
process.on(signal, () => {
|
|
app.log.info(`${signal} received, shutting down`)
|
|
app
|
|
.close()
|
|
.then(() => process.exit(0))
|
|
.catch((err) => {
|
|
app.log.error(err, 'error during shutdown')
|
|
process.exit(1)
|
|
})
|
|
})
|
|
}
|