feat(backup): добавить таблицу для резервных копий и функции для работы с ними
Docker images / prepare-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 1m35s
Docker images / frontend-image (push) Successful in 1m42s
Docker images / updater-image (push) Successful in 37s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s

This commit is contained in:
Denozordec committed 2026-05-12 21:56:13 +07:00
1 parent 63aa9d424b
commit 9ab2418a5f
6 files changed
+148 -68

No files matched your search

+8 -17
View File
@@ -1,5 +1,5 @@
import { randomUUID } from "node:crypto"
import { readFile, rm } from "node:fs/promises"
import { readFile } from "node:fs/promises"
import path from "node:path"
import { z } from "zod"
import type { FastifyPluginAsyncZod } from "@fastify/type-provider-zod"
@@ -8,12 +8,13 @@ import { listServersRead } from "../modules/servers/service/servers-service.js"
import { appendEvent } from "../modules/events/service/events-service.js"
import { refreshScheduler } from "../services/scheduler.js"
import {
deleteBackupRecord,
getBackupById,
getBackupsDir,
getBackupScheduleSettings,
readBackupIndex,
listBackups,
runBackupForServer,
updateBackupScheduleSettings,
writeBackupIndex,
type BackupMeta,
} from "../services/backup-service.js"
@@ -47,11 +48,9 @@ const BackupJobIdParamSchema = z.object({
async function processBackupJob(job: BackupJob, ids: string[], notes?: string) {
job.status = "running"
job.startedAt = new Date().toISOString()
const indexRows = await readBackupIndex()
for (const id of ids) {
try {
const meta = await runBackupForServer(id, "manual", notes)
indexRows.unshift(meta)
job.created.push(meta)
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
@@ -60,7 +59,6 @@ async function processBackupJob(job: BackupJob, ids: string[], notes?: string) {
job.completed += 1
}
}
await writeBackupIndex(indexRows)
job.status = "done"
job.finishedAt = new Date().toISOString()
appendEvent({
@@ -82,9 +80,7 @@ async function processBackupJob(job: BackupJob, ids: string[], notes?: string) {
const backupsRoutes: FastifyPluginAsyncZod = async (app) => {
app.get("/backups", async (_req, reply) => {
const rows = await readBackupIndex()
rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
return reply.send(rows)
return reply.send(listBackups())
})
app.get("/backups/schedule", async (_req, reply) => {
@@ -171,8 +167,7 @@ const backupsRoutes: FastifyPluginAsyncZod = async (app) => {
})
app.get("/backups/:id/download", { schema: { params: BackupIdParamSchema } }, async (req, reply) => {
const rows = await readBackupIndex()
const hit = rows.find((r) => r.id === req.params.id)
const hit = getBackupById(req.params.id)
if (!hit) return reply.status(404).send({ error: "Бэкап не найден" })
const filePath = path.join(getBackupsDir(), hit.filename)
const content = await readFile(filePath, "utf8").catch(() => null)
@@ -183,12 +178,8 @@ const backupsRoutes: FastifyPluginAsyncZod = async (app) => {
})
app.delete("/backups/:id", { schema: { params: BackupIdParamSchema } }, async (req, reply) => {
const rows = await readBackupIndex()
const idx = rows.findIndex((r) => r.id === req.params.id)
if (idx < 0) return reply.status(404).send({ error: "Бэкап не найден" })
const [hit] = rows.splice(idx, 1)
await writeBackupIndex(rows)
await rm(path.join(getBackupsDir(), hit.filename), { force: true })
const hit = await deleteBackupRecord(req.params.id)
if (!hit) return reply.status(404).send({ error: "Бэкап не найден" })
return reply.status(204).send()
})
}