Files
jorgecuadros-platform/apps/web/src/app/layout.tsx
T
rmancinasandClaude Opus 5 14c6183aa2
Build and Push Images / Build jorgecuadros-web (push) Successful in 1m55s
Build and Push Images / Build jorgecuadros-api (push) Successful in 2m35s
feat(deploy): derive the API origin from the page, not from a pinned env var
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>
2026-08-11 12:55:32 -07:00

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>
);
}