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.
This commit is contained in:
1 parent
e9ea19795a
commit
6f15e0c8a3
23 files changed
+276
-5
No files matched your search
@@ -6,6 +6,7 @@ import { LaravelEncrypter } from "@/lib/auth/laravel-encrypter";
|
||||
import { checkLogin } from "@/lib/auth/password";
|
||||
import { verifyTotp } from "@/lib/auth/totp";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
||||
import { env } from "@/env";
|
||||
|
||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
@@ -24,6 +25,9 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
const password = String(credentials?.password ?? "");
|
||||
if (!username || !password) return null;
|
||||
|
||||
// Throttle login attempts per IP (10 per 5 min) against credential stuffing.
|
||||
if (!rateLimit(`login:${await clientIp()}`, 10, 5 * 60_000).ok) return null;
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { username } });
|
||||
if (!user) return null;
|
||||
|
||||
|
||||
Reference in new issue
Block a user