diff --git a/.env.example b/.env.example index 0c5dc73..4babbc6 100644 --- a/.env.example +++ b/.env.example @@ -1,29 +1,26 @@ +# ============================================================================= +# Public suite URL — edit PUBLIC_HOST + SECURE when switching domain +# ============================================================================= +# Hostname only (no scheme). Keep in sync with ulti-backend/.env PUBLIC_HOST. +PUBLIC_HOST=localhost +# "s" → https/wss (Cloudflare tunnel, prod) ; empty → http/ws (local nginx :80) +SECURE= + # API backend — URL relative : Next.js proxy vers nginx (:80), pas de CORS en dev NEXT_PUBLIC_API_URL=/api/v1 -NEXT_PUBLIC_WS_URL=ws://localhost/ws # Cible du proxy Next (optionnel, défaut 127.0.0.1:80) # Sert aussi aux appels OIDC serveur (discovery/token) — Docker: http://nginx # ULTI_PROXY_ORIGIN=http://127.0.0.1:80 -# OIDC Authentik (blueprints deploy/authentik dans ulti-backend) -NEXT_PUBLIC_OIDC_ISSUER=http://localhost/auth/application/o/ulti/ NEXT_PUBLIC_OIDC_CLIENT_ID=ulti-backend -# URL publique affichée dans les redirects OIDC (navigateur) — utiliser localhost, pas 0.0.0.0 -# URL publique navigateur (suite nginx) — pas :3004 si tu passes par http://localhost/mail -NEXT_PUBLIC_APP_URL=http://localhost -# Cookies session Secure (auto: true seulement si NEXT_PUBLIC_APP_URL est https://) +# Cookies session Secure (auto: true si NEXT_PUBLIC_APP_URL dérivé en https://) # COOKIE_SECURE=false # Secret serveur uniquement — doit matcher ULTID_OIDC_CLIENT_SECRET / blueprint OIDC_CLIENT_SECRET=changeme -# OnlyOffice editor (UltiDrive — tableurs/présentations) -NEXT_PUBLIC_ONLYOFFICE_URL=http://localhost/office -# Rich text editor (TipTap + Hocuspocus — docs texte) -NEXT_PUBLIC_HOCUSPOCUS_URL=ws://localhost/collab # UltiAI (chemin proxy OpenWebUI — même origine) NEXT_PUBLIC_AI_PUBLIC_PATH=/ai -# Origine UltiSpace par défaut (picker serveur apps Tauri mobiles) -# NEXT_PUBLIC_ULTISPACE_ORIGIN=https://dev.ultispace.fr -# Dev Next.js (:3000) : charger l'iframe depuis nginx (:80) pour cookies session + proxy OpenWebUI -NEXT_PUBLIC_AI_ORIGIN=http://localhost + +# NEXT_PUBLIC_APP_URL, WS_URL, OIDC_ISSUER, ONLYOFFICE_URL, HOCUSPOCUS_URL, +# AI_ORIGIN, ULTISPACE_ORIGIN — dérivés dans next.config.mjs depuis PUBLIC_HOST + SECURE. diff --git a/Dockerfile b/Dockerfile index 9aeb01d..8acf54e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,10 @@ FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . +ARG PUBLIC_HOST=localhost +ARG SECURE= +ENV PUBLIC_HOST=$PUBLIC_HOST +ENV SECURE=$SECURE ENV NEXT_TELEMETRY_DISABLED=1 RUN pnpm run build diff --git a/next.config.mjs b/next.config.mjs index 548821f..fc1cd3a 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -10,22 +10,48 @@ const projectRoot = path.dirname(fileURLToPath(import.meta.url)) const ultiProxyOrigin = process.env.ULTI_PROXY_ORIGIN ?? "http://127.0.0.1" +/** "s" for https/wss when SECURE=s (or true/1). */ +function suiteSecureSuffix() { + const secure = process.env.SECURE?.trim().toLowerCase() + if (secure === "s" || secure === "true" || secure === "1") return "s" + return "" +} + +/** Derive browser-facing NEXT_PUBLIC_* URLs from PUBLIC_HOST + SECURE. */ +function suitePublicEnv() { + const host = ( + process.env.PUBLIC_HOST ?? + process.env.DOMAIN ?? + "localhost" + ).trim() + const s = suiteSecureSuffix() + const origin = `http${s}://${host}` + return { + NEXT_PUBLIC_APP_URL: origin, + NEXT_PUBLIC_WS_URL: `ws${s}://${host}/ws`, + NEXT_PUBLIC_OIDC_ISSUER: `${origin}/auth/application/o/ulti/`, + NEXT_PUBLIC_ONLYOFFICE_URL: `${origin}/office`, + NEXT_PUBLIC_HOCUSPOCUS_URL: `ws${s}://${host}/collab/`, + NEXT_PUBLIC_AI_ORIGIN: origin, + NEXT_PUBLIC_ULTISPACE_ORIGIN: origin, + } +} + /** Hostnames allowed to load /_next/* in dev (tunnel, LAN, public dev domain). */ function allowedDevOrigins() { - const hosts = new Set([ - "127.0.0.1", - "localhost", - "192.168.0.20", - "100.120.4.66", - "dev.ultispace.fr", - "dev.ultimail.fr", - ]) - const appUrl = process.env.NEXT_PUBLIC_APP_URL?.trim() - if (appUrl) { + const hosts = new Set(["127.0.0.1", "localhost"]) + const publicHost = ( + process.env.PUBLIC_HOST ?? + process.env.DOMAIN ?? + "" + ).trim() + if (publicHost) hosts.add(publicHost) + const derived = suitePublicEnv().NEXT_PUBLIC_APP_URL + if (derived) { try { - hosts.add(new URL(appUrl).hostname) + hosts.add(new URL(derived).hostname) } catch { - /* ignore invalid NEXT_PUBLIC_APP_URL */ + /* ignore invalid origin */ } } return [...hosts] @@ -46,6 +72,7 @@ const mobilePageExtensions = ["tsx", "ts", "jsx", "js"] const baseConfig = { outputFileTracingRoot: projectRoot, allowedDevOrigins: allowedDevOrigins(), + env: suitePublicEnv(), typescript: { ignoreBuildErrors: true, },