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
85 lines
4.3 KiB
Bash
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
|