# Déploiement en production

## Pré-requis serveur

- PHP 8.1+ avec extensions : `pdo_mysql`, `mbstring`, `bcmath` (montants
  financiers en arithmétique exacte, voir `FactureService`), `fileinfo`
  (validation MIME réelle des uploads, voir `FileUploader`).
- MySQL/MariaDB 8+, un compte root (ou privilégié) dédié au provisioning
  (création de base + utilisateur par établissement), distinct du compte
  applicatif `ADMIN_DB_*`.
- Un serveur web (Apache ou Nginx) — **pas** `php -S`, qui est strictement un
  outil de développement local (voir `public/router-dev.php`).

## Topologie des deux espaces

`admin.newfarah` et `app.newfarah` sont deux front controllers séparés
(`public/admin/index.php`, `public/app/index.php`) qui partagent les mêmes
assets statiques (`public/assets/`, sibling des deux). La résolution
multi-tenant d'`app.newfarah` se fait sur le **Host HTTP complet**
(`TenantResolver::resolveBySousDomaine()` compare directement contre
`etablissements.sous_domaine`, qui stocke déjà le nom d'hôte complet de
chaque établissement, ex. `cliniquefarah.app.newfarah.example.com`) — donc
`app.newfarah` a besoin d'un vhost qui accepte **n'importe quel sous-domaine**
de ce domaine, pas d'un vhost figé.

### Exemple Apache

```apache
<VirtualHost *:443>
    ServerName admin.newfarah.example.com
    DocumentRoot /var/www/newfarahgroupe/public/admin
    Alias /assets /var/www/newfarahgroupe/public/assets

    <Directory /var/www/newfarahgroupe/public/admin>
        AllowOverride All
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.php [QSA,L]
    </Directory>
    <Directory /var/www/newfarahgroupe/public/assets>
        Require all granted
    </Directory>

    SSLEngine on
    # ... certificats ...
</VirtualHost>

<VirtualHost *:443>
    ServerName app.newfarah.example.com
    ServerAlias *.app.newfarah.example.com
    DocumentRoot /var/www/newfarahgroupe/public/app
    Alias /assets /var/www/newfarahgroupe/public/assets

    <Directory /var/www/newfarahgroupe/public/app>
        AllowOverride All
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.php [QSA,L]
    </Directory>
    <Directory /var/www/newfarahgroupe/public/assets>
        Require all granted
    </Directory>

    SSLEngine on
    # certificat wildcard *.app.newfarah.example.com
</VirtualHost>
```

### Exemple Nginx (bloc `app.newfarah`, adapter pour `admin.newfarah`)

```nginx
server {
    listen 443 ssl;
    server_name app.newfarah.example.com *.app.newfarah.example.com;
    root /var/www/newfarahgroupe/public/app;

    location /assets/ {
        alias /var/www/newfarahgroupe/public/assets/;
    }

    location / {
        try_files $uri /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
```

Un certificat **wildcard** (`*.app.newfarah.example.com`) est nécessaire côté
`app.newfarah` puisque chaque établissement obtient son propre sous-domaine
au moment de sa création dans `admin.newfarah`.

## Dossiers à protéger / permissions

- `storage/` (logs, cache du rate limiter, uploads) : accessible en écriture
  par l'utilisateur du serveur web, **jamais** exposé publiquement (il est en
  dehors de `public/`, donc déjà hors de portée d'un accès web direct — ne pas
  créer d'alias vers lui).
- `.env` : permissions restrictives (`chmod 640`), jamais committé.

## Variables d'environnement (voir `.env.example`)

| Variable | Rôle |
|---|---|
| `APP_ENCRYPTION_KEY` | Chiffre `etablissements.db_password_chiffre`. Générer avec `php -r "echo base64_encode(random_bytes(32));"` et **ne jamais régénérer après mise en prod** (les mots de passe déjà chiffrés deviendraient illisibles). |
| `ADMIN_DB_*` | Compte applicatif normal de la base admin (catalogue établissements/modules), droits limités à cette seule base. |
| `PROVISIONING_DB_*` | Compte privilégié utilisé **uniquement** par `EtablissementProvisioningService` pour créer une base + un utilisateur MySQL par établissement. À restreindre au strict nécessaire (`CREATE`, `GRANT OPTION` limité, pas de superuser applicatif partagé). |
| `TENANT_DB_HOST` / `TENANT_SOUS_DOMAINE_SUFFIX` | Hôte MySQL où sont créées les bases tenant, et suffixe ajouté au slug de chaque nouvel établissement pour former son sous-domaine complet. |
| `SESSION_COOKIE_SECURE` | Doit rester `true` en production (cookies HTTPS uniquement). |

## Déploiement (mise à jour d'une version existante)

```bash
composer install --no-dev --optimize-autoloader
php bin/migrate.php admin
# Pour chaque établissement existant, appliquer les nouvelles migrations tenant :
php bin/migrate.php tenant --host=... --database=newfarah_tenant_xxx --username=...
php bin/build-assets.php   # si public/assets/vendor/ a changé
```

Aucune migration de schéma tenant n'est automatique au déploiement : la liste
des bases tenant existantes vient de `SELECT db_host, db_port, db_name,
db_username FROM etablissements` dans la base admin — scripter cette boucle
au moment voulu plutôt que de l'automatiser à l'aveugle (un établissement en
maintenance ne doit pas forcément être migré en même temps que les autres).

## Première installation (nouvelle plateforme, aucun établissement encore créé)

```bash
composer install --no-dev --optimize-autoloader
cp .env.example .env   # renseigner toutes les valeurs, surtout APP_ENCRYPTION_KEY
php bin/migrate.php admin
php bin/build-assets.php
# Créer le premier compte super-admin directement en base (aucune UI pour
# le tout premier compte, par design — voir PlatformAdminRepository) :
php -r '
require "vendor/autoload.php";
echo (new NewFarah\Core\Auth\PasswordHasher())->hash("mot-de-passe-a-changer-immediatement");
'
# puis INSERT INTO platform_admins (nom, email, mot_de_passe_hash, actif) VALUES (...);
```

Les établissements suivants se créent depuis l'UI `admin.newfarah`
(provisioning automatique de leur base).

## Sauvegardes

Voir `BACKUP.md`.

## CI/CD

`.github/workflows/ci.yml` exécute, sur chaque push/PR : lint PHP (`php -l`),
`composer validate`, puis la suite PHPUnit complète contre un vrai MySQL
(service container), avec les 3 schémas de test appliqués automatiquement
(`newfarah_admin_test`, `newfarah_tenant_test`, `newfarah_legacy_test`).
