feat(sase): deploy regression Telegram alert
Closes the alert side of the deploy-regression view added in Faz 2b.
The dashboard table already flagged regressed deploys; this commit
pushes a Telegram when one happens, so MTTD doesn't depend on the
founder checking the dashboard.
Detection (panel)
- detectVinRegressions() pulls the last 20 Coolify deploys for the
Sase.tr app, filters to those whose post-window has elapsed (≥30min
since finishedAt) and isn't too old (≤180min since finishedAt), and
reuses analyzeDeployRegressions to compute the 30min before/after
success-rate slices. A row is flagged when:
- both before and after have ≥5 samples, and
- success rate dropped ≥10pp (severity 'high'; ≥15pp → 'critical').
- Returns a RegressionHit per flagged deploy with a 24h dedupe TTL
keyed on deploymentUuid so each deploy alerts exactly once ever
(regardless of how often the 5-min cron checks).
Endpoint
- GET /api/internal/vin-anomaly-check now returns
{ ok, current, baseline, anomalies, regressions }.
Worker
- sendTelegram() accepts an optional dedupeTtlSeconds override so
per-call long-TTL dedupes (like deploy alerts) don't have to go
through the global env default.
- New alertVinRegression() formats severity icon + before/after %
+ deploy commit/timestamp + dashboard link.
- runVinAnomalyDetect now also walks the regressions array and
fires Telegram for each. Returns { anomalies, regressions,
alertsFired, alertsDeduped }; pipeline log prints when either
count is non-zero.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { timingSafeEqual } from "node:crypto";
|
||||
import { detectVinAnomalies } from "@/lib/sase/vin-anomaly";
|
||||
import { detectVinAnomalies, detectVinRegressions } from "@/lib/sase/vin-anomaly";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -20,6 +20,9 @@ export async function GET(req: Request) {
|
||||
if (!validToken(req)) {
|
||||
return NextResponse.json({ ok: false, error: "unauthorized" }, { status: 401 });
|
||||
}
|
||||
const result = await detectVinAnomalies();
|
||||
return NextResponse.json({ ok: true, ...result });
|
||||
const [anomalyResult, regressions] = await Promise.all([
|
||||
detectVinAnomalies(),
|
||||
detectVinRegressions(),
|
||||
]);
|
||||
return NextResponse.json({ ok: true, ...anomalyResult, regressions });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user