Files
MikrotikManager/packages/contracts/src/logs.ts
T
Denozordec 5efd0339fb
Docker images / prepare-release (push) Successful in 9s
Docker images / backend-test (push) Successful in 4m35s
Docker images / frontend-image (push) Successful in 3m29s
Docker images / updater-image (push) Successful in 41s
Docker images / netflow-agent-image (push) Successful in 34s
Docker images / backend-image (push) Successful in 1m44s
Docker images / publish-release (push) Successful in 10s
Docker images / notify-webhook (push) Successful in 2s
feat(logs): добавить раздел логов с историей и живым потоком
2026-10-16 22:13:48 +07:00

41 lines
3.0 KiB
TypeScript

import { z } from "zod"
export const logLevels = ["critical", "error", "warning", "info", "debug", "unknown"] as const
export const logLevelSchema = z.enum(logLevels)
export type LogLevel = z.infer<typeof logLevelSchema>
const id = z.string().regex(/^\d+$/)
const instant = z.string().datetime({ offset: true })
export const logEntrySchema = z.object({
id, serverId: z.number().int(), serverName: z.string(), host: z.string(),
routerId: z.string(), rawTime: z.string(), eventAt: instant.nullable(), receivedAt: instant,
effectiveAt: instant, timeKind: z.enum(["router", "receipt"]), topics: z.array(z.string()),
level: logLevelSchema, message: z.string(), generation: z.number().int(),
})
export type LogEntry = z.infer<typeof logEntrySchema>
export const logFiltersSchema = z.object({
serverId: z.coerce.number().int().positive().optional(), from: instant.default(() => new Date(Date.now() - 900000).toISOString()), to: instant.optional(),
levels: z.string().max(100).default("").refine(v => !v || v.split(",").every(x => (logLevels as readonly string[]).includes(x))),
topics: z.string().max(500).default(""), q: z.string().max(500).default(""),
preset: z.enum(["", "login-failure", "interfaces", "routing", "firewall"]).default(""),
}).refine(v => !v.from || !v.to || Date.parse(v.from) <= Date.parse(v.to), "Некорректный период")
export type LogFilters = z.infer<typeof logFiltersSchema>
export const logPageQuerySchema = logFiltersSchema.safeExtend({
cursor: z.string().max(200).optional(), watermark: id.optional(),
limit: z.coerce.number().int().min(1).max(200).default(200),
})
export const logStateSchema = z.object({ serverId: z.number().int(), serverName: z.string(), host: z.string(),
lastCollectedAt: instant.nullable(), lastError: z.string().nullable(), gapAt: instant.nullable(),
nextPollAt: instant.nullable(), failures: z.number().int(), generation: z.number().int() })
export const logPageSchema = z.object({ entries: z.array(logEntrySchema), nextCursor: z.string().nullable(), watermark: id })
export type LogPage = z.infer<typeof logPageSchema>
export const logSettingsSchema = z.object({ enabled: z.boolean(), intervalSec: z.number().int().min(5).max(3600), retentionDays: z.number().int().min(1).max(365) })
export type LogSettings = z.infer<typeof logSettingsSchema>
export const logSettingsPatchSchema = logSettingsSchema.partial().strict().refine(v => Object.keys(v).length > 0)
export const logSummarySchema = z.object({ total: z.number(), levels: z.record(logLevelSchema, z.number()),
buckets: z.array(z.object({ at: instant, critical: z.number(), error: z.number(), warning: z.number(), info: z.number(), debug: z.number(), unknown: z.number() })),
settings: logSettingsSchema, states: z.array(logStateSchema) })
export type LogSummary = z.infer<typeof logSummarySchema>
export const logStreamSchema = z.object({ entries: z.array(logEntrySchema), cursor: id, state: logStateSchema.nullable(), enabled: z.boolean() })
export type LogStream = z.infer<typeof logStreamSchema>
export type LogStreamReset = { reason: "database-maintenance" }