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]>
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
import type { FastifyPluginAsyncZod } from "@fastify/type-provider-zod"
|
|
import {
|
|
getAlertsMeta,
|
|
getTelegramBotToken,
|
|
getTelegramMessageThreadIdForApi,
|
|
getTelegramPublic,
|
|
listAlertGroups,
|
|
listAlertRules,
|
|
listMergedHistory,
|
|
replaceAlertsConfig,
|
|
formatAlertRuleTestTelegramText,
|
|
sendTelegramAlertMessage,
|
|
updateTelegramSettings,
|
|
} from "../services/alerts-service.js"
|
|
import {
|
|
putRulesSchema,
|
|
putTelegramSchema,
|
|
testTelegramSchema,
|
|
} from "@mmapp/contracts/alerts"
|
|
|
|
const alertsRoutes: FastifyPluginAsyncZod = async (app) => {
|
|
app.get("/alerts", async (_req, reply) => {
|
|
const tg = await getTelegramPublic()
|
|
const connected = tg.tokenConfigured && Boolean(tg.chatId?.trim())
|
|
return reply.send({
|
|
telegram: { ...tg, connected },
|
|
groups: await listAlertGroups(),
|
|
rules: await listAlertRules(),
|
|
history: await listMergedHistory(),
|
|
meta: await getAlertsMeta(),
|
|
})
|
|
})
|
|
|
|
app.put("/alerts/rules", async (req, reply) => {
|
|
const parsed = putRulesSchema.safeParse(req.body)
|
|
if (!parsed.success) {
|
|
return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
|
|
}
|
|
const groups =
|
|
parsed.data.groups ??
|
|
(await listAlertGroups()).map((g) => ({
|
|
id: g.id,
|
|
name: g.name,
|
|
combineMode: g.combineMode,
|
|
enabled: g.enabled,
|
|
cooldownOverride: g.cooldownOverride,
|
|
}))
|
|
const rules = parsed.data.rules.map((r) => ({
|
|
id: r.id,
|
|
name: r.name,
|
|
type: r.type,
|
|
target: r.target,
|
|
targets: r.targets,
|
|
groupId: r.groupId ?? null,
|
|
condition: r.condition,
|
|
conditions:
|
|
r.conditions && r.conditions.length > 0
|
|
? r.conditions.map((c) => c.trim()).filter(Boolean)
|
|
: r.condition?.trim()
|
|
? [r.condition.trim()]
|
|
: [],
|
|
severity: r.severity,
|
|
enabled: r.enabled,
|
|
cooldown: r.cooldown,
|
|
confirmStabilitySec: r.confirmStabilitySec ?? null,
|
|
recoveryMode: r.recoveryMode ?? "always",
|
|
recoveryStabilitySec: r.recoveryStabilitySec ?? null,
|
|
chatId: r.chatId,
|
|
}))
|
|
await replaceAlertsConfig({ groups, rules })
|
|
return reply.send({ ok: true, groups: await listAlertGroups(), rules: await listAlertRules() })
|
|
})
|
|
|
|
app.put("/alerts/telegram", async (req, reply) => {
|
|
const parsed = putTelegramSchema.safeParse(req.body)
|
|
if (!parsed.success) {
|
|
return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
|
|
}
|
|
const next = await updateTelegramSettings({
|
|
token: parsed.data.token,
|
|
chatId: parsed.data.chatId,
|
|
messageThreadId: parsed.data.messageThreadId,
|
|
})
|
|
const connected = next.tokenConfigured && Boolean(next.chatId?.trim())
|
|
return reply.send({ ok: true, telegram: { ...next, connected } })
|
|
})
|
|
|
|
app.post("/alerts/telegram/test", async (req, reply) => {
|
|
const parsed = testTelegramSchema.safeParse(req.body ?? {})
|
|
if (!parsed.success) {
|
|
return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
|
|
}
|
|
const fromDb = await getTelegramBotToken()
|
|
const token = (parsed.data.token?.trim() || fromDb).trim()
|
|
const pub = await getTelegramPublic()
|
|
const chatId = (parsed.data.chatId?.trim() || pub.chatId || "").trim()
|
|
if (!token) {
|
|
return reply.status(400).send({ error: "Не задан Bot Token (сохраните в БД или передайте в запросе)" })
|
|
}
|
|
if (!chatId) {
|
|
return reply.status(400).send({ error: "Не задан Chat ID" })
|
|
}
|
|
const threadId =
|
|
parsed.data.messageThreadId != null && parsed.data.messageThreadId >= 1
|
|
? parsed.data.messageThreadId
|
|
: await getTelegramMessageThreadIdForApi()
|
|
const text = parsed.data.rulePreview
|
|
? formatAlertRuleTestTelegramText(parsed.data.rulePreview)
|
|
: "MikroTik Manager — тест оповещений (Telegram)."
|
|
const send = await sendTelegramAlertMessage({
|
|
text,
|
|
token: parsed.data.token?.trim() || undefined,
|
|
chatId,
|
|
messageThreadId: threadId,
|
|
})
|
|
if (!send.ok) {
|
|
return reply.status(502).send({ ok: false, error: send.error })
|
|
}
|
|
return reply.send({ ok: true })
|
|
})
|
|
}
|
|
|
|
export default alertsRoutes
|