From 063c5758a7b300d3df4de7d236a7efea5e306a7c Mon Sep 17 00:00:00 2001 From: vl Date: Sun, 20 Sep 2026 18:21:24 +0200 Subject: [PATCH] chore: settle the public domain and move off port 3000 The application is published at horaires.ita-ito.com. One origin serves both the panel and the browser, so the OIDC callback, the image URL the device is handed and the documentation all follow from this single name. Port 3000 is already taken by the facture_ocr stack on the development machine, so the app listens on 3010 there and the reason is written next to the setting rather than left to be rediscovered. The fixtures and the frozen payload snapshot move to the real domain too: a contract snapshot carrying a hostname that never existed is a small puzzle left for whoever reads it next. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd --- .env.example | 8 +++++--- .env.swp | Bin 12288 -> 0 bytes PLAN.md | 12 ++++++------ lib/config.ts | 2 +- lib/screen/__snapshots__/viewmodel.test.ts.snap | 2 +- lib/screen/render.test.ts | 2 +- lib/screen/viewmodel.test.ts | 2 +- package.json | 2 +- scripts/preview.ts | 2 +- 9 files changed, 17 insertions(+), 15 deletions(-) delete mode 100644 .env.swp diff --git a/.env.example b/.env.example index ace6a89..0eab434 100644 --- a/.env.example +++ b/.env.example @@ -9,8 +9,10 @@ # Nom de domaine public de l'app sur le VPS. Un seul domaine sert à la fois # l'écran et le navigateur : le callback OIDC, l'URL d'image envoyée au panneau # et les liens du README en dépendent tous. Doit rester cohérent avec AUTH_URL. -APP_DOMAIN=horaires.loxi.ch -# Port publié en local (dev). En production, Traefik s'en charge : aucun port publié. +APP_DOMAIN=horaires.ita-ito.com +# Port publié sur l'hôte. 3000 est déjà pris par la stack facture_ocr sur la +# machine de développement, d'où 3010 ; en production Traefik s'en charge et +# aucun port n'est publié. APP_PORT=3010 APP_BIND=127.0.0.1 TZ=Europe/Zurich @@ -34,7 +36,7 @@ TEST_DATABASE_URL= # # URI de redirection à déclarer dans Authentik (correspondance stricte) : # /api/auth/callback/authentik -AUTH_URL=https://horaires.loxi.ch +AUTH_URL=https://horaires.ita-ito.com # Générer avec : openssl rand -base64 33 AUTH_SECRET= diff --git a/.env.swp b/.env.swp deleted file mode 100644 index 48d7bb2c1947c0b511434fb0221379c776bb172c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2J#W)M7=W*Z4GIVrHr#e4Uk#-qk_B#kZ7uBBvYl4#VqKCWHg$cjeGWuIFtG6x z_yG(|u)+=-Gdl>0f5GdL651jaRSZ;m&w6zBz4!6@xm%*RTi-A&2p71r4Bq zQyZ8e&4ot296moct6aOk@q&+=O{rb3` zI#Q1xu|flA01co4G=K)s02)98XaEhM0W^RH&Y%I-C*<`-Lgr>5dHDbT;P?Nx%Y=Le zJ^~+rx4=sv1Ny*u;N2xc9KZ&afE&O$;494a1$Ym<0$u>SfCJb-xz;Rz1r4A9G=K)s z02)98XaEhM0W|P`8CbVXL)W&vKy!?sGa{(N1wFST40{Y8$qz#^P~wEOwu6smB_B z&#la-99iv!gy?j`SfPPSS6%9@2P>xSQ9bbJin_D_Sse-64sGuTZ%<;HY1*5?y6-#c z0~Y7}V2ojDYcyE3T}$)T?rsY2Pdz`-HD6oSJi4zd6}cQ_Jl~7K+4#Hfyw&H!ib zB@wd1Dmm|ET;6LeELH1pHCl^{x0=mg-2=&Uc{lHce3z+d(T$@XnOBTN@;X|H{6v?_Vzp3Oxks%)`11`&(C{x6vMCmgKvz%ASKTr2aruuT=*sibc zzY}q6&tHR$2prAxHf>i||L9`Uuj5fyJrFl()|V%Z#NSqFk->?qZ5LSto31&|&qbSc Z_~5sE(rrsK+HjtFEDm8$TIHD}KLFPd6k7lQ diff --git a/PLAN.md b/PLAN.md index f859746..0014246 100644 --- a/PLAN.md +++ b/PLAN.md @@ -47,7 +47,7 @@ par deux chemins qui ne se croisent jamais. │ GET /api/device/image/.bmp ← non devinable, immuable, cacheable ▼ ┌─────────────────────────────────────────────────────┐ - │ Next.js 16 (App Router, TS strict) trmnl.loxi.ch │ + │ Next.js 16 (App Router, TS strict) horaires.ita-ito.com │ │ │ │ lib/schedule/resolver.ts ← pur, sans I/O, 100 % testé │ lib/screen/viewmodel.ts ← Prisma → ScreenPayload (schema 1) @@ -250,7 +250,7 @@ e2e/*.spec.ts ← Playwright | `GET /api/device/image/.bmp` | — | l'image, `Cache-Control: immutable` | **Appairage** (README, pas de reflash) : maintenir le bouton ~5 s → portail captif → Wi-Fi → -*Advanced > Custom Server > Yes* → `https://trmnl.loxi.ch` **sans slash final**. +*Advanced > Custom Server > Yes* → `https://horaires.ita-ito.com` **sans slash final**. `refresh_rate` adaptatif, calculé à chaque `/api/display` : `refreshRateOpenSec` (600) si la boutique est ouverte ou ouvre dans l'heure, `refreshRateClosedSec` (7200) sinon, et raccourci @@ -314,7 +314,7 @@ change pas, badge + bouton « Réessayer ». ``` DATABASE_URL POSTGRES_USER / PASSWORD / DB -APP_DOMAIN # ex. trmnl.loxi.ch — callback OIDC + image_url ← À CONFIRMER +APP_DOMAIN # ex. horaires.ita-ito.com — callback OIDC + image_url ← À CONFIRMER NEXTAUTH_URL NEXTAUTH_SECRET AUTHENTIK_ISSUER # https://auth.loxi.ch/application/o// ← À CONFIRMER AUTHENTIK_CLIENT_ID AUTHENTIK_CLIENT_SECRET @@ -412,8 +412,8 @@ npm run lint && npm run typecheck && npm run test -- --coverage # 3. Contrat d'affichage, sans appareil npm run screen:preview # → aperçu 800×480 dans le navigateur -curl -s localhost:3000/api/display -H "Access-Token: " | jq -curl -s localhost:3000/api/device/image/.bmp -o /tmp/screen.bmp +curl -s localhost:3010/api/display -H "Access-Token: " | jq +curl -s localhost:3010/api/device/image/.bmp -o /tmp/screen.bmp file /tmp/screen.bmp # attendu : PC bitmap, 800 x 480 x 1 # 4. Non-régression horaires (le test qui compte) @@ -431,7 +431,7 @@ du jour, une modification faite depuis un téléphone apparaît au réveil suiva ## Points à confirmer -1. **Domaine public** de l'app sur le VPS (`trmnl.loxi.ch` ?) — nécessaire au callback OIDC +1. **Domaine public** de l'app sur le VPS (`horaires.ita-ito.com` ?) — nécessaire au callback OIDC et à l'`image_url` servie à l'appareil. 2. **Slug de l'application Authentik** et **nom du groupe admin**. 3. **Clé API `llk_…`** pour `api.loxi.ch`, et confirmation que l'instance est bien joignable diff --git a/lib/config.ts b/lib/config.ts index 4712c76..99aafb8 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -29,7 +29,7 @@ export function publicBaseUrl(request?: Request): string { return domain.startsWith('http') ? domain.replace(/\/$/, '') : `https://${domain}`; } - return 'http://localhost:3000'; + return 'http://localhost:3010'; } /** diff --git a/lib/screen/__snapshots__/viewmodel.test.ts.snap b/lib/screen/__snapshots__/viewmodel.test.ts.snap index 405f3d2..0bd45d7 100644 --- a/lib/screen/__snapshots__/viewmodel.test.ts.snap +++ b/lib/screen/__snapshots__/viewmodel.test.ts.snap @@ -8,7 +8,7 @@ exports[`buildScreenPayload > matches the frozen payload contract 1`] = ` "text_fr": "Fermeture exceptionnelle jeudi après-midi", }, "generated_at": "2026-09-22T12:00:00+02:00", - "logo_url": "https://trmnl.loxi.ch/brand/logo-eink.png", + "logo_url": "https://horaires.ita-ito.com/brand/logo-eink.png", "schema": 1, "shop_name": "ITA ITO", "today": { diff --git a/lib/screen/render.test.ts b/lib/screen/render.test.ts index 8b4150f..8b82657 100644 --- a/lib/screen/render.test.ts +++ b/lib/screen/render.test.ts @@ -9,7 +9,7 @@ const PAYLOAD: ScreenPayload = { schema: 1, generated_at: '2026-09-22T12:00:00+02:00', shop_name: 'ITA ITO', - logo_url: 'https://trmnl.loxi.ch/brand/logo-eink.png', + logo_url: 'https://horaires.ita-ito.com/brand/logo-eink.png', today: { date_fr: 'Mardi 22 septembre', date_en: 'Tuesday 22 September', diff --git a/lib/screen/viewmodel.test.ts b/lib/screen/viewmodel.test.ts index 1d00159..86c34de 100644 --- a/lib/screen/viewmodel.test.ts +++ b/lib/screen/viewmodel.test.ts @@ -37,7 +37,7 @@ function input(overrides: Partial[0]> = {} now: new Date('2026-09-22T10:00:00Z'), ctx: context(), shopName: 'ITA ITO', - logoUrl: 'https://trmnl.loxi.ch/brand/logo-eink.png', + logoUrl: 'https://horaires.ita-ito.com/brand/logo-eink.png', messages: [] as MessageCandidate[], ...overrides, }; diff --git a/package.json b/package.json index ee07b99..e702a42 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "dev": "next dev --port 3010", "build": "next build", - "start": "next start", + "start": "next start --port ${PORT:-3010}", "lint": "eslint . --max-warnings 0", "typecheck": "tsc --noEmit", "test": "vitest run", diff --git a/scripts/preview.ts b/scripts/preview.ts index 0807bcc..034bf8e 100644 --- a/scripts/preview.ts +++ b/scripts/preview.ts @@ -61,7 +61,7 @@ function payload(): ScreenPayload { now: new Date('2026-09-23T10:30:00Z'), ctx: CONTEXT, shopName: 'ITA ITO', - logoUrl: 'https://trmnl.loxi.ch/brand/logo-eink.png', + logoUrl: 'https://horaires.ita-ito.com/brand/logo-eink.png', messages: [ { textFr: 'Fermeture exceptionnelle jeudi matin',