Files
EpicNext-Cms/src/app/global-error.tsx
T
Simo 6f15e0c8a3 Production hardening: error pages, rate limiting, metadata
- Custom not-found (404) + error / global-error boundaries, styled with
  the public design system; raw errors logged, never shown to users.
- In-process rate limiter (src/lib/rate-limit.ts) wired into the abuse-
  prone flows: login (10/5min/IP), register (5/10min/IP), password-reset
  request (3/15min/IP), keyed by the proxy-forwarded client IP.
- SEO/metadata: root generateMetadata sets a `%s · {hotel}` title
  template from the live hotel_name; dynamic generateMetadata on
  news/[slug] (article title + excerpt) and u/[username] (name + motto);
  static titles on 12 primary public pages.
- env.ts: added the vars introduced since (PASSWORD_HASH, OPENAI_API_KEY,
  DISCORD_WEBHOOK_URL, ALERT_EMAIL, PAYPAL_*) so env stays authoritative.

Verified on the prod server: /missing → 404 card, news title renders
"News · Habbo". tsc 0, vitest 49/49, next build 0.
2026-06-28 20:12:12 +02:00

61 lines
1.6 KiB
TypeScript

"use client";
import { useEffect } from "react";
/**
* Last-resort boundary for errors thrown in the root layout itself. It replaces
* the whole document, so it must render its own <html>/<body> and can't rely on
* the app shell or its CSS variables.
*/
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<html lang="en">
<body
style={{
margin: 0,
minHeight: "100vh",
display: "grid",
placeItems: "center",
fontFamily: "Nunito, system-ui, sans-serif",
background: "#0f1117",
color: "#e5e7eb",
}}
>
<div style={{ textAlign: "center", padding: "2rem", maxWidth: 420 }}>
<div style={{ fontSize: "2.5rem" }}>⚠️</div>
<h1 style={{ margin: "0.5rem 0", fontSize: "1.4rem" }}>Something went wrong</h1>
<p style={{ color: "#9ca3af", marginTop: 0 }}>
The hotel ran into an unexpected error. Please try again.
</p>
<button
type="button"
onClick={() => reset()}
style={{
marginTop: "1rem",
padding: "0.55rem 1.4rem",
border: "none",
borderRadius: 10,
fontWeight: 700,
cursor: "pointer",
background: "#f59e0b",
color: "#1a1a2e",
}}
>
Try again
</button>
</div>
</body>
</html>
);
}