Aller au contenu

Déploiement

La production tourne sur Dokploy, en Docker Compose, à partir de la branche main du dépôt. Pas de base de données ni de volume : un seul conteneur sert le client et le WebSocket.

flowchart LR
  DEV["git push origin main"] --> GH["GitHub<br/>DuncanCos/orapamine"]
  GH -->|"auto-deploy"| DOK["Dokploy<br/>projet orapamine"]
  DOK -->|"docker compose build + up"| CTN["Conteneur orapamine<br/>port 8080"]
  USER["Navigateur"] -->|"HTTPS / WSS"| PROXY["Reverse proxy Dokploy<br/>Let's Encrypt"]
  PROXY -->|"orapamine.dunkan.xyz"| CTN

Image Docker

Dockerfile multi-étapes :

Étape Image de base Ce qu'elle fait Produit
wasm-builder rust:1-slim-bookworm Ajoute la cible wasm32-unknown-unknown, installe wasm-bindgen-cli 0.2.127, compile orapa-wasm, génère les bindings (--target web --typescript) /app/wasm-out
web-builder node:22-slim npm ci, copie les bindings dans web/src/wasm, puis npx tsc -b && npx vite build (sans build:wasm, déjà fait) /app/web/dist
server-builder rust:1-slim-bookworm cargo build --release -p orapa-server binaire orapa-server
runtime debian:bookworm-slim ca-certificates, utilisateur système orapa, copie du binaire et de web/dist image finale

Image finale :

  • USER orapa, WORKDIR /app
  • ENV ORAPA_STATIC_DIR=/app/web/dist, ENV PORT=8080, EXPOSE 8080
  • ENTRYPOINT ["./orapa-server"]

.dockerignore exclut target/, web/node_modules/, web/dist/, web/src/wasm/, .git/ et *.md.

Note

data/pieces.json est embarqué dans les binaires à la compilation : il n'est pas lu à l'exécution, et toute modification demande une reconstruction de l'image.

docker-compose.yml

services:
  orapamine:
    build: .
    ports:
      - "8080:8080"
    environment:
      - RUST_LOG=info
    restart: unless-stopped
Variable Valeur dans l'image / le compose Rôle
PORT 8080 (Dockerfile) Port d'écoute
ORAPA_STATIC_DIR /app/web/dist (Dockerfile) Fichiers statiques du client
RUST_LOG info (compose) Niveau de log

Test local de l'image : docker compose up --build, puis http://localhost:8080.

Mapping de port

ports: "8080:8080" publie aussi le port 8080 directement sur l'hôte. Le domaine de production passe par le reverse proxy de Dokploy, qui cible le service orapamine sur le port 8080. Ce mapping ne lui est donc pas nécessaire.

Configuration Dokploy

Paramètre Valeur
Projet orapamine (environnement production)
Service Docker Compose orapamine
Source GitHub DuncanCos/orapamine, branche main
Fichier compose ./docker-compose.yml
Domaine https://orapamine.dunkan.xyz, chemin /, vers le service orapamine, port 8080
HTTPS Certificat Let's Encrypt
Auto-deploy Activé : chaque push sur main redéploie

Aucune variable d'environnement n'est nécessaire en plus de celles de l'image et du compose. Le WebSocket utilise le même domaine (wss://orapamine.dunkan.xyz/ws).

Redéployer

git push origin main

L'auto-deploy de Dokploy reconstruit l'image et relance le conteneur.

Projet orapamine, puis service compose orapamine, puis Deploy. Les journaux de build et d'exécution sont consultables depuis le service.

Avec le wrapper dokploy (clé dans ~/.dokploy.env, à ne jamais afficher) :

# Retrouver le composeId
dokploy GET project.all | jq -r '.[] | select(.name=="orapamine") | .environments[].compose[] | "\(.name) \(.composeId)"'

# Lancer un déploiement
dokploy POST compose.deploy '{"composeId":"<composeId>"}'

# Suivre le statut (done, running, error…)
dokploy GET "compose.one?composeId=<composeId>" | jq -r .composeStatus

Warning

La réponse de compose.one contient aussi la configuration complète du service (champ env compris). Filtrer avec jq plutôt que de l'afficher en entier.

Vérifier après un déploiement

  • composeStatus vaut done dans Dokploy.
  • https://orapamine.dunkan.xyz répond (HTTP 200 sur /, le client se charge).
  • Créer une partie solo : si le plateau et les tirs répondent, le WebSocket fonctionne.

Un redéploiement coupe les parties en cours

Les parties ne vivent qu'en mémoire du processus serveur. Chaque redéploiement (donc chaque push sur main) redémarre le conteneur et perd toutes les parties en cours. Les clients tentent de se reconnecter et reçoivent reconnect_failed. Il n'y a pas d'endpoint de santé dédié : / sert index.html.