A slim Staging bar on staging
CI / test (push) Successful in 57s
CI / image (apps/web/Dockerfile, t5de-hub-web) (push) Successful in 5s
CI / image (apps/api/Dockerfile, t5de-hub-api) (push) Successful in 5s

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:
dhkatzandClaude Opus 5.5 committed 2026-11-02 09:09:22 -08:00
1 parent 975e11e077
commit 21e1bd8790
3 files changed
+24

No files matched your search

+3
View File
@@ -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
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>`.
+2
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Header } from "@/components/header";
import { StagingBanner } from "@/components/staging-banner";
import { cn } from "@/lib/utils";
import "./globals.css";
@@ -16,6 +17,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<html lang="en" className={cn("font-sans antialiased", sans.variable, mono.variable)}>
<body className="flex min-h-dvh flex-col">
<StagingBanner />
<Header />
<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">
+19
View File
@@ -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>
);
}