Files
donut-gestprojet/DEPLOY.md
T
Arnaud 3a32e93ab1 feat: initial API skeleton — routes, controllers, migrations, deploy docs
Implements the JSON API for Le Donut Infolab per INSTRUCTIONS.md.

Schema (migrations/001-002):
  - 8 tables: categories, members, projects, project_members, roadmap_steps,
    discussions, resources, activity_log
  - 6 categories seeded with colors / dimensions (social, ecologique, transversal)
  - FK contraintes + soft delete sur projects, CHECK sur progress_percent

Bare PHP framework (src/):
  - Front controller public/index.php + router :params + auth flag par route
  - PSR-4 autoload sans Composer (bootstrap.php)
  - Database PDO singleton + helpers fetchOne/All/insert/update/delete
  - Validator fluent -> 422 {error: validation_failed, fields: {...}}
  - Bearer token admin (hash_equals) sur POST/PATCH/DELETE
  - Slugger avec unicité (suffixe -2, -3...) via intl Transliterator
  - ActivityLogger best-effort sur activity_log
  - Erreurs uniformes : 400/401/404/405/409/422/500 + CORS configurable

10 contrôleurs (Health, Categories, Members, Projects, ProjectMembers,
Roadmap, Discussions, Resources, Activity, Stats). Transitions de statut
loggées (created_project, completed_step, joined, etc.).

Cible prod :
  - Dockerfile (php:8.3-apache + pdo_mysql + intl + opcache)
  - docker-compose.yml sur réseau donut-net, bind 127.0.0.1:8003
  - caddy.conf pour infolab.ledonut-marseille.org
  - migrations/apply.sh idempotent (table schema_migrations)
  - DEPLOY.md avec commandes SSH exactes

Tests :
  - tests/smoke.sh exerce 10 endpoints clés, body envoyé via stdin
    (--data-binary @-) pour gérer l UTF-8 sous Git Bash Windows.

Vérifié localement sous Laragon Apache + MySQL 8.4 : 10/10 OK + cas
négatifs (401, 404, 405, 409, 422) conformes au contrat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:55:28 +02:00

6.2 KiB

DEPLOY — Le Donut Infolab

Procédure exacte de déploiement sur le serveur Donut Marseille.

  • Hôte : arnaud.donut@82.66.40.209
  • OS : Ubuntu 26.04
  • Sous-domaine : infolab.ledonut-marseille.org
  • Conteneur PHP : donut-infolab (UID 33 = www-data côté Apache)
  • BDD : conteneur existant donut-mariadb, réseau Docker donut-net
  • Reverse proxy : Caddy natif sur l'hôte

⚠️ Pré-requis DNS : créer chez OVH un record A infolab.ledonut-marseille.org → 82.66.40.209 avant le reload Caddy (sinon échec ACME challenge).


0. Pré-requis vérifiés une fois

ssh arnaud.donut@82.66.40.209

# Le réseau donut-net existe
docker network ls | grep donut-net
# Le mariadb tourne et est joignable depuis donut-net
docker ps | grep donut-mariadb
# /srv/donut/secrets/mariadb.env est en place et lisible par root
sudo ls -l /srv/donut/secrets/mariadb.env
# Le group donut-admins existe
getent group donut-admins

1. Copier le projet vers le serveur

Depuis ta machine de dev (à la racine du repo) :

# Code app -> /srv/donut/web/infolab/   (bind-mount dans le conteneur, UID 33)
rsync -avz --delete \
    --exclude '.git' --exclude '.idea' --exclude '.env' --exclude 'tests' \
    --exclude 'docker-compose.yml' --exclude 'Dockerfile' --exclude 'caddy.conf' \
    --exclude 'migrations' --exclude 'DEPLOY.md' --exclude 'INSTRUCTIONS.md' \
    ./ arnaud.donut@82.66.40.209:/tmp/infolab-app/

# Fichiers d'orchestration -> /srv/donut/compose/infolab/
rsync -avz \
    docker-compose.yml Dockerfile caddy.conf README.md DEPLOY.md .env.example \
    migrations \
    arnaud.donut@82.66.40.209:/tmp/infolab-compose/

Puis sur le serveur :

ssh arnaud.donut@82.66.40.209

# Code app (UID 33 = www-data du conteneur Apache)
sudo mkdir -p /srv/donut/web/infolab
sudo rsync -a --delete /tmp/infolab-app/ /srv/donut/web/infolab/
sudo chown -R 33:33 /srv/donut/web/infolab
sudo find /srv/donut/web/infolab -type d -exec chmod 755 {} \;
sudo find /srv/donut/web/infolab -type f -exec chmod 644 {} \;

# Orchestration (root:donut-admins, lisible par les admins)
sudo mkdir -p /srv/donut/compose/infolab
sudo rsync -a /tmp/infolab-compose/ /srv/donut/compose/infolab/
sudo chown -R root:donut-admins /srv/donut/compose/infolab
sudo chmod 750 /srv/donut/compose/infolab
sudo chmod 640 /srv/donut/compose/infolab/*

# Cleanup tmp
rm -rf /tmp/infolab-app /tmp/infolab-compose

2. Créer les secrets /srv/donut/secrets/infolab.env

ssh arnaud.donut@82.66.40.209

DB_PASS="$(openssl rand -hex 24)"
ADMIN_TOKEN="$(openssl rand -hex 32)"

sudo tee /srv/donut/secrets/infolab.env > /dev/null <<EOF
DB_HOST=donut-mariadb
DB_PORT=3306
DB_NAME=infolab
DB_USER=infolab
DB_PASS=$DB_PASS

ADMIN_TOKEN=$ADMIN_TOKEN

CORS_ORIGIN=https://infolab.ledonut-marseille.org
APP_ENV=production
APP_DEBUG=false
EOF

sudo chown root:donut-admins /srv/donut/secrets/infolab.env
sudo chmod 640 /srv/donut/secrets/infolab.env

# Note les 2 secrets quelque part de sûr (gestionnaire de mdp) :
sudo cat /srv/donut/secrets/infolab.env | grep -E '^(ADMIN_TOKEN|DB_PASS)='

3. Créer la BDD + le user MariaDB + appliquer les migrations

# Le script apply.sh fait tout : crée la base, le user, applique les .sql
# Idempotent : peut être ré-exécuté sans danger (table schema_migrations).
sudo /srv/donut/compose/infolab/migrations/apply.sh

Tu dois voir :

  • apply 001_init_schema.sql
  • apply 002_seed_categories.sql
  • résumé avec categories_count = 6

4. Build + start du conteneur

cd /srv/donut/compose/infolab
sudo docker compose --env-file /srv/donut/secrets/infolab.env up -d --build

# Vérifie qu'il tourne et écoute en local sur 127.0.0.1:8003
docker ps | grep donut-infolab
ss -ltn | grep 8003

# Sonde de vie via le port mappé en local
curl -s http://127.0.0.1:8003/api/health
# {"status":"ok","time":"..."}

Si la sonde renvoie degraded, vérifier les logs :

docker logs --tail 100 donut-infolab

5. Vhost Caddy

Le fichier caddy.conf a été copié à /srv/donut/compose/infolab/caddy.conf. Vérifier qu'il est bien import-é par /etc/caddy/Caddyfile :

grep -n 'infolab' /etc/caddy/Caddyfile

# Si manquant, ajouter en fin de Caddyfile :
echo 'import /srv/donut/compose/infolab/caddy.conf' | sudo tee -a /etc/caddy/Caddyfile

# Valider la syntaxe puis recharger
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Caddy va négocier le certificat Let's Encrypt (à condition que le DNS A record soit en place — voir avertissement en tête de fichier).


6. Vérification finale

# Depuis la machine de dev (TLS terminé par Caddy)
curl -s https://infolab.ledonut-marseille.org/api/health
curl -s https://infolab.ledonut-marseille.org/api/categories | head -c 400

# Smoke tests complets (lit ADMIN_TOKEN depuis le secret côté serveur)
ssh arnaud.donut@82.66.40.209 'sudo bash -c "
  TOKEN=\$(grep ^ADMIN_TOKEN= /srv/donut/secrets/infolab.env | cut -d= -f2-)
  INFOLAB_URL=https://infolab.ledonut-marseille.org ADMIN_TOKEN=\$TOKEN \
      /srv/donut/compose/infolab/tests/smoke.sh
"'

Si tout est vert : OK : 11 / FAIL : 0.


7. Cycle de mise à jour (pour plus tard)

# Code app
rsync -avz --delete --exclude '.git' --exclude '.env' \
    public/ src/ arnaud.donut@82.66.40.209:/srv/donut/web/infolab/

# Si changement Dockerfile / docker-compose / migrations :
rsync -avz docker-compose.yml Dockerfile migrations/ \
    arnaud.donut@82.66.40.209:/srv/donut/compose/infolab/

ssh arnaud.donut@82.66.40.209 << 'EOF'
  sudo /srv/donut/compose/infolab/migrations/apply.sh   # nouvelle migration éventuelle
  cd /srv/donut/compose/infolab
  sudo docker compose --env-file /srv/donut/secrets/infolab.env up -d --build
  sudo chown -R 33:33 /srv/donut/web/infolab
EOF

Points à valider une fois en prod

  • DNS A infolab.ledonut-marseille.org → 82.66.40.209 ajouté chez OVH
  • Certificat Let's Encrypt négocié OK (caddy.log côté hôte)
  • ADMIN_TOKEN archivé dans le gestionnaire de mots de passe partagé
  • tests/smoke.sh vert depuis l'extérieur
  • Conteneur donut-infolab visible dans Dozzle (logs Apache + PHP via stderr)
  • Pas de fuite : curl -I https://infolab.ledonut-marseille.org/.env → 403/404