Files
MIS-Contro-Tower/app/api/overview/route.ts

102 lines
3.3 KiB
TypeScript

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createHash } from "crypto";
import { prisma } from "@/lib/prisma";
import { requireSession } from "@/lib/auth/requireSession";
import { getOverviewData } from "@/lib/overview/getOverviewData";
function toMs(value?: Date | null) {
return value ? value.getTime() : 0;
}
export async function GET(req: NextRequest) {
const session = await requireSession();
if (!session) {
return NextResponse.json({ ok: false, error: "Unauthorized" }, { status: 401 });
}
const url = new URL(req.url);
const eventsMode = url.searchParams.get("events") ?? "critical";
const eventsWindowSecRaw = Number(url.searchParams.get("eventsWindowSec") ?? "21600");
const eventsWindowSec = Number.isFinite(eventsWindowSecRaw) ? eventsWindowSecRaw : 21600;
const eventMachinesRaw = Number(url.searchParams.get("eventMachines") ?? "6");
const eventMachines = Number.isFinite(eventMachinesRaw) ? Math.max(1, eventMachinesRaw) : 6;
const [machineAgg, heartbeatAgg, kpiAgg, eventAgg, orgSettings] = await Promise.all([
prisma.machine.aggregate({
where: { orgId: session.orgId },
_max: { updatedAt: true },
}),
prisma.machineHeartbeat.aggregate({
where: { orgId: session.orgId },
_max: { tsServer: true },
}),
prisma.machineKpiSnapshot.aggregate({
where: { orgId: session.orgId },
_max: { tsServer: true },
}),
prisma.machineEvent.aggregate({
where: { orgId: session.orgId },
_max: { tsServer: true },
}),
prisma.orgSettings.findUnique({
where: { orgId: session.orgId },
select: { updatedAt: true, stoppageMultiplier: true, macroStoppageMultiplier: true },
}),
]);
const lastModifiedMs = Math.max(
toMs(machineAgg._max.updatedAt),
toMs(heartbeatAgg._max.tsServer),
toMs(kpiAgg._max.tsServer),
toMs(eventAgg._max.tsServer),
toMs(orgSettings?.updatedAt)
);
const versionParts = [
session.orgId,
eventsMode,
eventsWindowSec,
eventMachines,
toMs(machineAgg._max.updatedAt),
toMs(heartbeatAgg._max.tsServer),
toMs(kpiAgg._max.tsServer),
toMs(eventAgg._max.tsServer),
toMs(orgSettings?.updatedAt),
];
const etag = `W/"${createHash("sha1").update(versionParts.join("|")).digest("hex")}"`;
const lastModified = new Date(lastModifiedMs || 0).toUTCString();
const responseHeaders = new Headers({
"Cache-Control": "private, no-cache, max-age=0, must-revalidate",
ETag: etag,
"Last-Modified": lastModified,
Vary: "Cookie",
});
const ifNoneMatch = req.headers.get("if-none-match");
if (ifNoneMatch && ifNoneMatch === etag) {
return new NextResponse(null, { status: 304, headers: responseHeaders });
}
const ifModifiedSince = req.headers.get("if-modified-since");
if (!ifNoneMatch && ifModifiedSince) {
const since = Date.parse(ifModifiedSince);
if (!Number.isNaN(since) && lastModifiedMs <= since) {
return new NextResponse(null, { status: 304, headers: responseHeaders });
}
}
const { machines: machineRows, events } = await getOverviewData({
orgId: session.orgId,
eventsMode,
eventsWindowSec,
eventMachines,
orgSettings,
});
return NextResponse.json(
{ ok: true, machines: machineRows, events },
{ headers: responseHeaders }
);
}