Files
ita-ito-horaires/.env.example
T
vliaudatandClaude Opus 5 a9809f83ca chore: scaffold Next.js 16 admin app with ITA ITO design tokens
Set up the project skeleton for the ITA ITO opening-hours display admin:
Next.js 16 (App Router) with TypeScript in strict mode, Tailwind CSS 4,
Vitest, ESLint and a blocking CI workflow.

The design tokens are copied verbatim from the model_ita_ito project
(palette, Inter Variable + Source Serif 4, radii, dark theme) so the two
applications look like one family, as required by the spec.

ESLint is pinned to v9: eslint-config-next bundles a react plugin that
crashes on ESLint 10. The typed `consistent-type-imports` rule is left
out because `verbatimModuleSyntax` already enforces the same discipline
at compile time, without the cost of typed linting across the repo.

PLAN.md records the agreed architecture, including the decisions that
depart from the original spec — most importantly the move from BYOD to
a self-hosted BYOS server, which removes the TRMNL private plugin, the
Liquid template and the webhook entirely.

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

85 lines
4.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. Sert à trois choses :
# le callback OIDC, l'URL d'image envoyée à l'écran, et les liens du README.
APP_DOMAIN=trmnl.loxi.ch
# Port publié en local (dev). En production, Traefik s'en charge : aucun port publié.
APP_PORT=3000
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
# -----------------------------------------------------------------------------
# Authentification — Authentik (OIDC, Authorization Code + PKCE)
# -----------------------------------------------------------------------------
# Doit correspondre EXACTEMENT à l'`issuer` du document de découverte :
# https://auth.loxi.ch/application/o/<SLUG>/.well-known/openid-configuration
AUTHENTIK_ISSUER=https://auth.loxi.ch/application/o/CHANGEME/
AUTHENTIK_CLIENT_ID=
AUTHENTIK_CLIENT_SECRET=
# Groupe Authentik dont les membres sont administrateurs. Les autres utilisateurs
# authentifiés sont en lecture seule (rôle `viewer`).
AUTHENTIK_ADMIN_GROUP=horaires-admins
# URL publique complète de l'app. L'URI de redirection à déclarer dans Authentik est
# <NEXTAUTH_URL>/api/auth/callback/authentik
NEXTAUTH_URL=https://trmnl.loxi.ch
# Générer avec : openssl rand -base64 33
NEXTAUTH_SECRET=
# -----------------------------------------------------------------------------
# É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). À n'activer que si le
# firmware ESP32 échoue sur la chaîne TLS. Le jeton d'appareil circulerait alors
# en clair : il est distinct de tout autre secret et révocable depuis /admin/parametres.
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