A slim Staging bar on staging
Shown above the header when the site is served from a staging. address, or with HUB_ENVIRONMENT=staging (production hides it), read per request since staging and production run the same image. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
975e11e077
commit
21e1bd8790
3 files changed
+24
No files matched your search
@@ -49,4 +49,7 @@ Needs Bun (1.4) and Node (for Next.js).
|
|||||||
that runs Bun or containers (Railway, Render, Fly.io, Heroku), with `DATABASE_URL`, `AUTH_SECRET`, `API_URL` (its
|
that runs Bun or containers (Railway, Render, Fly.io, Heroku), with `DATABASE_URL`, `AUTH_SECRET`, `API_URL` (its
|
||||||
public address) and `WEB_URL` (the website's).
|
public address) and `WEB_URL` (the website's).
|
||||||
|
|
||||||
|
A staging site shows a slim "Staging" bar: when it's served from a `staging.` address (`hub.staging.t5de.dev`),
|
||||||
|
or with the website's `HUB_ENVIRONMENT=staging` (`production` hides it).
|
||||||
|
|
||||||
T5DE's proxies reach the API's address directly (sign-in and the WebSocket): `t5de-proxy signin -hub <API address>`.
|
T5DE's proxies reach the API's address directly (sign-in and the WebSocket): `t5de-proxy signin -hub <API address>`.
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import { Header } from "@/components/header";
|
import { Header } from "@/components/header";
|
||||||
|
import { StagingBanner } from "@/components/staging-banner";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
return (
|
return (
|
||||||
<html lang="en" className={cn("font-sans antialiased", sans.variable, mono.variable)}>
|
<html lang="en" className={cn("font-sans antialiased", sans.variable, mono.variable)}>
|
||||||
<body className="flex min-h-dvh flex-col">
|
<body className="flex min-h-dvh flex-col">
|
||||||
|
<StagingBanner />
|
||||||
<Header />
|
<Header />
|
||||||
<main className="mx-auto w-full max-w-5xl flex-1 px-4 py-10 sm:px-6 sm:py-14">{children}</main>
|
<main className="mx-auto w-full max-w-5xl flex-1 px-4 py-10 sm:px-6 sm:py-14">{children}</main>
|
||||||
<footer className="border-t">
|
<footer className="border-t">
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { headers } from "next/headers";
|
||||||
|
|
||||||
|
// A slim bar on staging, so testers don't mistake it for T5DE's hub: shown when HUB_ENVIRONMENT is "staging", or
|
||||||
|
// (unset) when the site is served from a staging address, such as hub.staging.t5de.dev. Read per request, since
|
||||||
|
// staging and production run the same image.
|
||||||
|
export async function StagingBanner() {
|
||||||
|
const env = process.env.HUB_ENVIRONMENT;
|
||||||
|
const host = (await headers()).get("host") ?? "";
|
||||||
|
const staging = env ? env === "staging" : /(^|\.)staging\./.test(host);
|
||||||
|
if (!staging) return null;
|
||||||
|
return (
|
||||||
|
<div className="bg-amber-400 py-1 text-center text-xs font-medium text-amber-950">
|
||||||
|
Staging ·{" "}
|
||||||
|
<a href="https://hub.t5de.dev" className="underline underline-offset-2">
|
||||||
|
hub.t5de.dev
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user