Production

Le backend se déploie séparément : bunx convex deploy (+ convex env set sur le déploiement de prod). Le frontend est une image Docker autonome :

docker build -t fabulous-crm .
docker run -d -p 8099:80 -e VITE_CONVEX_URL=https://<deployment>.convex.cloud fabulous-crm

Image multi-stage : build bun (tsc + vite) → caddy:2-alpine servant dist/ (cache immutable sur /assets, no-cache sur le HTML et env.js, fallback SPA).

Après une mise à jour

Déploiement local via Docker (build + run)

Procédure complète pour lancer l'image en local et arriver jusqu'à l'assistant /setup.

  1. Renseigner l'environnement du déploiement Convex (côté serveur — pas l'environnement du conteneur), SETUP_TOKEN en tête car exigé au premier démarrage :

    bunx convex env set SETUP_TOKEN $(openssl rand -hex 32)         # requis au 1er démarrage
    bunx convex env set SITE_URL https://crm.example.com            # requis (auth) : origine SPA + base des liens email/consentement
    bunx convex env set BETTER_AUTH_SECRET $(openssl rand -hex 32)  # requis en prod : signature des sessions
    bunx convex env set BREVO_API_KEY <clé>                         # requis pour les emails et SMS
    bunx convex env set EMAIL_SENDER_EMAIL noreply@example.com      # expéditeur Brevo vérifié
    bunx convex env set EMAIL_SENDER_NAME "CRM"
    bunx convex env set BREVO_SMS_SENDER "CRM"                      # requis pour les SMS (≤ 11 car. alphanum.)
    bunx convex env set BREVO_WEBHOOK_SECRET $(openssl rand -hex 32) # optionnel : désinscription SMS via réponse STOP
    bunx convex env set FHIR_API_KEY <clé>                          # optionnel : vérif. RPPS (Annuaire Santé)

    Liste complète : Variables d'environnement — Backend.

  2. Construire l'image dev (Dockerfile.local, base oven/bun — c'est elle qui embarque bun ; l'image de production caddy:2-alpine n'a pas bun) :

    docker build -f Dockerfile.local --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t wap-crm:dev .
  3. Lancer le conteneur (nom wap-crm-dev, réseau proxy, dossier projet monté en volume) :

    docker rm -f wap-crm-dev || true && \
    docker run -d --name wap-crm-dev --network proxy -v "$PWD":/app wap-crm:dev

    Aucun port publié : le reverse proxy de l'hôte route vers wap-crm-dev:4202.

  4. Entrer dans le conteneur, installer, démarrer (l'entrypoint est inerte, tail -f) :

    docker exec -it wap-crm-dev bash
    bun install
    bun run dev            # convex dev + vite (`bunx convex login` une fois)

    Les VITE_* (dont VITE_CONVEX_URL) sont lues depuis .env.local monté dans le conteneur — pas d'injection -e, qui ne sert qu'à l'image de production.

  5. Ouvrir /setup, saisir le SETUP_TOKEN, terminer l'assistant : le premier administrateur est créé et connecté, puis l'assistant se verrouille.

Deux niveaux d'environnement distincts. Le conteneur dev lit les VITE_* depuis .env.local (monté en volume). SETUP_TOKEN et les autres secrets (BREVO_API_KEY, expéditeur…) vivent sur le déploiement Convex (bunx convex env set) — les placer dans l'environnement du conteneur n'a aucun effet, car l'assistant vérifie le jeton côté Convex.

Caddy & reverse proxy

Le Caddy embarqué dans l'image (docker/Caddyfile) n'est pas un reverse proxy : c'est un simple serveur statique sur :80 (fallback SPA vers index.html, Cache-Control: immutable sur /assets/*, no-cache sur le HTML et env.js, en-têtes de sécurité). Rien n'est proxifié car le navigateur parle directement au déploiement Convex (VITE_CONVEX_URL) — aucun trafic API ne transite par le conteneur.

Pour exposer le conteneur derrière le reverse proxy de l'hôte (TLS, nom de domaine), il suffit de pointer vers le port publié (8099 dans l'exemple ci-dessus). Avec Caddy sur l'hôte :

crm.example.com {
	reverse_proxy 127.0.0.1:8099
}

Aucune directive particulière n'est nécessaire : pas de WebSocket ni de chemin d'API côté conteneur (le WebSocket Convex va directement du navigateur vers *.convex.cloud). Penser à aligner SITE_URL (env Convex) sur l'URL publique pour que l'authentification, les liens de connexion et de consentement soient corrects.