# BucketList — Déploiement

> App = front PWA (`public/`) + API PHP (`public/api/`) + MySQL.
> Pas de framework, pas de build : pose les fichiers, c'est en ligne.

---

## 0. Arborescence du projet

```
Bucketlist/
├── public/                       # ← document root du site
│   ├── index.html                # SPA + PWA
│   ├── manifest.webmanifest
│   ├── service-worker.js
│   ├── icons/                    # icon-192.png, icon-512.png, icon.svg
│   └── api/                      # backend PHP
│       ├── index.php             # front controller (router)
│       ├── .htaccess             # rewrite + sécurité
│       ├── config.php            # creds + secrets (à éditer)
│       ├── db.php
│       ├── lib/helpers.php
│       └── routes/               # auth, catalog, list, emblems, ideas, orders
├── sql/
│   ├── schema.sql                # CREATE TABLE …
│   └── seed.php                  # importe data/items.csv en base
├── data/
│   └── items.csv                 # source figée (export du Google Sheet)
└── DEPLOY.md                     # ce fichier
```

---

## 1. Pré-requis serveur

| Composant | Version mini |
|---|---|
| PHP        | 8.1 (PDO, pdo_mysql, json, openssl, fileinfo) |
| MySQL      | 8.0 *(ou MariaDB ≥ 10.6)* |
| Apache     | 2.4 avec `mod_rewrite` activé *(O2switch : OK par défaut)* |
|            | *Nginx* possible — voir §6 |
| HTTPS      | Obligatoire (cookies, service worker, Google Sign-In) |

---

## 2. Déploiement sur O2switch (cPanel)

### 2.1 Créer la base de données

Dans cPanel → **MySQL® Databases** :

1. Crée une base, ex. `monuser_bucketlist`
2. Crée un utilisateur, ex. `monuser_bl`, mot de passe fort
3. Attache l'utilisateur à la base avec **ALL PRIVILEGES**
4. Note les 4 valeurs : `host` (souvent `localhost`), `name`, `user`, `pass`

### 2.2 Importer le schéma

cPanel → **phpMyAdmin** → sélectionne la base → onglet **Importer** → upload `sql/schema.sql` → Exécuter.

### 2.3 Uploader les fichiers

Via le **File Manager** ou **SFTP** :

```
public_html/                       (le document root chez O2switch)
├── index.html                     (= public/index.html)
├── manifest.webmanifest
├── service-worker.js
├── icons/
└── api/
    └── …
```

Et au-dessus du `public_html` (zone privée) :
```
sql/seed.php
data/items.csv
```

> Garde `sql/` et `data/` **hors** du document root pour qu'ils ne soient pas exposés sur le web.

### 2.4 Configurer la connexion à la base

Édite `public_html/api/config.php` **OU** mieux : pose ces variables dans `.htaccess` du `api/` :

```apache
SetEnv BL_DB_HOST     "localhost"
SetEnv BL_DB_NAME     "monuser_bucketlist"
SetEnv BL_DB_USER     "monuser_bl"
SetEnv BL_DB_PASS     "<motdepasse>"
SetEnv BL_APP_SECRET  "<32-chars-aléatoires>"
SetEnv BL_GOOGLE_CLIENT_ID "<client-id>.apps.googleusercontent.com"
SetEnv BL_DEBUG       "0"
```

(Ne JAMAIS commiter ces valeurs au repo.)

### 2.5 Seeder le catalogue

Deux options :

**A. Via le shell SSH** *(O2switch propose SSH sur les offres récentes)* :
```bash
cd ~/path/to/Bucketlist
php sql/seed.php
```

**B. Sans shell** : crée un fichier temporaire `public_html/api/_seed_once.php` contenant :
```php
<?php require __DIR__.'/../../sql/seed.php';
```
Ouvre-le 1 fois dans le navigateur, vérifie la sortie, puis **supprime-le**.

Tu devrais voir :
```
[seed] Connected to MySQL
[seed] Categories: 6 upserted
[seed] Subcategories: 58 upserted
[seed] Emblems: 28 upserted
[seed] Items: 556 upserted
[seed] Done ✓
```

### 2.6 Configurer Google Sign-In *(optionnel)*

1. Va sur https://console.cloud.google.com/apis/credentials
2. Crée des **OAuth 2.0 Client IDs** type **Web application**
3. **Authorized JavaScript origins** : ton URL prod (ex. `https://bucketlist.example.com`)
4. Copie le **Client ID** dans **deux endroits** :
   - `BL_GOOGLE_CLIENT_ID` côté serveur (voir §2.4)
   - `window.BL_CONFIG.GOOGLE_CLIENT_ID` dans `index.html` (en haut, dans le `<script>` inline)

### 2.7 Tester

- `https://ton-site/api/health` → JSON `{ ok: true, … }`
- `https://ton-site/` → l'app charge le catalogue (556 items)
- Crée un compte, ajoute un item, coche-le → le `streak` et les emblèmes doivent évoluer

---

## 3. Déploiement sur VPS (Ubuntu/Debian + Apache)

```bash
# Paquets
sudo apt update && sudo apt install -y apache2 mariadb-server \
  php php-mysql php-mbstring php-json libapache2-mod-php

sudo a2enmod rewrite headers
sudo systemctl restart apache2

# Base de données
sudo mysql -e "CREATE DATABASE bucketlist CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'bl'@'localhost' IDENTIFIED BY 'CHANGEME';"
sudo mysql -e "GRANT ALL ON bucketlist.* TO 'bl'@'localhost'; FLUSH PRIVILEGES;"
mysql -u bl -p bucketlist < sql/schema.sql

# Fichiers
sudo rsync -av public/ /var/www/bucketlist/
sudo mkdir /opt/bucketlist && sudo rsync -av sql data /opt/bucketlist/

# Vhost Apache /etc/apache2/sites-available/bucketlist.conf :
#   <VirtualHost *:443>
#     ServerName bucketlist.example.com
#     DocumentRoot /var/www/bucketlist
#     <Directory /var/www/bucketlist>
#       AllowOverride All
#       Require all granted
#     </Directory>
#     SetEnv BL_DB_HOST     "localhost"
#     SetEnv BL_DB_NAME     "bucketlist"
#     SetEnv BL_DB_USER     "bl"
#     SetEnv BL_DB_PASS     "CHANGEME"
#     SetEnv BL_APP_SECRET  "$(openssl rand -hex 32)"
#     # SSL via certbot (recommandé)
#   </VirtualHost>

sudo a2ensite bucketlist && sudo systemctl reload apache2
sudo certbot --apache -d bucketlist.example.com

# Seed
php /opt/bucketlist/sql/seed.php
```

---

## 4. Sécurité — checklist

- [ ] `BL_APP_SECRET` rempli avec ≥ 32 chars aléatoires *(rotation = logout global)*
- [ ] HTTPS forcé (redirect 80 → 443)
- [ ] `BL_DEBUG=0` en production
- [ ] `sql/` et `data/` hors document root, OU bloqués par `.htaccess`
- [ ] Les fichiers `api/config.php` et `api/db.php` sont déjà bloqués via `<FilesMatch>` dans `api/.htaccess`
- [ ] Mot de passe MySQL utilisé uniquement pour cette base
- [ ] Backups MySQL automatisés *(cPanel : Backup Wizard ; VPS : `mysqldump` en cron)*

---

## 5. Mises à jour du catalogue depuis le Google Sheet

```bash
# 1. Re-exporter le sheet en CSV
curl -sL "https://docs.google.com/spreadsheets/d/<ID>/export?format=csv&gid=172344851" -o data/items.csv

# 2. Re-seeder (idempotent grâce à ON DUPLICATE KEY UPDATE)
php sql/seed.php
```

Le script écrase titres/catégories pour les ID existants, ajoute les nouveaux ID, ne supprime rien.
**Pour supprimer un item** : passe-le en `STATUT = Rejeté` dans le sheet, puis fais `UPDATE items SET status='rejected' WHERE …` manuellement (le seed actuel ne tue pas les lignes orphelines pour éviter la casse).

---

## 6. Nginx — config minimale

```nginx
server {
  listen 443 ssl http2;
  server_name bucketlist.example.com;
  root /var/www/bucketlist;
  index index.html;

  # Statique + SPA
  location / {
    try_files $uri $uri/ /index.html;
  }

  # API → PHP
  location /api/ {
    try_files $uri /api/index.php?$query_string;
  }

  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param BL_DB_HOST "localhost";
    fastcgi_param BL_DB_NAME "bucketlist";
    fastcgi_param BL_DB_USER "bl";
    fastcgi_param BL_DB_PASS "CHANGEME";
    fastcgi_param BL_APP_SECRET "<openssl rand -hex 32>";
  }

  # Protège les configs
  location ~ /api/(config|db)\.php$ { deny all; }
}
```

---

## 7. Endpoints API — référence rapide

| Méthode | Route                       | Auth | Rôle |
|---|---|---|---|
| GET   | `/api/health`              | —   | ping |
| POST  | `/api/auth/register`       | —   | email + password |
| POST  | `/api/auth/login`          | —   | email + password |
| POST  | `/api/auth/google`         | —   | Google ID token |
| POST  | `/api/auth/logout`         | ✓   |  |
| GET   | `/api/auth/me`             | ✓   |  |
| PATCH | `/api/auth/me`             | ✓   | pseudo, isPrivate, settings |
| GET   | `/api/categories`          | —   | + sous-catégories + counts |
| GET   | `/api/items`               | —   | filtres : cat, sub, q, limit, offset |
| GET   | `/api/items/{id}`          | —   |  |
| GET   | `/api/list`                | ✓   | liste utilisateur |
| POST  | `/api/list`                | ✓   | {itemId} |
| DELETE| `/api/list/{itemId}`       | ✓   |  |
| PATCH | `/api/list/{itemId}`       | ✓   | {done} → renvoie streak + nouveaux emblèmes |
| GET   | `/api/emblems`             | ✓   | catalogue + progress + unlocked |
| POST  | `/api/ideas`               | —   | proposition d'item |
| POST  | `/api/orders/pdf`          | ✓   | crée commande PDF (pending) |
| POST  | `/api/orders/booklet`      | ✓   | {format, cover, shippingName, shippingAddress} |
| GET   | `/api/orders`              | ✓   | historique |

---

## 8. Brancher un vrai paiement (Stripe) plus tard

Le flux `orders` est déjà prêt : chaque commande crée une ligne `status=pending` avec une référence.
Pour activer Stripe :
1. Crée un compte Stripe + récupère les clés
2. Crée un endpoint `/api/orders/{id}/checkout` qui appelle `Stripe::PaymentIntent::create`
3. Crée un webhook `/api/orders/webhook` qui passe la commande en `paid` quand Stripe confirme
4. Le front : redirige `paymentUrl` vers la session Stripe Checkout

Schéma DB déjà compatible (`payment_provider`, `payment_ref`, `status='paid'`).
