- Cash Floats: ledger card (opening balance, top-ups, slips), manual entries, slip photos - Office access level; clock app Slips tab (photograph a slip, popup for supplier/cost code/comments/amount); only Office sees it; per-role route table in lib/clock-access - Job cards: Machine Service PDF (with Oil block), JC numbers, manual Close job, Delete - Hide Expenses > Vehicle Costs and Finance (Bastion) from the menu Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
38 lines
2.4 KiB
TypeScript
38 lines
2.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { clockAuthDestination } from "@/lib/clock-pwa";
|
|
import { clockHome, clockPathAllowed } from "@/lib/clock-access";
|
|
import { sessionFromToken } from "@/lib/auth-service";
|
|
import { SESSION_COOKIE, authDestination } from "@/lib/auth-policy";
|
|
|
|
export async function proxy(request: NextRequest) {
|
|
const path = request.nextUrl.pathname;
|
|
if (path === "/api/health" || path === "/api/auth/logout" || path === "/login" || path === "/clock/login" || path === "/clock.webmanifest") return NextResponse.next();
|
|
let session;
|
|
try {
|
|
session = await sessionFromToken(request.cookies.get(SESSION_COOKIE)?.value || "");
|
|
} catch {
|
|
return path.startsWith("/api/")
|
|
? NextResponse.json({ error: "Account service is temporarily unavailable." }, { status: 503 })
|
|
: new NextResponse("Account service is temporarily unavailable. Please reload to try again.", { status: 503, headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" } });
|
|
}
|
|
const clockPath=path==="/clock"||path.startsWith("/clock/")||path.startsWith("/api/clock/");
|
|
const destination = clockPath?clockAuthDestination(session):authDestination(session);
|
|
// Logout is a server action and remains available during first-login onboarding.
|
|
if ((path === "/change-password" || path === "/clock/change-password") && session) return NextResponse.next();
|
|
if (destination !== (clockPath?"/clock":"/")) {
|
|
if (path.startsWith("/api/")) return NextResponse.json({ error: !session ? "Please sign in." : session.passwordChanged ? "This account only has access to the clock app." : "Change your starter password first." }, { status: session ? 403 : 401 });
|
|
return NextResponse.redirect(new URL(destination, request.url));
|
|
}
|
|
// Restricted roles only reach their own part of the clock app.
|
|
if (clockPath && session && !clockPathAllowed(session.access, path)) {
|
|
if (path.startsWith("/api/")) return NextResponse.json({ error: "Your account cannot use this part of the app." }, { status: 403 });
|
|
return NextResponse.redirect(new URL(clockHome(session.access), request.url));
|
|
}
|
|
const response = NextResponse.next();
|
|
response.headers.set("Cache-Control", "private, no-store");
|
|
return response;
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ["/((?!_next/static|_next/image|brand/|icons/|favicon\\.ico|manifest\\.json|sw\\.js|workbox-[^/]+\\.js|swe-worker-[^/]+\\.js|worker-[^/]+\\.js).*)"],
|
|
};
|