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.
|
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -9,6 +9,7 @@ import { prisma } from "@/lib/prisma";
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import ApplicationClient from "./ApplicationClient";
|
import ApplicationClient from "./ApplicationClient";
|
||||||
|
|
||||||
|
import { setRequestLocale } from "next-intl/server";
|
||||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||||
import {
|
import {
|
||||||
buildPageMetadata,
|
buildPageMetadata,
|
||||||
@@ -82,16 +83,15 @@ export async function generateMetadata({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateStaticParams intentionally omitted — combined with
|
// generateStaticParams omitted — slug pages render on-demand and are cached
|
||||||
// `dynamic = "force-dynamic"`, this guarantees pure SSR per request.
|
// by ISR (revalidate=60). The DYNAMIC_SERVER_USAGE issue that previously
|
||||||
// Having it (even returning [] in prod) made Next.js classify the
|
// blocked this is now fixed via setRequestLocale.
|
||||||
// route as SSG-eligible, which conflicted with dynamic API reads
|
|
||||||
// elsewhere in the tree and surfaced as DYNAMIC_SERVER_USAGE errors.
|
|
||||||
|
|
||||||
// 🔥 AHORA RECIBIMOS EL LOCALE DESDE LA URL
|
// 🔥 AHORA RECIBIMOS EL LOCALE DESDE LA URL
|
||||||
export default async function ApplicationPage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
|
export default async function ApplicationPage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
|
||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const { slug, locale } = resolvedParams;
|
const { slug, locale } = resolvedParams;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
|
||||||
// 1. Buscamos la Teoría General de la Aplicación
|
// 1. Buscamos la Teoría General de la Aplicación
|
||||||
let rawData: any = null;
|
let rawData: any = null;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||||
export const dynamic = "force-dynamic";
|
// setRequestLocale caches the locale so next-intl doesn't read cookies/headers.
|
||||||
|
export const revalidate = 60;
|
||||||
|
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -11,7 +12,7 @@ import AutoPlayVideo from "@/components/AutoPlayVideo";
|
|||||||
|
|
||||||
// 🔥 IMPORTACIONES DE IDIOMAS
|
// 🔥 IMPORTACIONES DE IDIOMAS
|
||||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
import { buildPageMetadata } from "@/lib/seo";
|
import { buildPageMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
@@ -197,7 +198,7 @@ export default async function HeritagePage({ params }: { params: Promise<{ local
|
|||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const locale = resolvedParams.locale;
|
const locale = resolvedParams.locale;
|
||||||
|
|
||||||
// Pass locale explicitly so getTranslations stays static-friendly under ISR.
|
setRequestLocale(locale);
|
||||||
const t = await getTranslations({ locale, namespace: "HeritagePage" });
|
const t = await getTranslations({ locale, namespace: "HeritagePage" });
|
||||||
|
|
||||||
let rawSections: any[] = [];
|
let rawSections: any[] = [];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
|
import { Suspense } from "react";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
|
|
||||||
@@ -9,9 +10,15 @@ import Footer from "@/components/layout/Footer";
|
|||||||
import CartDrawer from "@/components/layout/CartDrawer";
|
import CartDrawer from "@/components/layout/CartDrawer";
|
||||||
|
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import { getMessages } from 'next-intl/server';
|
import { getMessages, setRequestLocale } from 'next-intl/server';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { routing } from '@/i18n/routing';
|
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 { getBranding, getSocialLinks } from '@/lib/siteSettings';
|
||||||
import { organizationSchema, websiteSchema } from '@/lib/seo';
|
import { organizationSchema, websiteSchema } from '@/lib/seo';
|
||||||
import JsonLd from '@/components/seo/JsonLd';
|
import JsonLd from '@/components/seo/JsonLd';
|
||||||
@@ -116,6 +123,10 @@ export default async function RootLayout({
|
|||||||
notFound();
|
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([
|
const [messages, branding, social] = await Promise.all([
|
||||||
getMessages({ locale }),
|
getMessages({ locale }),
|
||||||
getBranding(),
|
getBranding(),
|
||||||
@@ -153,7 +164,10 @@ export default async function RootLayout({
|
|||||||
|
|
||||||
<CartDrawer />
|
<CartDrawer />
|
||||||
|
|
||||||
<NavigationManager />
|
{/* Suspense boundary required for useSearchParams() under ISR */}
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<NavigationManager />
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
<div className="flex-grow w-full flex flex-col relative">
|
<div className="flex-grow w-full flex flex-col relative">
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -8,6 +8,7 @@ import { prisma } from "@/lib/prisma";
|
|||||||
import { ArrowLeft, Calendar, Tag, Linkedin } from "lucide-react";
|
import { ArrowLeft, Calendar, Tag, Linkedin } from "lucide-react";
|
||||||
import BreathingField from "@/components/visuals/BreathingField";
|
import BreathingField from "@/components/visuals/BreathingField";
|
||||||
|
|
||||||
|
import { setRequestLocale } from "next-intl/server";
|
||||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||||
import {
|
import {
|
||||||
buildPageMetadata,
|
buildPageMetadata,
|
||||||
@@ -47,8 +48,9 @@ export async function generateMetadata({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateStaticParams intentionally omitted — see /[locale]/page.tsx
|
// generateStaticParams omitted — slug pages render on-demand and are cached
|
||||||
// for the rationale.
|
// 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) ──
|
// ── 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) ...
|
// ... (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 }> }) {
|
export default async function ArticlePage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
|
||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const { slug, locale } = resolvedParams;
|
const { slug, locale } = resolvedParams;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
|
||||||
let rawArticle: any = null;
|
let rawArticle: any = null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { Newspaper, ArrowRight, Calendar } from "lucide-react";
|
|||||||
import BreathingField from "@/components/visuals/BreathingField";
|
import BreathingField from "@/components/visuals/BreathingField";
|
||||||
|
|
||||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
import { buildPageMetadata } from "@/lib/seo";
|
import { buildPageMetadata } from "@/lib/seo";
|
||||||
|
|
||||||
// Force-dynamic — see /[locale]/page.tsx for the rationale.
|
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
@@ -31,7 +31,7 @@ export default async function NewsHub({ params }: { params: Promise<{ locale: st
|
|||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const locale = resolvedParams.locale;
|
const locale = resolvedParams.locale;
|
||||||
|
|
||||||
// Pass locale explicitly so getTranslations stays static-friendly under ISR.
|
setRequestLocale(locale);
|
||||||
const t = await getTranslations({ locale, namespace: "NewsHub" });
|
const t = await getTranslations({ locale, namespace: "NewsHub" });
|
||||||
|
|
||||||
let rawArticles: any[] = [];
|
let rawArticles: any[] = [];
|
||||||
|
|||||||
@@ -15,17 +15,16 @@ import ApplicationsDeep from "@/components/sections/ApplicationsDeep";
|
|||||||
import HeroReel from "@/components/sections/HeroReel";
|
import HeroReel from "@/components/sections/HeroReel";
|
||||||
import WhatWeDo from "@/components/sections/WhatWeDo";
|
import WhatWeDo from "@/components/sections/WhatWeDo";
|
||||||
|
|
||||||
|
import { setRequestLocale } from "next-intl/server";
|
||||||
import { buildPageMetadata } from "@/lib/seo";
|
import { buildPageMetadata } from "@/lib/seo";
|
||||||
import { getBranding } from "@/lib/siteSettings";
|
import { getBranding } from "@/lib/siteSettings";
|
||||||
|
|
||||||
// Force-dynamic — there's an interaction between next-intl 4, Prisma client,
|
// ISR: revalidate every 60s. The DYNAMIC_SERVER_USAGE issue was caused by
|
||||||
// and Next.js 16 standalone that intermittently leaks dynamic API reads
|
// next-intl internally reading cookies/headers to resolve the locale.
|
||||||
// (cookies / headers) through Server Components and trips DYNAMIC_SERVER_USAGE
|
// Fixed by calling setRequestLocale(locale) which caches the locale in
|
||||||
// under ISR. Caching is still effective at the Nginx layer (max-age=300 on
|
// React cache, preventing the dynamic API read. Nginx also caches with
|
||||||
// /_next/image and asset routes), and metadata is still recomputed per request
|
// s-maxage=60 + stale-while-revalidate=300 as a safety net.
|
||||||
// via generateMetadata. We can move back to ISR per-page once we've isolated
|
export const revalidate = 60;
|
||||||
// the offending dynamic read.
|
|
||||||
export const dynamic = "force-dynamic";
|
|
||||||
|
|
||||||
const TITLES: Record<string, string> = {
|
const TITLES: Record<string, string> = {
|
||||||
en: "FLUX | Solid-State RF Industrial Solutions",
|
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
|
// ✅ Next.js 16: params es Promise y DEBE ser awaiteado
|
||||||
export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
|
export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
|
||||||
// --- 1. SLIDES DEL HERO (CMS-managed via HeroSlide model, fallback to filesystem) ---
|
// --- 1. SLIDES DEL HERO (CMS-managed via HeroSlide model, fallback to filesystem) ---
|
||||||
let heroSlides: Array<{
|
let heroSlides: Array<{
|
||||||
|
|||||||
Reference in New Issue
Block a user