1bc2de655a
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
30 lines
1.3 KiB
TypeScript
30 lines
1.3 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { headers } from "next/headers";
|
|
import { notFound } from "next/navigation";
|
|
|
|
/* The operations console lives under /ops and is served only on ops.threadcount.tech.
|
|
*
|
|
* proxy.ts already refuses /ops on every other hostname, but the proxy is not the only guard: a
|
|
* matcher change can silently remove its coverage, so the host is checked here again, from the
|
|
* request headers, before anything under this layout renders. Same comparison as the proxy —
|
|
* exact, lower-cased, port-stripped, never `includes`.
|
|
*
|
|
* Every page under here is dynamic. ISR is not keyed by host, so a cached ops page could be
|
|
* served on threadcount.tech; `force-dynamic` on the layout keeps the whole subtree out of the
|
|
* prerender cache. */
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export const metadata: Metadata = {
|
|
title: { default: "ThreadCount ops", template: "%s — ThreadCount ops" },
|
|
robots: { index: false, follow: false },
|
|
};
|
|
|
|
const OPS_HOST = "ops.threadcount.tech";
|
|
|
|
export default async function OpsLayout({ children }: { children: React.ReactNode }) {
|
|
const h = await headers();
|
|
const host = (h.get("x-forwarded-host") ?? h.get("host") ?? "").split(":")[0].toLowerCase();
|
|
if (host !== OPS_HOST) notFound();
|
|
return <div className="tc-ops" role="main">{children}</div>;
|
|
}
|