Rate-limiting on messages.send, reports.create, reactions.toggle, follows.create, reposts.create. Unique constraints on Report to stop report-flooding. Mux webhook now fails hard in production without a signing secret instead of skipping verification. Admin-panel outline buttons fixed on 4 more pages (same white-on-dark bug as the Users page). Timing-safe comparison for ADMIN_SECRET/CRON_SECRET. Fixed duplicate HSTS header (Cloudflare's stronger one was being shadowed by the app's own). tRPC now returns clean JSON 401s instead of HTML redirects when unauthenticated, and no longer blocks public procedures for logged-out visitors. 14 previously-silent catch blocks now log to GlitchTip. invite/page.tsx and EmergencyVetSection.tsx fully localized (were 100% hardcoded German). Stale TODO comments removed, auth.test.ts scaffold rewritten to match the actual lazy-upsert pattern instead of a Clerk webhook flow that was never built.
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import type { NextConfig } from "next";
|
|
import createNextIntlPlugin from "next-intl/plugin";
|
|
import { withSentryConfig } from "@sentry/nextjs";
|
|
|
|
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
|
|
|
|
function getSupabaseHostname(): string {
|
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
if (url) {
|
|
try { return new URL(url).hostname; } catch { /* fall through */ }
|
|
}
|
|
return "*.supabase.co";
|
|
}
|
|
|
|
const nextConfig: NextConfig = {
|
|
output: "standalone",
|
|
allowedDevOrigins: ["192.168.1.92"],
|
|
images: {
|
|
remotePatterns: [
|
|
{ protocol: "https", hostname: getSupabaseHostname() },
|
|
],
|
|
},
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: "/(.*)",
|
|
headers: [
|
|
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
{ key: "X-Frame-Options", value: "DENY" },
|
|
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
|
|
// HSTS intentionally NOT set here — Cloudflare (in front of the origin) already
|
|
// sends a stronger one (max-age=63072000; preload). Setting both resulted in two
|
|
// Strict-Transport-Security headers on every response; browsers only honor the
|
|
// first, so the app's weaker one was pure noise. If Cloudflare is ever removed
|
|
// from the path, re-add this header at the edge/proxy layer, not here.
|
|
],
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default withSentryConfig(withNextIntl(nextConfig), {
|
|
org: process.env.SENTRY_ORG,
|
|
project: process.env.SENTRY_PROJECT,
|
|
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
silent: true,
|
|
// No auth token configured on this self-hosted deploy yet — skip
|
|
// source-map upload rather than failing the Docker build.
|
|
sourcemaps: { disable: !process.env.SENTRY_AUTH_TOKEN },
|
|
disableLogger: true,
|
|
});
|