The browser hard-required API_ORIGIN, so every move of the server — tailnet today, the 192.168.1.0 office LAN later, a temporary demo domain in between — meant editing the deploy env and redeploying. Worse, an http:// API origin on a page served over TLS is blocked outright as mixed active content, which is what broke the demo on https://jorgecuadros.freakma.com. The browser now derives the origin from window.location the way a PHP app would: same host on port 3001 over plain HTTP, or the same-origin /api path under https (the reverse proxy strips the prefix). API_ORIGIN survives as an optional override for a deployment that genuinely splits the two hosts, and SSR still reads process.env because a derived origin is browser-only. WEB_ORIGIN becomes a comma-separated list to match: one deployment is now reached under several origins, and a credentialed fetch from an unlisted one gets no CORS headers and fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import "./globals.css";
|
|
import { readBuildInfoFromEnv } from "@/lib/build-info";
|
|
|
|
export const metadata = {
|
|
title: "Jorge Cuadros & Asociados — Plataforma",
|
|
description:
|
|
"Plataforma interna unificada de clientes, servicios y seguros.",
|
|
};
|
|
|
|
// API_ORIGIN is an OPTIONAL override, read here on the server per request and
|
|
// injected as window.__API_ORIGIN__ (see lib/api.ts). NEXT_PUBLIC_* would bake
|
|
// it at build time (one URL per image); reading it here keeps one image usable
|
|
// anywhere. Left unset — the normal case — this injects the empty string and
|
|
// lib/api.ts derives the origin from window.location instead, so the app
|
|
// follows the server when it moves without an env edit. force-dynamic
|
|
// guarantees process.env is read at request time, never baked into a static
|
|
// prerender.
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
const apiOrigin = process.env.API_ORIGIN ?? "";
|
|
// Same reason as the API origin: read on the server per request so the built
|
|
// image is not pinned to one build identity in its client bundle.
|
|
const build = readBuildInfoFromEnv();
|
|
|
|
return (
|
|
<html lang="es">
|
|
<head>
|
|
{/* Must run before the app bundle so lib/api.ts sees it at import. */}
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html:
|
|
`window.__API_ORIGIN__=${JSON.stringify(apiOrigin)};` +
|
|
`window.__APP_BUILD__=${JSON.stringify(build)};`,
|
|
}}
|
|
/>
|
|
{/* Text-size preference, applied before first paint so the page never
|
|
flashes at the default size. Mirrors lib/ui-scale.ts — keep the key
|
|
and the clamp in sync with it. */}
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html:
|
|
`try{var s=parseFloat(localStorage.getItem("jc.ui-scale"));` +
|
|
`if(isFinite(s))document.documentElement.style.setProperty(` +
|
|
`"--ui-scale",String(Math.min(1.5,Math.max(0.9,s))));}catch(e){}`,
|
|
}}
|
|
/>
|
|
{/* Google Fonts via <link> so an offline build still runs with the
|
|
system fallback stacks defined in globals.css. */}
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link
|
|
rel="preconnect"
|
|
href="https://fonts.gstatic.com"
|
|
crossOrigin="anonymous"
|
|
/>
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,560;9..144,600&family=Work+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
</head>
|
|
<body>{children}</body>
|
|
</html>
|
|
);
|
|
}
|