import {z} from "zod" import {normalizeAlertsConfig,alertsResponseSchema,alertsRulesReceiptSchema,telegramSettingsReceiptSchema,telegramTestReceiptSchema,putTelegramSchema,testTelegramSchema,type PutRulesBody,type PutTelegramBody,type TestTelegramBody} from "@mmapp/contracts/alerts" import {requestJson} from "./http-client" type Options={signal?:AbortSignal;expectedRevision?:string} const signal=(options?:Options)=>{const active=options?.signal?AbortSignal.any([options.signal,AbortSignal.timeout(30_000)]):AbortSignal.timeout(30_000);active.throwIfAborted();return active} function parse(schema:S,raw:unknown,message:string):z.output{const result=schema.safeParse(raw);if(!result.success)throw Error(message);return result.data} export async function getAlerts(baseUrl:string,options?:Options){return parse(alertsResponseSchema,await requestJson(baseUrl,"/api/alerts",{signal:signal(options),cache:"no-store"}),"Данные оповещений не подтверждены сервером")} export async function saveAlertRules(baseUrl:string,payload:PutRulesBody,options?:Options){ const body=normalizeAlertsConfig(payload) if(!options?.expectedRevision||!/^[a-f0-9]{64}$/.test(options.expectedRevision))throw Error("Обновите снимок правил перед сохранением") const receipt=parse(alertsRulesReceiptSchema,await requestJson(baseUrl,"/api/alerts/rules",{method:"PUT",headers:{"If-Match":'"'+options.expectedRevision+'"'},body:JSON.stringify(body),signal:signal(options)}),"Сохранение правил не подтверждено сервером") const same=(a:unknown,b:unknown)=>JSON.stringify(a)===JSON.stringify(b) if(receipt.rules.length!==body.rules.length||body.rules.some(rule=>{ const saved=receipt.rules.find(row=>row.id===rule.id) if(!saved)return true const targets=(rule.targets?.length?rule.targets:[rule.target??""]).map(value=>value.trim()).filter(Boolean) const conditions=(rule.conditions?.length?rule.conditions:[rule.condition??""]).map(value=>value.trim()).filter(Boolean) const expected={name:rule.name,type:rule.type,targets,conditions,groupId:rule.groupId?.trim()||null,severity:rule.severity,enabled:rule.enabled,cooldown:rule.cooldown,confirmStabilitySec:rule.confirmStabilitySec||null,recoveryMode:rule.recoveryMode??"always",recoveryStabilitySec:rule.recoveryStabilitySec||null,chatId:rule.chatId} return Object.entries(expected).some(([key,value])=>!same(saved[key as keyof typeof saved],value)) })||body.groups!==undefined&&(receipt.groups.length!==body.groups.length||body.groups.some(group=>{const saved=receipt.groups.find(row=>row.id===group.id);return !saved||Object.entries({...group,name:group.name.trim()||group.id}).some(([key,value])=>!same(saved[key as keyof typeof saved],value))})))throw Error("Сохранённые правила или группы не совпадают с отправленными") return receipt } export async function saveAlertsTelegram(baseUrl:string,payload:PutTelegramBody,options?:Options){ const body=parse(putTelegramSchema,payload,"Проверьте параметры Telegram") if(Object.keys(body).length===0)throw Error("Нет изменённых параметров Telegram") const receipt=parse(telegramSettingsReceiptSchema,await requestJson(baseUrl,"/api/alerts/telegram",{method:"PUT",body:JSON.stringify(body),signal:signal(options)}),"Сохранение параметров Telegram не подтверждено сервером") if(body.chatId!==undefined&&receipt.telegram.chatId!==body.chatId.trim()||body.messageThreadId!==undefined&&receipt.telegram.messageThreadId!==body.messageThreadId||body.token!==undefined&&receipt.telegram.tokenConfigured!==Boolean(body.token?.trim()))throw Error("Сохранённые параметры Telegram не совпадают с отправленными") return receipt } export async function testAlertsTelegram(baseUrl:string,payload:TestTelegramBody={},options?:Options){ const body=parse(testTelegramSchema,payload,"Проверьте параметры тестового сообщения Telegram") return parse(telegramTestReceiptSchema,await requestJson(baseUrl,"/api/alerts/telegram/test",{method:"POST",body:JSON.stringify(body),signal:signal(options)}),"Отправка тестового сообщения не подтверждена сервером") }