feat(insights): Telegram alerts + daily brief push notifications
apps/worker/src/lib/telegram.ts: - sendTelegram() with Redis-backed dedupe (NX SETEX, 1h TTL) - Helpers: alertP0Insight, alertRegression, alertSanitizationAnomaly, alertBudgetCap Wired into: - analyze.ts: P0/P1 insight creation → instant alert (dedupe per insight_id); budget guard halt → daily cap alert (dedupe per state per day) - validation.ts: regression detected (≥3 sessions w/ same fingerprint after shippedAt) → alert (dedupe per insight_id) - compress-sessions.ts: sanitization anomaly (>500 tokens, 0 PII matches) → alert (dedupe per session_id) — possible PII leak warning Daily Brief (jobs/daily-brief.ts): - Cron @05:00 UTC (= 08:00 Europe/Istanbul) - 24h: sessions/insights/cost/cache-hit + 3 top priorities + 7d shipped/validated/regressed - POST /api/insights/brief/send for manual trigger / smoke test Env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, PANEL_PUBLIC_URL (Coolify both apps). Bot: @Pl24_mitm_bot (AiFactory), chat 7840804807. Source: airflow3 monitoring DAG. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
20
apps/web/src/app/api/insights/brief/send/route.ts
Normal file
20
apps/web/src/app/api/insights/brief/send/route.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { pipelineQueue } from "@/lib/queue";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// Manually enqueue a daily-brief job. Useful for "send me the brief now" + smoke testing.
|
||||
export async function POST() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
if (!session) return NextResponse.json({ ok: false, error: "unauthenticated" }, { status: 401 });
|
||||
|
||||
const queue = pipelineQueue();
|
||||
const job = await queue.add(
|
||||
"daily-brief",
|
||||
{},
|
||||
{ removeOnComplete: 50, removeOnFail: 25 },
|
||||
);
|
||||
return NextResponse.json({ ok: true, jobId: String(job.id ?? "unknown") });
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user