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
При старте backend накатывает схему PostgreSQL 18 и, если база пустая, один раз импортирует mikrotik.db с тома. Повторный старт не копирует данные. Бэкап в UI идёт через pg_dump. Co-authored-by: Cursor <[email protected]>
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import type { FastifyPluginAsyncZod } from "@fastify/type-provider-zod"
|
|
import { refreshScheduler } from "../services/scheduler.js"
|
|
import {
|
|
getInternetPathSettings,
|
|
getLatestInternetPathSnapshot,
|
|
updateInternetPathSettings,
|
|
} from "../services/internet-path-collector.js"
|
|
|
|
const internetPathRoutes: FastifyPluginAsyncZod = async (app) => {
|
|
app.get("/internet-path/settings", async (_req, reply) => {
|
|
const s = await getInternetPathSettings()
|
|
return reply.send({
|
|
enabled: s.enabled,
|
|
intervalSec: s.intervalSec,
|
|
retentionDays: s.retentionDays,
|
|
lastCollectedAt: s.lastCollectedAt ?? null,
|
|
lastDurationMs: s.lastDurationMs ?? null,
|
|
lastError: s.lastError || null,
|
|
})
|
|
})
|
|
|
|
app.put("/internet-path/settings", async (req, reply) => {
|
|
const body = req.body as {
|
|
enabled?: boolean
|
|
intervalSec?: number | string
|
|
retentionDays?: number | string
|
|
}
|
|
const updated = await updateInternetPathSettings({
|
|
enabled: body.enabled,
|
|
intervalSec: body.intervalSec == null ? undefined : Math.max(30, Number.parseInt(String(body.intervalSec), 10) || 300),
|
|
retentionDays: body.retentionDays == null ? undefined : Math.max(1, Number.parseInt(String(body.retentionDays), 10) || 14),
|
|
})
|
|
await refreshScheduler()
|
|
return reply.send({
|
|
ok: true,
|
|
settings: {
|
|
enabled: updated.enabled,
|
|
intervalSec: updated.intervalSec,
|
|
retentionDays: updated.retentionDays,
|
|
lastCollectedAt: updated.lastCollectedAt ?? null,
|
|
lastDurationMs: updated.lastDurationMs ?? null,
|
|
lastError: updated.lastError || null,
|
|
},
|
|
})
|
|
})
|
|
|
|
app.get("/internet-path/latest", async (_req, reply) => {
|
|
const row = await getLatestInternetPathSnapshot()
|
|
if (!row) return reply.send({ snapshot: null })
|
|
let payload: unknown = row.payloadJson
|
|
if (typeof row.payloadJson === "string") {
|
|
try {
|
|
payload = JSON.parse(row.payloadJson)
|
|
} catch {
|
|
payload = null
|
|
}
|
|
}
|
|
return reply.send({
|
|
snapshot: payload,
|
|
sampledAt: row.sampledAt,
|
|
})
|
|
})
|
|
}
|
|
|
|
export default internetPathRoutes
|