Completes securing the web front end (vrek gol-wqf95dq, iss-r5vrjx7). - /login: Server Action with a generic error, same-site-only redirect back to ?next=, and throttling of failed logins (10 per address and 100 overall per 15 min). The header shows "Signed in as" with Sign out (iss-nj9wmwp). - First run with no admin: instrumentation prints a one-time setup code, shared with the app through globalThis. /setup requires it, and 5 wrong codes rotate it. "Skip for now" runs unsecured for the browser session behind a red warning banner on every page (iss-9nxdndr). - src/proxy.ts: optimistic redirects to /login or /setup, 401 for the API, and the 12 h sliding session refresh. requirePageAccess() and apiAccessDenied() re-check in the page and all 6 route handlers (iss-76d5wrb). - An expired session now shows "Your session has ended" instead of the camera-login form. - README documents in-app setup, skipping and signing in. Verified with unit tests (383, 99.9% line coverage), end to end against `next start`, and manually in a browser by the user. Refreshes the vrek export. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
35 lines
845 B
TypeScript
35 lines
845 B
TypeScript
import type { Metadata } from "next";
|
|
import { Geist, Geist_Mono } from "next/font/google";
|
|
import "./globals.css";
|
|
import Providers from "./providers";
|
|
import SecurityBar from "./security-bar";
|
|
|
|
const geistSans = Geist({
|
|
variable: "--font-geist-sans",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
const geistMono = Geist_Mono({
|
|
variable: "--font-geist-mono",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
export const metadata: Metadata = {
|
|
title: "ONVIF Cameras",
|
|
description: "Discover ONVIF cameras on the local network",
|
|
};
|
|
|
|
export default function RootLayout({ children }: LayoutProps<"/">) {
|
|
return (
|
|
<html
|
|
lang="en"
|
|
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
|
>
|
|
<body className="min-h-full flex flex-col">
|
|
<SecurityBar />
|
|
<Providers>{children}</Providers>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|