This commit is contained in:
Marcelo
2025-12-18 20:17:20 +00:00
parent 0e9b2dd72d
commit ffc39a5c90
9 changed files with 1268 additions and 69 deletions

View File

@@ -22,18 +22,25 @@ export async function POST(req: Request) {
const e = body.event;
const rawEvent = body.event;
const e = Array.isArray(rawEvent) ? rawEvent[0] : rawEvent;
const ts =
typeof e?.data?.timestamp === "number"
? new Date(e.data.timestamp)
: undefined;
if (!e || typeof e !== "object") {
return NextResponse.json({ ok: false, error: "Invalid event object" }, { status: 400 });
}
const rawType =
e.eventType ?? e.anomaly_type ?? e.topic ?? body.topic ?? "";
// normalize inputs from event
const sev = String(e.severity ?? "").toLowerCase();
const typ = String(e.eventType ?? e.anomaly_type ?? "").toLowerCase();
const title = String(e.title ?? "").trim();
const normalizeType = (t: string) =>
String(t)
.trim()
.toLowerCase()
.replace(/_/g, "-");
const typ = normalizeType(rawType);
const sev = String(e.severity ?? "").trim().toLowerCase();
// accept these types
const ALLOWED_TYPES = new Set([
"slow-cycle",
"anomaly-detected",
@@ -43,35 +50,58 @@ const ALLOWED_TYPES = new Set([
"microstop",
]);
const ALLOWED_SEVERITIES = new Set(["warning", "critical"]);
// Drop generic/noise
if (!ALLOWED_SEVERITIES.has(sev) || !ALLOWED_TYPES.has(typ)) {
return NextResponse.json({ ok: true, skipped: true }, { status: 200 });
if (!ALLOWED_TYPES.has(typ)) {
return NextResponse.json({ ok: true, skipped: true, reason: "type_not_allowed", typ, sev }, { status: 200 });
}
if (!title) return NextResponse.json({ ok: true, skipped: true }, { status: 200 });
// optional: severity enforcement only for SOME types (not slow-cycle)
const NEEDS_HIGH_SEV = new Set(["down", "scrap-spike"]);
const ALLOWED_SEVERITIES = new Set(["warning", "critical", "error"]);
if (NEEDS_HIGH_SEV.has(typ) && !ALLOWED_SEVERITIES.has(sev)) {
return NextResponse.json({ ok: true, skipped: true, reason: "severity_too_low", typ, sev }, { status: 200 });
}
// timestamp handling (support multiple field names)
const tsMs =
(typeof (e as any)?.timestamp === "number" && (e as any).timestamp) ||
(typeof e?.data?.timestamp === "number" && e.data.timestamp) ||
(typeof e?.data?.event_timestamp === "number" && e.data.event_timestamp) ||
(typeof e?.data?.ts === "number" && e.data.ts) ||
undefined;
const ts = tsMs ? new Date(tsMs) : new Date(); // default to now if missing
const title =
String(e.title ?? "").trim() ||
(typ === "slow-cycle" ? "Slow Cycle Detected" : "Event");
const description = e.description
? String(e.description)
: null;
const row = await prisma.machineEvent.create({
data: {
orgId: machine.orgId,
machineId: machine.id,
ts,
topic: String(e.topic ?? typ),
eventType: typ, // ✅ store normalized type
severity: sev || "info", // ✅ store normalized severity
requiresAck: !!e.requires_ack,
title,
description,
data: e.data ?? e,
workOrderId:
(e as any)?.work_order_id ? String((e as any).work_order_id)
: e?.data?.work_order_id ? String(e.data.work_order_id)
: null,
},
});
return NextResponse.json({ ok: true, id: row.id, ts: row.ts });
const row = await prisma.machineEvent.create({
data: {
orgId: machine.orgId,
machineId: machine.id,
ts: ts ?? undefined,
topic: e.topic ? String(e.topic) : "event",
eventType: e.anomaly_type ? String(e.anomaly_type) : "unknown",
severity: e.severity ? String(e.severity) : "info",
requiresAck: !!e.requires_ack,
title: e.title ? String(e.title) : "Event",
description: e.description ? String(e.description) : null,
data: e.data ?? e, // store full blob
workOrderId: e?.data?.work_order_id ? String(e.data.work_order_id) : null,
},
});
return NextResponse.json({ ok: true, id: row.id, ts: row.ts });
}