Files
vliaudatandClaude Opus 5 13abcf3242 chore: drop the plain-HTTP route now the panel is proven on TLS
The transport log settles it: the device reaches the server over https
and validates the Let's Encrypt chain without trouble. The dedicated
port, the Traefik entrypoint and the plain-HTTP router were all built on
a hypothesis the evidence has since refused.

DEVICE_ALLOW_HTTP goes back to false, so the device routes refuse an
unencrypted request again.

DEPLOY.md is rewritten around the real cause — the firmware does not
follow redirects, and a trailing slash was answered with a 308 — and
records the three hypotheses that were wrong, so nobody spends another
evening on them. It also names the trap that made this slow: Traefik,
a production Next server and tcpdump were each read as saying "no
traffic" when all three were simply silent by default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
2026-09-21 23:19:45 +02:00

105 lines
5.3 KiB
Bash

# =============================================================================
# ITA ITO — Panneau d'administration des horaires (écran e-ink TRMNL, BYOS)
# Copiez ce fichier en `.env` et renseignez les valeurs. `.env` n'est JAMAIS commité.
# =============================================================================
# -----------------------------------------------------------------------------
# Application
# -----------------------------------------------------------------------------
# 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.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
# -----------------------------------------------------------------------------
# Base de données (PostgreSQL 16)
# -----------------------------------------------------------------------------
POSTGRES_DB=horaires
POSTGRES_USER=horaires
POSTGRES_PASSWORD=
DATABASE_URL=postgresql://horaires:CHANGEME@db:5432/horaires?schema=public
# Only needed to run the integration tests. They truncate every table, so this
# must never point at a database holding anything you want to keep.
TEST_DATABASE_URL=
# -----------------------------------------------------------------------------
# Authentification — Authentik (OIDC, Authorization Code + PKCE)
# -----------------------------------------------------------------------------
# Noms de variables imposés par Auth.js v5, identiques au projet api_llm_loxi :
# le provider est découvert automatiquement à partir d'AUTH_AUTHENTIK_*.
#
# URI de redirection à déclarer dans Authentik (correspondance stricte) :
# <AUTH_URL>/api/auth/callback/authentik
AUTH_URL=https://horaires.ita-ito.com
# Générer avec : openssl rand -base64 33
AUTH_SECRET=
AUTH_AUTHENTIK_ID=
AUTH_AUTHENTIK_SECRET=
# Doit correspondre EXACTEMENT à l'`issuer` du document de découverte, slash
# final compris :
# https://auth.loxi.ch/application/o/<SLUG>/.well-known/openid-configuration
AUTH_AUTHENTIK_ISSUER=https://auth.loxi.ch/application/o/horaires-ita-ito/
# Libellé du bouton sur la page de connexion.
AUTHENTIK_DISPLAY_NAME=Loxi
# Groupe Authentik dont les membres sont administrateurs. Tout autre utilisateur
# authentifié est en lecture seule (rôle `viewer`).
AUTHENTIK_ADMIN_GROUP=horaires-admins
# -----------------------------------------------------------------------------
# Écran e-ink — API appareil (BYOS)
# -----------------------------------------------------------------------------
# Format d'image servi à l'appareil. Le firmware Seeed référence des .bmp ;
# basculer sur `png` si l'appareil refuse le BMP.
DEVICE_IMAGE_FORMAT=bmp
# Autorise l'appareil à appeler l'API en clair (HTTP). Les routes de l'écran
# REFUSENT une requête non chiffrée tant que ce réglage vaut autre chose que
# « true » : c'est une décision explicite, pas un effet de bord d'une
# configuration de proxy.
#
# À activer seulement si le firmware ESP32 s'avère incapable de valider la
# chaîne TLS. Celui du kit Seeed (1.5.12) y parvient sans problème : ne pas
# supposer le contraire devant une panne de connexion, voir DEPLOY.md.
# Le jeton d'appareil circule alors en clair : il ne sert à rien d'autre et se
# révoque depuis /admin/parametres. Voir DEPLOY.md.
DEVICE_ALLOW_HTTP=false
# Intervalles de réveil, en secondes. Court quand la boutique est ouverte ou sur le
# point de changer d'état, long la nuit et les jours de fermeture.
DEVICE_REFRESH_OPEN_SEC=600
DEVICE_REFRESH_CLOSED_SEC=7200
# -----------------------------------------------------------------------------
# Traduction FR -> EN — api.loxi.ch (projet api_llm_loxi)
# -----------------------------------------------------------------------------
# ATTENTION : cette API n'est ni Anthropic- ni OpenAI-compatible.
# Contrat réel : POST {TRANSLATION_API_URL}/api/generate {"model_id": <int>, "prompt": "..."}
# -> 200 {"stdout": "...", "stderr": "...", "exit_code": 0}
# Un échec du CLI renvoie quand même HTTP 200 : c'est `exit_code` qui fait foi.
TRANSLATION_API_URL=https://api.loxi.ch
# Clé API créée depuis la page « API keys » du dashboard api-llm-loxi (format llk_...).
TRANSLATION_API_KEY=
TRANSLATION_API_FLAVOR=loxi
# Nom du modèle, résolu en `model_id` au démarrage via GET /api/models.
# L'alias `haiku` pointe toujours vers la dernière version.
TRANSLATION_MODEL_NAME=haiku
# Le backend lance réellement le CLI Claude Code : prévoir large.
TRANSLATION_TIMEOUT_MS=30000
# -----------------------------------------------------------------------------
# Jours fériés — OpenHolidays (aucune clé requise)
# -----------------------------------------------------------------------------
HOLIDAYS_API_URL=https://openholidaysapi.org
# -----------------------------------------------------------------------------
# Déploiement (production, docker-compose.prod.yml)
# -----------------------------------------------------------------------------
TRAEFIK_NETWORK=web
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_CERTRESOLVER=myresolver