Files
MikrotikManager/backend/src/routes/system-database.ts
T
DenozordecandCursor ec43591a99
Docker images / prepare-release (push) Successful in 12s
Docker images / backend-test (push) Successful in 4m9s
Docker images / frontend-image (push) Successful in 4m28s
Docker images / updater-image (push) Successful in 58s
Docker images / backend-image (push) Successful in 2m49s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 11s
feat(db): перевести хранилище с SQLite на PostgreSQL
При старте backend накатывает схему PostgreSQL 18 и, если база пустая, один раз импортирует mikrotik.db с тома. Повторный старт не копирует данные. Бэкап в UI идёт через pg_dump.

Co-authored-by: Cursor <[email protected]>
2026-09-08 01:36:48 +07:00

74 lines
2.5 KiB
TypeScript

import type { FastifyPluginAsync } from "fastify"
import { appendEvent } from "../modules/events/service/events-service.js"
import {
exportSystemDatabaseBackup,
getSystemDatabasePath,
restoreSystemDatabaseBackup,
} from "../services/system-database-backup.js"
const systemDatabaseRoutes: FastifyPluginAsync = 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 } = await exportSystemDatabaseBackup()
await appendEvent({
level: "info",
eventType: "system.database.backup",
sourceModule: "system",
title: "Создан бэкап базы приложения",
message: filename,
entityType: "system_database",
entityId: filename,
payload: {
filename,
sizeBytes: buffer.length,
databasePath: getSystemDatabasePath(),
},
})
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(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 {
await restoreSystemDatabaseBackup(body)
await appendEvent({
level: "warning",
eventType: "system.database.restore",
sourceModule: "system",
title: "Восстановлена база приложения",
message: "Данные PostgreSQL заменены из загруженного дампа",
entityType: "system_database",
entityId: "restore",
payload: {
sizeBytes: body.length,
databasePath: getSystemDatabasePath(),
},
})
return reply.send({ ok: true })
} 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 systemDatabaseRoutes