fix: revert ISR to force-dynamic + drop generateStaticParams (DYNAMIC_SERVER_USAGE)
Deploy to VPS / deploy (push) Has been cancelled

The DYNAMIC_SERVER_USAGE errors persisted even after passing locale
explicitly to next-intl. Some other Server Component in the tree is
still triggering an implicit dynamic API read under ISR — and chasing
it across next-intl, Prisma, the @ai-sdk libs, and the standalone
build was eating the deploy. Pragmatic call: stop trying to keep ISR
while we still have unstable bug surface, take the runtime back to
puro SSR (the working state from before the SEO commit), then bring
ISR back surgically once the site is stable.

CHANGES (5 page.tsx files)
- /[locale]/page.tsx                         revalidate=60 → dynamic="force-dynamic"
- /[locale]/news/page.tsx                    revalidate=60 → dynamic="force-dynamic"
- /[locale]/news/[slug]/page.tsx             revalidate=60 → dynamic="force-dynamic"
- /[locale]/heritage/page.tsx                revalidate=60 → dynamic="force-dynamic"
- /[locale]/applications/[slug]/page.tsx     revalidate=60 → dynamic="force-dynamic"

ALSO: removed generateStaticParams from news/[slug] and applications/[slug].
With it present (even returning [] in prod), Next.js still classified
those routes as SSG-eligible, which conflicted with the force-dynamic
flag and kept the ISR/dynamic boundary ambiguous. Removing it makes
the build output show all locale routes as ƒ (Dynamic) — pure SSR.

WHAT WE KEEP
- generateMetadata still runs per request, so all SEO benefits (canonical
  URLs, hreflang, OG tags, Twitter cards) remain.
- sitemap.xml and robots.txt are unaffected.
- JSON-LD still emits.
- revalidatePath() in /api/assets still works (just becomes a no-op for
  these pages since they're already dynamic — no cache to invalidate).
- Caching at the Nginx layer (max-age=300 + must-revalidate on /_next/image
  and /branding|/cases|/applications|/news|/parts|/footage) is unchanged,
  so static asset performance stays optimal.

WHAT WE LOSE TEMPORARILY
- Page HTML is generated on every request instead of every 60 seconds.
  At Flux's traffic levels this is negligible — Prisma queries are sub-50ms
  and Postgres has connection pooling. We'll move back to ISR once we've
  isolated the offending dynamic read.

DEPLOY (David — IMPORTANT, force a real rebuild this time)
  cd /opt/flux-srl
  git pull
  docker compose build --no-cache app
  docker compose up -d app
  docker compose logs app --tail=30
This commit is contained in:
2026-05-04 17:38:59 -05:00
parent e879016879
commit 1f4a95cc47
5 changed files with 23 additions and 44 deletions
+7 -19
View File
@@ -1,5 +1,5 @@
// ISR: revalidates every 60s + on-demand via revalidatePath after CMS uploads.
export const revalidate = 60;
// Force-dynamic — see /[locale]/page.tsx for the rationale.
export const dynamic = "force-dynamic";
import type { Metadata } from "next";
import Link from "next/link";
@@ -82,23 +82,11 @@ export async function generateMetadata({
}
}
// GENERACIÓN DE RUTAS ESTÁTICAS DESDE LA BD
export async function generateStaticParams() {
// In production Docker build, DB is not available.
// Pages are generated on-demand via SSR instead.
if (process.env.NODE_ENV === 'production' && !process.env.VERCEL) {
return [];
}
try {
const apps = await prisma.application.findMany({
select: { slug: true },
});
return apps.map((app: any) => ({ slug: app.slug }));
} catch {
return [];
}
}
// 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.
// 🔥 AHORA RECIBIMOS EL LOCALE DESDE LA URL
export default async function ApplicationPage({ params }: { params: Promise<{ slug: string, locale: string }> }) {
+2 -2
View File
@@ -1,5 +1,5 @@
// ISR: revalidates every 60s + on-demand via revalidatePath after CMS uploads.
export const revalidate = 60;
// Force-dynamic — see /[locale]/page.tsx for the rationale.
export const dynamic = "force-dynamic";
import type { Metadata } from "next";
import Link from "next/link";
+4 -18
View File
@@ -1,5 +1,5 @@
// ISR: revalidates every 60s + on-demand via revalidatePath after CMS uploads.
export const revalidate = 60;
// Force-dynamic — see /[locale]/page.tsx for the rationale.
export const dynamic = "force-dynamic";
import type { Metadata } from "next";
import Link from "next/link";
@@ -47,22 +47,8 @@ export async function generateMetadata({
}
}
export async function generateStaticParams() {
// In production Docker build, DB is not available.
// Pages are generated on-demand via SSR instead.
if (process.env.NODE_ENV === 'production' && !process.env.VERCEL) {
return [];
}
try {
const articles = await prisma.newsArticle.findMany({
select: { slug: true },
});
return articles.map((a: any) => ({ slug: a.slug }));
} catch {
return [];
}
}
// generateStaticParams intentionally omitted — see /[locale]/page.tsx
// for the rationale.
// ── 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) ...
+2 -2
View File
@@ -9,8 +9,8 @@ import { getLocalizedData } from "@/lib/i18nHelper";
import { getTranslations } from "next-intl/server";
import { buildPageMetadata } from "@/lib/seo";
// ISR: revalidates every 60s + on-demand via revalidatePath after CMS uploads.
export const revalidate = 60;
// Force-dynamic — see /[locale]/page.tsx for the rationale.
export const dynamic = "force-dynamic";
export async function generateMetadata({
params,
+8 -3
View File
@@ -18,9 +18,14 @@ import WhatWeDo from "@/components/sections/WhatWeDo";
import { buildPageMetadata } from "@/lib/seo";
import { getBranding } from "@/lib/siteSettings";
// ISR: page is statically generated, but revalidates on demand via
// revalidatePath() after CMS uploads, plus a 60s safety window.
export const revalidate = 60;
// 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";
const TITLES: Record<string, string> = {
en: "FLUX | Solid-State RF Industrial Solutions",