import type { FastifyPluginAsync } from "fastify" import { appendEvent } from "../modules/events/service/events-service.js" import { exportSystemDatabaseBackup, restoreSystemDatabaseBackup, } from "../services/system-database-backup.js" interface Dependencies { exportBackup: typeof exportSystemDatabaseBackup restoreBackup: typeof restoreSystemDatabaseBackup event: typeof appendEvent } export function createSystemDatabaseRoutes(deps: Dependencies): FastifyPluginAsync { return async (app) => { app.addContentTypeParser( "application/octet-stream", { parseAs: "buffer", bodyLimit: 512 * 1024 * 1024 }, (_req, body, done) => { done(null, body) }, ) app.get("/system/database/backup", async (_req, reply) => { try { const { filename, buffer, ...warnings } = await deps.exportBackup() let eventError: string | undefined try { await deps.event({ level: "info", eventType: "system.database.backup", sourceModule: "system", title: "Создан бэкап базы приложения", message: filename, entityType: "system_database", entityId: filename, payload: { filename, sizeBytes: buffer.length, }, }) } catch { eventError = "Не удалось записать событие резервного копирования" } const outcome = { ...warnings, ...(eventError ? { eventError } : {}) } reply.header("X-System-Database-Outcome", encodeURIComponent(JSON.stringify(outcome))) reply.header("Content-Type", "application/octet-stream") reply.header("Content-Disposition", `attachment; filename="${filename}"`) return reply.send(buffer) } catch (err) { const message = err instanceof Error ? err.message : String(err) return reply.status(message.includes("уже выполняется") ? 409 : 500).send({ error: message }) } }) app.post("/system/database/restore", async (req, reply) => { const body = req.body if (!Buffer.isBuffer(body) || body.length === 0) { return reply.status(400).send({ error: "Ожидается тело запроса с файлом pg_dump (custom, PGDMP)" }) } try { const outcome = await deps.restoreBackup(body) let eventError: string | undefined try { await deps.event({ level: "warning", eventType: "system.database.restore", sourceModule: "system", title: "Восстановлена база приложения", message: "Данные PostgreSQL заменены из загруженного дампа", entityType: "system_database", entityId: "restore", payload: { sizeBytes: body.length, }, }) } catch { eventError = "Не удалось записать событие восстановления" } return reply.send({ ok: true, ...outcome, ...(eventError ? { eventError } : {}) }) } catch (err) { const message = err instanceof Error ? err.message : String(err) const status = message.includes("уже выполняется") ? 409 : 400 return reply.status(status).send({ error: message }) } }) } } export default createSystemDatabaseRoutes({ exportBackup: exportSystemDatabaseBackup, restoreBackup: restoreSystemDatabaseBackup, event: appendEvent })