fix: restore ISR on public pages — isolate DYNAMIC_SERVER_USAGE root cause
Deploy to VPS / deploy (push) Has been cancelled
Deploy to VPS / deploy (push) Has been cancelled
Root cause: next-intl's getMessages/getTranslations internally resolves requestLocale by reading cookies/headers, which trips DYNAMIC_SERVER_USAGE under ISR. Fixed by calling setRequestLocale(locale) in layout + every public page — caches the locale in React cache so next-intl never reads cookies. Changes: - [locale]/layout.tsx: +setRequestLocale, +generateStaticParams (5 locales), wrap NavigationManager in <Suspense> (uses useSearchParams) - 5 public pages: force-dynamic → revalidate=60, +setRequestLocale - HQ dashboard pages: unchanged (still force-dynamic for auth) Build verified: home/heritage/news pre-render as SSG with 1m revalidation, slug pages render on-demand with ISR cache. Nginx s-maxage=60 remains as safety net. Zero DYNAMIC_SERVER_USAGE errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
||||
export const dynamic = "force-dynamic";
|
||||
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||
export const revalidate = 60;
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
@@ -9,6 +9,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { notFound } from "next/navigation";
|
||||
import ApplicationClient from "./ApplicationClient";
|
||||
|
||||
import { setRequestLocale } from "next-intl/server";
|
||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||
import {
|
||||
buildPageMetadata,
|
||||
@@ -82,16 +83,15 @@ export async function generateMetadata({
|
||||
}
|
||||
}
|
||||
|
||||
// generateStaticParams intentionally omitted — combined with
|
||||
// `dynamic = "force-dynamic"`, this guarantees pure SSR per request.
|
||||
// Having it (even returning [] in prod) made Next.js classify the
|
||||
// route as SSG-eligible, which conflicted with dynamic API reads
|
||||
// elsewhere in the tree and surfaced as DYNAMIC_SERVER_USAGE errors.
|
||||
// generateStaticParams omitted — slug pages render on-demand and are cached
|
||||
// by ISR (revalidate=60). The DYNAMIC_SERVER_USAGE issue that previously
|
||||
// blocked this is now fixed via setRequestLocale.
|
||||
|
||||
// 🔥 AHORA RECIBIMOS EL LOCALE DESDE LA URL
|
||||
export default async function ApplicationPage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
|
||||
const resolvedParams = await params;
|
||||
const { slug, locale } = resolvedParams;
|
||||
setRequestLocale(locale);
|
||||
|
||||
// 1. Buscamos la Teoría General de la Aplicación
|
||||
let rawData: any = null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
||||
export const dynamic = "force-dynamic";
|
||||
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||
// setRequestLocale caches the locale so next-intl doesn't read cookies/headers.
|
||||
export const revalidate = 60;
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
@@ -11,7 +12,7 @@ import AutoPlayVideo from "@/components/AutoPlayVideo";
|
||||
|
||||
// 🔥 IMPORTACIONES DE IDIOMAS
|
||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import { buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -197,7 +198,7 @@ export default async function HeritagePage({ params }: { params: Promise<{ local
|
||||
const resolvedParams = await params;
|
||||
const locale = resolvedParams.locale;
|
||||
|
||||
// Pass locale explicitly so getTranslations stays static-friendly under ISR.
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale, namespace: "HeritagePage" });
|
||||
|
||||
let rawSections: any[] = [];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Metadata, Viewport } from "next";
|
||||
import { Suspense } from "react";
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
@@ -9,9 +10,15 @@ import Footer from "@/components/layout/Footer";
|
||||
import CartDrawer from "@/components/layout/CartDrawer";
|
||||
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getMessages } from 'next-intl/server';
|
||||
import { getMessages, setRequestLocale } from 'next-intl/server';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { routing } from '@/i18n/routing';
|
||||
|
||||
// Pre-render all 5 locale variants at build time so Next.js knows which
|
||||
// [locale] segments are valid. Required for ISR to work with next-intl.
|
||||
export function generateStaticParams() {
|
||||
return routing.locales.map((locale) => ({ locale }));
|
||||
}
|
||||
import { getBranding, getSocialLinks } from '@/lib/siteSettings';
|
||||
import { organizationSchema, websiteSchema } from '@/lib/seo';
|
||||
import JsonLd from '@/components/seo/JsonLd';
|
||||
@@ -116,6 +123,10 @@ export default async function RootLayout({
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Cache the locale so next-intl resolves it from memory instead of
|
||||
// reading cookies/headers — this is what allows ISR on child pages.
|
||||
setRequestLocale(locale);
|
||||
|
||||
const [messages, branding, social] = await Promise.all([
|
||||
getMessages({ locale }),
|
||||
getBranding(),
|
||||
@@ -153,7 +164,10 @@ export default async function RootLayout({
|
||||
|
||||
<CartDrawer />
|
||||
|
||||
{/* Suspense boundary required for useSearchParams() under ISR */}
|
||||
<Suspense fallback={null}>
|
||||
<NavigationManager />
|
||||
</Suspense>
|
||||
|
||||
<div className="flex-grow w-full flex flex-col relative">
|
||||
{children}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
||||
export const dynamic = "force-dynamic";
|
||||
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||
export const revalidate = 60;
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
@@ -8,6 +8,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { ArrowLeft, Calendar, Tag, Linkedin } from "lucide-react";
|
||||
import BreathingField from "@/components/visuals/BreathingField";
|
||||
|
||||
import { setRequestLocale } from "next-intl/server";
|
||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||
import {
|
||||
buildPageMetadata,
|
||||
@@ -47,8 +48,9 @@ export async function generateMetadata({
|
||||
}
|
||||
}
|
||||
|
||||
// generateStaticParams intentionally omitted — see /[locale]/page.tsx
|
||||
// for the rationale.
|
||||
// generateStaticParams omitted — slug pages render on-demand and are cached
|
||||
// by ISR (revalidate=60). The DYNAMIC_SERVER_USAGE issue is fixed via
|
||||
// setRequestLocale.
|
||||
|
||||
// ── SÚPER PARSER MARKDOWN (Con Tablas, Imágenes y Dark/Light Mode) ──
|
||||
// ... (El código del Súper Parser se queda IGUAL, no te preocupes) ...
|
||||
@@ -213,6 +215,7 @@ const renderMarkdown = (text: string) => {
|
||||
export default async function ArticlePage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
|
||||
const resolvedParams = await params;
|
||||
const { slug, locale } = resolvedParams;
|
||||
setRequestLocale(locale);
|
||||
|
||||
let rawArticle: any = null;
|
||||
try {
|
||||
|
||||
@@ -6,11 +6,11 @@ import { Newspaper, ArrowRight, Calendar } from "lucide-react";
|
||||
import BreathingField from "@/components/visuals/BreathingField";
|
||||
|
||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import { buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
||||
export const dynamic = "force-dynamic";
|
||||
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||
export const revalidate = 60;
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -31,7 +31,7 @@ export default async function NewsHub({ params }: { params: Promise<{ locale: st
|
||||
const resolvedParams = await params;
|
||||
const locale = resolvedParams.locale;
|
||||
|
||||
// Pass locale explicitly so getTranslations stays static-friendly under ISR.
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale, namespace: "NewsHub" });
|
||||
|
||||
let rawArticles: any[] = [];
|
||||
|
||||
@@ -15,17 +15,16 @@ import ApplicationsDeep from "@/components/sections/ApplicationsDeep";
|
||||
import HeroReel from "@/components/sections/HeroReel";
|
||||
import WhatWeDo from "@/components/sections/WhatWeDo";
|
||||
|
||||
import { setRequestLocale } from "next-intl/server";
|
||||
import { buildPageMetadata } from "@/lib/seo";
|
||||
import { getBranding } from "@/lib/siteSettings";
|
||||
|
||||
// Force-dynamic — there's an interaction between next-intl 4, Prisma client,
|
||||
// and Next.js 16 standalone that intermittently leaks dynamic API reads
|
||||
// (cookies / headers) through Server Components and trips DYNAMIC_SERVER_USAGE
|
||||
// under ISR. Caching is still effective at the Nginx layer (max-age=300 on
|
||||
// /_next/image and asset routes), and metadata is still recomputed per request
|
||||
// via generateMetadata. We can move back to ISR per-page once we've isolated
|
||||
// the offending dynamic read.
|
||||
export const dynamic = "force-dynamic";
|
||||
// ISR: revalidate every 60s. The DYNAMIC_SERVER_USAGE issue was caused by
|
||||
// next-intl internally reading cookies/headers to resolve the locale.
|
||||
// Fixed by calling setRequestLocale(locale) which caches the locale in
|
||||
// React cache, preventing the dynamic API read. Nginx also caches with
|
||||
// s-maxage=60 + stale-while-revalidate=300 as a safety net.
|
||||
export const revalidate = 60;
|
||||
|
||||
const TITLES: Record<string, string> = {
|
||||
en: "FLUX | Solid-State RF Industrial Solutions",
|
||||
@@ -63,6 +62,7 @@ export async function generateMetadata({
|
||||
// ✅ Next.js 16: params es Promise y DEBE ser awaiteado
|
||||
export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
|
||||
// --- 1. SLIDES DEL HERO (CMS-managed via HeroSlide model, fallback to filesystem) ---
|
||||
let heroSlides: Array<{
|
||||
|
||||
Reference in New Issue
Block a user