# Deploiement en preproduction (recette) — hebergement OVH reel

Ce runbook complete `docs/deploiement-ovh.md` (regles generales mutualise OVH)
avec les informations **specifiques a l'hebergement reel du client** et une
suite d'etapes concretes pour mettre en ligne une **preproduction de recette**.

> **Etat reel (deploiement du 2026-07-07) : cette preproduction a ete
> deployee pour de vrai** sur `new.biome.immo`. La section 0 juste en dessous
> liste tous les pieges reellement rencontres, avec les commandes exactes qui
> les evitent ou les corrigent. **A lire avant toute nouvelle mise a jour ou
> avant le passage en production definitive.** Les sections 1 a 12 qui suivent
> ont ete corrigees pour rester coherentes avec ce retour d'experience, mais
> en cas de doute, **la section 0 fait foi**.

Infos hebergement reel (deploiement du 2026-07-07) :

- Mutualise OVH.
- Serveur SSH : `ssh01.cluster126.gra` — connexion `ssh biomein@ssh01.cluster126.gra`.
- Compte d'hebergement : identifiant `biomein`.
- Dossier du projet Laravel : `/homez.172/biomein/biomewebsite` (chemin
  ABSOLU a utiliser systematiquement, voir section 0.5). Le dossier servi au
  web est `/homez.172/biomein/biomewebsite/public`.
- Un site WordPress de **production** existe deja dans `~/www` sur ce meme
  compte hebergement (domaine principal `biome.immo`) : **ne jamais y
  toucher**, il n'a aucun rapport avec ce projet Laravel qui vit dans son
  propre dossier `biomewebsite/` (voir section 0.9).
- Domaine de la preproduction : **new.biome.immo** (sous-domaine dedie,
  distinct du domaine principal `biome.immo` qui sert le WordPress).
- SSL Let's Encrypt actif sur `new.biome.immo`.
- PHP global du compte hebergement = **7.4** (comme documente plus bas : on
  force 8.3 pour ce site precis, voir section 0.2).

Note historique : ce document a d'abord ete redige **avant** ce deploiement
reel, sur la base d'hypotheses (sous-domaine `recette.biome.immo`, serveur
`cluster026`, dossier `www/recette-biome/`). Ces hypotheses se sont revelees
fausses sur ce cluster et ont ete corrigees partout dans ce document. Si vous
retrouvez encore `recette.biome.immo`, `cluster026` ou `www/recette-biome/`
ailleurs (autre doc, script...), c'est un oubli a corriger.

## 0. Retour d'experience reel — pieges OVH (a lire avant tout deploiement ou mise a jour)

Cette section rassemble, de facon operationnelle, tous les pieges reellement
rencontres lors du premier deploiement de la preproduction sur `new.biome.immo`
(cluster `ssh01.cluster126.gra`). Chaque point donne la commande exacte a
utiliser.

### 0.1 Le PHP CLI 8.3 n'est pas dans le PATH

Sur ce cluster, ni `php`, ni `php8.3` ne pointent vers PHP 8.3 en ligne de
commande : `php` tout court = PHP **7.4** (le PHP global du compte). Les
raccourcis `php8.3` / `php8.2` **ne fonctionnent pas tels quels** ici : ce
sont des chemins absolus qu'il faut utiliser.

Chemins reels constates :

```bash
/usr/local/php8.3/bin/php -v
/usr/local/php8.2/bin/php -v
```

Toutes les commandes `artisan` et `composer` de ce document doivent donc
utiliser le chemin absolu, par exemple :

```bash
/usr/local/php8.3/bin/php artisan migrate --force
/usr/local/php8.3/bin/php /homez.172/biomein/biomewebsite/composer.phar install --no-dev --optimize-autoloader
```

Astuce de confort pour la session SSH en cours (ne modifie rien de permanent
sur le serveur) :

```bash
alias php83=/usr/local/php8.3/bin/php
php83 artisan --version
```

### 0.2 Version PHP du site web : fichier `.ovhconfig` dans `public/`

Contrairement a une premiere hypothese (fichier a la racine du projet, a cote
de `public/`), le fichier `.ovhconfig` doit etre depose **dans le dossier
servi au web**, donc `biomewebsite/public/.ovhconfig`. Sinon le site web
tourne en PHP 7.4 et Laravel 12 refuse de demarrer (erreur type "requires
PHP >= 8.2").

Contenu exact du fichier (les 3 lignes sont necessaires) :

```ini
app.engine=php
app.engine.version=8.3
environment=production
```

Chemin exact sur cette preproduction :
`/homez.172/biomein/biomewebsite/public/.ovhconfig`.

### 0.3 Ne jamais lancer `php artisan config:cache` sur cet hebergement

Constat reel : `php artisan config:cache` provoque une **erreur 500** sur ce
mutualise OVH (cause exacte non identifiee a ce jour — a investiguer avant le
passage en production definitive, voir section 12). Le site fonctionne
normalement **sans** cache de configuration.

A faire a la place, systematiquement :

```bash
/usr/local/php8.3/bin/php artisan optimize:clear
/usr/local/php8.3/bin/php artisan route:cache
/usr/local/php8.3/bin/php artisan view:cache
```

`route:cache` et `view:cache` fonctionnent normalement et peuvent rester
actifs. **Ne jamais executer `config:cache` (ni `artisan optimize`, qui
l'inclut) sur ce serveur.** Si le site renvoie une 500 juste apres un
deploiement, verifier en premier qu'aucun cache de configuration n'a ete
genere par erreur (fichier `bootstrap/cache/config.php` present = a
supprimer), puis relancer `optimize:clear`.

### 0.4 Livewire / Filament : le JS servi par PHP est tronque par OVH

Symptome : l'admin Filament (`/admin`) n'arrive pas a se connecter (boucle de
chargement, erreur navigateur type `NS_ERROR_NET_PARTIAL_TRANSFER`), sans
erreur Laravel visible dans les logs. Cause reelle : OVH compresse (gzip) le
fichier `livewire.min.js` servi dynamiquement par PHP et envoie un en-tete
`Content-Length` incoherent, ce qui tronque le fichier cote navigateur. Les
regles `.htaccess` classiques (`no-gzip`, `no-transform`) ne suffisent **pas**
a corriger ca sur ce cluster.

**Solution retenue : servir ce JS en fichier statique**, en copiant les
assets publies de Livewire dans `public/vendor/livewire` :

```bash
mkdir -p public/vendor/livewire
cp -r vendor/livewire/livewire/dist/* public/vendor/livewire/
```

Livewire detecte automatiquement le manifest publie a cet endroit et sert
`/vendor/livewire/livewire.min.js` en fichier statique via Apache (donc plus
de gzip/Content-Length casse par PHP).

**A refaire a chaque mise a jour de la librairie Livewire** (donc a chaque
`composer update` qui touche `livewire/livewire`) — c'est pour ca que cette
etape figure dans la sequence de deploiement standard (section 0.7). Si un
jour `/admin` ne se connecte plus sans raison apparente, **c'est la premiere
chose a verifier**.

### 0.5 Chemins et `$HOME` instables : toujours des chemins absolus

Le `$HOME` de l'utilisateur SSH du site a change entre deux sessions (tantot
le dossier racine du compte hebergement, tantot un sous-dossier), ce qui casse
tout ce qui depend de `~` : `~/.ssh`, l'emplacement suppose de
`composer.phar`, etc.

**Regle : ne jamais utiliser `~` dans une commande ou un script de
deploiement sur ce serveur. Toujours le chemin absolu**,
`/homez.172/biomein/biomewebsite`.

Pour Git, configurer la cle de deploiement par chemin absolu (ne pas dependre
d'un `~/.ssh/config` qui peut devenir introuvable) :

```bash
cd /homez.172/biomein/biomewebsite
git config core.sshCommand "ssh -i /homez.172/biomein/biomewebsite/.ssh/deploy_biome -o IdentitiesOnly=yes"
git remote -v   # remote standard, ex. git@github.com:BiomeOrganization/biome-site.git
```

### 0.6 `composer.phar` introuvable : le retelecharger par chemin absolu

L'emplacement de `composer.phar` n'est pas stable non plus (meme cause que
0.5). S'il est introuvable, le retelecharger via l'installeur officiel :

```bash
cd /homez.172/biomein/biomewebsite
/usr/local/php8.3/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
/usr/local/php8.3/bin/php composer-setup.php
rm composer-setup.php
```

Puis toujours l'utiliser par chemin absolu :

```bash
/usr/local/php8.3/bin/php /homez.172/biomein/biomewebsite/composer.phar install --no-dev --optimize-autoloader
```

### 0.7 Sequence de deploiement / mise a jour standard

A partir d'un depot deja clone dans `/homez.172/biomein/biomewebsite`, en
utilisant systematiquement les chemins absolus des points precedents :

```bash
cd /homez.172/biomein/biomewebsite

git pull

/usr/local/php8.3/bin/php composer.phar install --no-dev --optimize-autoloader

# Uniquement si Livewire a change (composer.lock touche livewire/livewire),
# voir section 0.4 :
mkdir -p public/vendor/livewire
cp -r vendor/livewire/livewire/dist/* public/vendor/livewire/

/usr/local/php8.3/bin/php artisan migrate --force

# Idempotent : peut etre relance sans risque a chaque mise a jour.
/usr/local/php8.3/bin/php artisan db:seed --class=RolePermissionSeeder --force

/usr/local/php8.3/bin/php artisan optimize:clear
/usr/local/php8.3/bin/php artisan view:cache
/usr/local/php8.3/bin/php artisan route:cache

# JAMAIS : php artisan config:cache (voir section 0.3)
```

### 0.8 Multisite OVH : le domaine doit pointer sur `public/`

`new.biome.immo` est associe (manager OVH, Hebergement > Multisite) au
dossier `/homez.172/biomein/biomewebsite/public` — jamais `biomewebsite` tout
court (sinon `.env`, `composer.json`, etc. deviennent accessibles
publiquement, voir `docs/deploiement-ovh.md` section 1). SSL Let's Encrypt
actif sur ce sous-domaine.

### 0.9 Cohabitation avec le WordPress de production

Un site WordPress de **production** existe deja dans `~/www` sur ce meme
compte d'hebergement OVH (domaine principal `biome.immo`). **Ne jamais
modifier ce dossier ni son contenu.** Le site Laravel de ce projet vit
entierement dans son propre dossier separe, `biomewebsite/`, sans aucun
partage de fichiers avec le WordPress.

### 0.10 Variables `.env` importantes en preproduction reelle

- `SITE_NOINDEX=true` : le site reste **publiquement visible** (voir 0.12)
  mais exclu de l'indexation (balise `<meta name="robots" content="noindex,
  nofollow">`, cablee via `config('site.noindex')` dans
  `resources/views/layouts/site.blade.php`).
- `CRON_TOKEN` : jeton long et aleatoire genere directement sur le serveur
  (`php -r "echo bin2hex(random_bytes(32));"`), jamais commite, protegeant
  l'endpoint du planificateur (voir 0.11).

### 0.11 Planificateur : pas de cron OVH, un cron HTTP externe

Aucune tache planifiee n'est configuree dans l'onglet "Taches" du manager
OVH pour ce projet. A la place, un service externe gratuit
([cron-job.org](https://cron-job.org)) appelle chaque minute :

```
GET https://new.biome.immo/planificateur/<CRON_TOKEN>
```

Cette route (`App\Http\Controllers\PlanificateurController`, voir
`routes/web.php`) execute `Artisan::call('schedule:run')`, qui declenche a son
tour la purge RGPD quotidienne et la transmission des leads au CRM (voir
`routes/console.php`). **Si le jeton est absent ou faux, la route repond 404**
(jamais 401/403, pour ne pas reveler son existence) : c'est le comportement
attendu, pas une panne.

### 0.12 Decision client : preproduction publique, pas de Basic Auth obligatoire

Decision actee (client, 2026-07-03) : la preproduction reste **publiquement
accessible** (pas de protection par mot de passe / Basic Auth). Seule la
protection anti-indexation (`SITE_NOINDEX=true` + `robots.txt`, voir 0.10 et
section 8.2) est en place. Toute mention plus bas dans ce document d'une
protection par mot de passe **obligatoire** est a lire comme **optionnelle**
(a activer seulement si le client change d'avis).

### Recapitulatif des corrections apportees a ce document

Par rapport a la version redigee avant ce deploiement reel :

- Domaine reel : `new.biome.immo` (et non `recette.biome.immo`).
- Serveur reel : `ssh01.cluster126.gra` (et non `biomein.cluster026...`).
- Dossier reel du projet : `/homez.172/biomein/biomewebsite` (et non
  `www/recette-biome/`).
- `.ovhconfig` : place dans `public/`, avec 3 cles (et non a la racine du
  projet avec une seule cle).
- `config:cache` retire de toutes les sequences (remplace par
  `optimize:clear`).
- Ajout de l'etape Livewire (copie des assets statiques) a la sequence de
  mise a jour.
- Ajout du seeding `RolePermissionSeeder` a chaque mise a jour.
- Basic Auth : optionnelle, non appliquee (decision client).
- Planificateur : cron HTTP externe (cron-job.org), pas de tache OVH.

## 1. Pourquoi cette preproduction n'est pas indexee (mais reste publique)

Cette mise en ligne est une **recette**, pas le site public definitif. Elle
n'est pas protegee par mot de passe (decision client, voir section 0.12),
mais elle reste exclue des moteurs de recherche, pour une raison :

- **Les biens a vendre affiches sont des fixtures de demonstration**
  (`App\Services\Crm\FixtureCrmClient`, voir `CLAUDE.md` section "Biens a
  vendre = CRM"). Le CRM reel ("Portail vendeur") n'est pas encore branche :
  tant que ce n'est pas fait, aucun bien ni aucune photo affiche n'est reel.
  On ne veut pas que Google indexe des fausses annonces.
- **Le CRM reel n'etant pas branche** (point precedent), toute mise en ligne
  reste une recette tant que le contrat CRM n'est pas convenu (voir
  `docs/contrat-crm-champs.md`).

Mise a jour (chantier mobile termine, lots M0-M5 fusionnes, recette finale
M6 faite au navigateur reel — voir `docs/mobile-strategie.md`) : le
responsive (mobile/tablette/desktop) n'est plus une raison d'exclure cette
preproduction de l'indexation. Seule la raison CRM ci-dessus reste
d'actualite.

Ce qui **fonctionne deja completement** et peut etre valide sur cette
preproduction :

- Les 13 pages publiques et leur contenu editable (Accueil, Qui-sommes-nous,
  Investir, Actualites, Offres d'emploi, Partenaires...).
- L'admin Filament (`/admin`) pour gerer ces contenus.
- Les 7 formulaires publics : chaque soumission cree bien un **lead en base**,
  consultable dans l'admin sous "Formulaires recus" — **et un e-mail de
  notification si un SMTP reel est configure** (voir etape 10 ; en preprod on
  peut aussi se contenter du mailer `log`, voir la meme etape).
- La purge RGPD automatique et la commande de transmission des leads au CRM
  (en mode bouchon, voir CLAUDE.md), toutes deux declenchees par le
  planificateur HTTP externe (voir section 0.11 et section 9).

## 2. Prerequis manager OVH

### 2.1 Choisir PHP 8.3 pour ce site (fichier `.ovhconfig`)

L'hebergement mutualise OVH utilise PHP 7.4 par defaut pour l'ensemble du
compte. Le projet exige PHP 8.2+ (voir `composer.json`, `"php": "^8.2"`) : on
selectionne donc **PHP 8.3 uniquement pour ce site**, sans toucher au reste du
compte, via un fichier `.ovhconfig`.

**Important : ce fichier se place dans le dossier servi au web (`public/`),
pas a la racine du projet** — voir le retour d'experience reel, section 0.2,
qui corrige une premiere hypothese erronee (documentee dans une version
anterieure de ce runbook).

Contenu exact :

```ini
app.engine=php
app.engine.version=8.3
environment=production
```

Chemin : `<dossier-du-site>/public/.ovhconfig` (exemple reel :
`/homez.172/biomein/biomewebsite/public/.ovhconfig`).

Alternative equivalente : regler la version PHP depuis le manager OVH,
Hebergement -> Multisite -> selectionner le domaine/sous-domaine -> "Modifier
les infos" -> version PHP -> choisir 8.3.

Verifier ensuite en SSH, avec le **chemin absolu** du binaire (sur le cluster
`ssh01.cluster126.gra`, `php` et `php8.3` seuls ne suffisent pas — voir
section 0.1) :

```bash
/usr/local/php8.3/bin/php -v
```

Le manager OVH met parfois quelques minutes a appliquer le changement pour la
partie web. Si le site affiche encore une erreur PHP 7.4 juste apres la
modification, patienter puis reessayer.

### 2.2 Creer une base MySQL dediee a la recette

Dans le manager OVH : Hebergement -> Bases de donnees -> Creer une base
MySQL.

- Choisir un nom explicite, par exemple `biomein_recette` (le prefixe
  `biomein_` est souvent impose automatiquement par OVH selon le login
  d'hebergement).
- OVH genere le nom d'utilisateur et demande de definir un mot de passe.
- **Noter precieusement** (dans un gestionnaire de mots de passe, jamais dans
  Git) : host de la base (ex. `biomeinXXXX.mysql.db`), nom de la base,
  utilisateur, mot de passe. Ces 4 valeurs vont dans le `.env` du serveur
  (voir `.env.production.example`).
- Ne jamais reutiliser la base de production reelle pour la recette : une
  base MySQL separee et dediee est indispensable (donnees fixtures, tests,
  purges, resets possibles sans risque).

### 2.3 Pointer le sous-domaine de preproduction

Manager OVH -> Domaines -> `biome.immo` -> Sous-domaines -> Creer le
sous-domaine dedie a la recette. Sur cette preproduction reelle, c'est
`new.biome.immo` qui a ete cree et utilise (voir section 0).

Puis, dans Hebergement -> Multisite :

- Associer le sous-domaine au dossier `public/` du projet, jamais la racine
  (exemple reel : `/homez.172/biomein/biomewebsite/public` — voir
  `docs/deploiement-ovh.md` section 1 et section 0.8 ci-dessus).
- Le dossier du projet (hors `public/`) contient le reste du code (app,
  config, `.env`, `vendor/`, etc.), le fichier `.ovhconfig` de l'etape 2.1 se
  trouvant lui **dans** `public/`.

Attendre la propagation DNS/SSL du sous-domaine (generalement quelques
minutes a quelques heures ; OVH fournit un certificat Let's Encrypt
automatique sur les sous-domaines multisite).

## 3. Verifier les extensions PHP requises

Laravel 12 + Filament 5.6 necessitent ces extensions : `intl`, `gd`, `zip`,
`bcmath`, `mbstring`, `openssl`, `pdo_mysql`, `fileinfo`, `curl`, `dom`,
`tokenizer`, `ctype`, `xml`. Elles sont normalement toutes presentes sur
l'offre du client, mais on verifie avant de deployer.

Verification en SSH (une fois connecte, voir etape 4), avec le chemin absolu
du binaire (voir section 0.1) :

```bash
/usr/local/php8.3/bin/php -m | grep -iE 'intl|gd|zip|bcmath|mbstring|openssl|pdo_mysql|fileinfo|curl|dom|tokenizer|ctype|xml'
```

Comparer la liste obtenue avec les 13 extensions attendues. S'il en manque
une :

- Verifier dans le manager OVH -> Hebergement -> Configurer PHP si des
  extensions optionnelles peuvent etre activees pour la version 8.3
  selectionnee.
- Sinon, contacter le support OVH (les extensions listees sont standard et ne
  devraient pas manquer).

Alternative sans SSH (a n'utiliser qu'en depannage) : deposer temporairement
un fichier `phpinfo.php` contenant `<?php phpinfo();` dans `public/`, le
consulter via le navigateur, puis **le supprimer immediatement** apres
verification (ne jamais le laisser en ligne : il expose des details serveur).

## 4. Deploiement du code via SSH

Se connecter en SSH (identifiants dans le manager OVH, onglet FTP-SSH) :

```bash
ssh biomein@ssh01.cluster126.gra
```

Se placer dans le dossier du projet, puis cloner le depot (premiere fois) ou
le mettre a jour (fois suivantes). Toujours en chemin absolu (voir section
0.5) :

```bash
cd /homez.172/biomein
git clone https://github.com/BiomeOrganization/biome-site.git biomewebsite
cd biomewebsite
# configurer la cle de deploiement (voir section 0.5) :
git config core.sshCommand "ssh -i /homez.172/biomein/biomewebsite/.ssh/deploy_biome -o IdentitiesOnly=yes"
# mises a jour suivantes : git pull origin main (ou la branche a recetter)
```

Installer les dependances PHP en mode production (sans les paquets de dev,
autoloader optimise), avec le chemin absolu du binaire PHP 8.3 (voir section
0.1) :

```bash
/usr/local/php8.3/bin/php /homez.172/biomein/biomewebsite/composer.phar install --no-dev --optimize-autoloader
```

Si `composer.phar` est introuvable (emplacement instable, voir section 0.6),
le retelecharger :

```bash
/usr/local/php8.3/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
/usr/local/php8.3/bin/php composer-setup.php
rm composer-setup.php
```

Rappel important : le dossier `biomewebsite/` contient TOUT le projet (y
compris `.env`, `vendor/`, `app/`...). Seul `biomewebsite/public/` est expose
au web via le Multisite configure a l'etape 2.3.

## 5. Fichier `.env` de production

Le fichier `.env` **n'est jamais commite** dans Git. Le creer directement sur
le serveur, a partir du modele `.env.production.example` livre a la racine du
depot :

```bash
cp .env.production.example .env
nano .env   # ou vi .env
```

Renseigner au minimum : `APP_KEY` (etape 6), les 4 informations `DB_*` notees
a l'etape 2.2, `APP_URL=https://new.biome.immo`, et le bloc `MAIL_*` choisi
(voir etape 10). Voir le detail commente de chaque variable dans
`.env.production.example`.

## 6. Commandes d'installation Laravel

Toujours en SSH, depuis `/homez.172/biomein/biomewebsite`, avec le chemin
absolu du binaire PHP 8.3 (voir section 0.1) :

```bash
/usr/local/php8.3/bin/php artisan key:generate
/usr/local/php8.3/bin/php artisan migrate --force --seed
/usr/local/php8.3/bin/php artisan storage:link
```

Notes :

- `--seed` peuple la base avec le contenu de demonstration conforme a la
  maquette (pages Accueil/Qui-sommes-nous/Investir, actualites, offres
  d'emploi, partenaires, realisations — voir README section "Demarrage
  local"). **Cela cree aussi un compte utilisateur de test
  (`test@example.com`)** : a supprimer depuis l'admin apres la creation du
  vrai compte (etape 7), ou a defaut a ignorer (ne jamais s'en servir en
  dehors de la recette).
- `storage:link` cree un lien symbolique `public/storage` -> `storage/app/public`.
  Sur mutualise OVH, ce lien symbolique **peut echouer** (droits, restriction
  de l'hebergeur). Si c'est le cas, suivre la solution deja documentee dans
  `docs/deploiement-ovh.md` section 6 : recreer un **lien relatif** a la main,
  ou utiliser un petit script PHP dedie place temporairement dans `public/`
  pour forcer la creation du lien, puis le supprimer.

Ensuite, seeder les roles/permissions Filament (idempotent, sans risque a
relancer) :

```bash
/usr/local/php8.3/bin/php artisan db:seed --class=RolePermissionSeeder --force
```

Puis mettre en cache les routes et les vues, et publier les assets Filament
(pas de build Node cote serveur, voir CLAUDE.md section 2). **Ne jamais
lancer `config:cache` sur cet hebergement (voir section 0.3, erreur 500
constatee).**

```bash
/usr/local/php8.3/bin/php artisan route:cache
/usr/local/php8.3/bin/php artisan view:cache
/usr/local/php8.3/bin/php artisan filament:assets
```

Enfin, publier les assets JS de Livewire en statique (indispensable sur ce
cluster, voir section 0.4) :

```bash
mkdir -p public/vendor/livewire
cp -r vendor/livewire/livewire/dist/* public/vendor/livewire/
```

## 7. Creation du compte admin reel

Ne pas utiliser le compte de test cree par le seeder. Creer le vrai compte
admin qui servira a la recette :

```bash
/usr/local/php8.3/bin/php artisan make:filament-user
```

La commande demande interactivement nom, e-mail et mot de passe. Choisir un
mot de passe fort.

## 8. Protection de la preproduction

**Rappel (decision client, section 0.12) : la Basic Auth ci-dessous est
OPTIONNELLE et n'est PAS appliquee sur cette preproduction.** Seule la
protection anti-indexation (8.2) est en place. La sous-section 8.1 reste
documentee au cas ou le client demanderait cette protection plus tard.

### 8.1 Authentification HTTP (Basic Auth via `.htaccess`) — optionnelle, non activee

Dans `public/`, creer (ou completer) un `.htaccess` :

```apache
AuthType Basic
AuthName "Preproduction Biome - acces reserve"
AuthUserFile /homez.172/biomein/biomewebsite/.htpasswd
Require valid-user
```

Le fichier `.htpasswd` doit se trouver **en dehors** de `public/` (donc non
accessible directement en HTTP), par exemple `biomewebsite/.htpasswd`. Le
generer en SSH avec `htpasswd` s'il est disponible :

```bash
htpasswd -c /homez.172/biomein/biomewebsite/.htpasswd recette
# demande un mot de passe, a definir avec l'equipe si cette protection est un jour activee
```

Si `htpasswd` n'est pas disponible sur le mutualise, generer la ligne
localement (n'importe quel outil "htpasswd generator" hors ligne ou
`openssl passwd -apr1`) et coller le resultat dans le fichier, au format :

```
recette:$apr1$xxxxxxxx$xxxxxxxxxxxxxxxxxxxxxx
```

Attention a ne pas ecraser un `.htaccess` existant genere par Laravel/OVH :
ajouter ces lignes **en tete** du fichier, avant les regles de reecriture
`RewriteEngine` deja presentes.

### 8.2 Exclusion des moteurs de recherche (noindex) — en place

Deux niveaux, cumulatifs, deja actifs sur `new.biome.immo` :

**a) `robots.txt`** — a la racine de `public/` :

```
User-agent: *
Disallow: /
```

**b) Meta noindex sur chaque page.** Cablee dans le layout Blade commun
(`resources/views/layouts/site.blade.php`), conditionnee a
`config('site.noindex')` :

```blade
@if(config('site.noindex'))
    <meta name="robots" content="noindex, nofollow">
@endif
```

Pilotee par la variable `SITE_NOINDEX=true` dans le `.env` du serveur (voir
`.env.production.example` et `config/site.php`). A laisser absente/`false` en
production definitive (voir section 12).

## 9. Planificateur (scheduler Laravel)

**Pas de tache planifiee OVH pour ce projet** (voir section 0.11) : le manager
OVH (Hebergement -> Taches) n'est pas utilise. A la place, un service externe
gratuit ([cron-job.org](https://cron-job.org)) appelle chaque minute
l'endpoint HTTP dedie :

```
GET https://new.biome.immo/planificateur/<CRON_TOKEN>
```

- `<CRON_TOKEN>` est le jeton configure dans `.env` (`config('site.cron_token')`),
  genere sur le serveur avec `php -r "echo bin2hex(random_bytes(32));"` et
  jamais commite.
- Cet endpoint (`App\Http\Controllers\PlanificateurController`, voir
  `routes/web.php`) execute `Artisan::call('schedule:run')`, qui declenche a
  son tour :
  - la purge RGPD quotidienne (`app:purger-leads-anciens`, voir
    `routes/console.php` et `docs/rgpd.md` section 4),
  - la transmission des leads au CRM toutes les 15 minutes
    (`app:transmettre-leads-crm`, en implementation bouchon
    `TransmetteurLeadCrmJournal` tant que le contrat CRM n'est pas signe — voir
    CLAUDE.md).
- Si le jeton est absent ou faux, la route repond **404** (comportement
  attendu, jamais 401/403, pour ne pas reveler l'existence de l'endpoint).

Configurer le job sur cron-job.org : URL ci-dessus, methode `GET`, frequence
**1 minute**. Verifier apres quelques minutes que le scheduler tourne bien
(voir `storage/logs/laravel.log` en SSH, ou l'historique d'execution fourni
par cron-job.org).

## 10. E-mails (SMTP)

Pour valider reellement la reception des leads par e-mail (et pas seulement
leur presence en base), il faut un mailer qui envoie pour de vrai.

Deux options pour cette recette :

- **`MAIL_MAILER=log`** (recommande pour cette preproduction) : aucun e-mail
  n'est reellement envoye, chaque notification est ecrite dans
  `storage/logs/laravel.log`. Suffisant pour verifier que le code declenche
  bien un envoi, sans risquer d'envoyer de faux e-mails a de vrais
  destinataires pendant les tests.
- **SMTP reel** (OVH fournit un service mail / Zimbra rattache au nom de
  domaine) : necessaire si on veut valider la reception effective dans une
  vraie boite mail avant le passage en public. Renseigner alors
  `MAIL_MAILER=smtp` et les identifiants du compte mail OVH dedie (voir bloc
  commente dans `.env.production.example`).

Recommandation de ce runbook : **`log` en preprod**, SMTP reel seulement si
l'equipe a besoin de tester concretement la reception des e-mails de
notification avant le public definitif.

## 11. Checklist finale de recette

A cocher avant de considerer la preproduction prete pour la revue :

- [ ] `new.biome.immo` repond et affiche le site (DNS + SSL propages).
- [ ] `/homez.172/biomein/biomewebsite/public/.ovhconfig` existe et contient
      les 3 lignes attendues (`app.engine=php`, `app.engine.version=8.3`,
      `environment=production` — voir section 0.2).
- [ ] `/usr/local/php8.3/bin/php -v` confirme bien PHP 8.3 (le simple `php`
      ou `php8.3` ne suffit pas sur ce cluster, voir section 0.1).
- [ ] Aucun cache de configuration present (`bootstrap/cache/config.php`
      absent) : `config:cache` n'a jamais ete lance (voir section 0.3).
- [ ] `public/vendor/livewire/livewire.min.js` existe (assets Livewire
      publies en statique, voir section 0.4) et `/admin` se connecte sans
      erreur reseau.
- [ ] Le site n'est pas indexable : `robots.txt` renvoie `Disallow: /`, et le
      `<head>` des pages contient `<meta name="robots" content="noindex, nofollow">`.
- [ ] Basic Auth : desactivee (decision client, voir section 0.12) — a
      cocher seulement si le client a explicitement redemande la protection.
- [ ] `/admin` est accessible et protege par l'authentification Filament (le
      compte de test a ete supprime ou est ignore, le vrai compte admin
      fonctionne).
- [ ] Un formulaire de test (ex. Contact) cree bien un lead en base, visible
      immediatement dans l'admin sous "Formulaires recus".
- [ ] `php artisan app:purger-leads-anciens --dry-run` (chemin PHP 8.3
      absolu) s'execute sans erreur et affiche un compte coherent.
- [ ] `php artisan app:transmettre-leads-crm --dry-run` (chemin PHP 8.3
      absolu) s'execute sans erreur (mode bouchon, aucun envoi reel attendu).
- [ ] Le job cron-job.org appelle bien `https://new.biome.immo/planificateur/<CRON_TOKEN>`
      chaque minute et le scheduler tourne (voir `storage/logs/laravel.log`
      apres quelques minutes, section 9).
- [ ] `git config core.sshCommand` est bien configure avec le chemin absolu
      de la cle de deploiement (section 0.5).

## 12. Vers le public definitif

Ce qui restera a faire avant d'ouvrir le vrai site public sur `biome.immo` :

- Brancher le vrai CRM "Portail vendeur" (contrat d'acces convenu, voir
  `docs/contrat-crm-champs.md` et `docs/architecture-donnees.md`) : les biens
  et leurs photos ne seront plus des fixtures.
- Passer sur un SMTP reel si ce n'etait pas deja fait en recette.
- **Investiguer la cause de l'erreur 500 sur `config:cache`** (section 0.3)
  avant la mise en production definitive : ce n'est pas bloquant
  (`optimize:clear` + `route:cache` + `view:cache` suffisent), mais ca reste
  un ecart a comprendre pour eviter une regression future.
- Retirer `SITE_NOINDEX` du `.env` (ou le passer a `false`) et remettre un
  `robots.txt` normal (autorisant l'indexation).
- Si une protection Basic Auth avait finalement ete activee entre-temps
  (section 8.1), la retirer : supprimer le bloc du `.htaccess` et
  renommer/supprimer `.htpasswd`.
- `APP_ENV=production` est deja le bon reglage des ce runbook (aucun
  changement necessaire sur ce point au passage en public).
- Basculer le Multisite OVH du domaine principal `biome.immo` vers le dossier
  de production (dossier dedie et base MySQL de production dediee, distincte
  de celle de la recette) — reappliquer tous les pieges de la section 0
  (`.ovhconfig` dans `public/`, chemins absolus, Livewire statique, pas de
  `config:cache`, cron HTTP externe) sur ce nouveau dossier/domaine.

## Points a fournir / decider par l'equipe avant execution

- **Identifiants de la base MySQL OVH de recette** (host, nom de base,
  utilisateur, mot de passe) — a creer et noter a l'etape 2.2, jamais dans Git.
- **Choix `log` vs SMTP reel** pour les e-mails de notification des leads en
  recette (etape 10) — decision d'equipe selon le besoin de test.
- **Cause exacte de l'erreur 500 sur `config:cache`** (section 0.3) — a
  investiguer avant la mise en production definitive.

(Point resolu : la protection Basic Auth de la preproduction n'est pas
requise — decision client du 2026-07-03, voir section 0.12.)
