# Vinyle — Wishlist de vinyles avec suivi de prix manuel

Petite app web personnelle pour tenir à jour une wishlist de vinyles, la partager avec des proches (qui peuvent « réserver » un album avant l'achat) et noter manuellement les prix sur différents sites marchands.

- **Mobile first**, **Bootstrap 5 + jQuery**, **PHP 7.4+ compatible** (testé sur 7.4 et 8.4), **SQLite**.
- 2 types d'URL : une pour le propriétaire (tous droits), autant que souhaité pour les viewers (lecture + pose de verrous nommés).
- Recherche d'album via MusicBrainz, pochettes iTunes avec fallback Cover Art Archive, covers téléchargées en local pour un affichage instantané.
- Sources de prix **entièrement administrables** (nom + couleur configurables, ajout / suppression libre). Les prix et URLs sont saisis manuellement par l'owner — pas d'automatisation (le scraping des sites marchands s'est révélé trop peu fiable).

---

## 1. Pré-requis

- **PHP 7.4** ou **PHP 8.x** avec les extensions `pdo_sqlite` et `json` (standard sur tout hébergement moderne).
- Apache (mod_rewrite activé) ou Nginx.
- Pour le dev local : Docker + Docker Compose v2+.
- Pour la prod : VPS ou hébergement mutualisé compatible PHP 7.4+ / mod_rewrite.

---

## 2. Démarrage rapide (développement local)

```bash
git clone <ce-dépôt> vinyle
cd vinyle

# 1. Autorise le domaine de dev en local (/etc/hosts)
sudo sh -c 'echo "127.0.0.1 vinyle-dev.dpc.li" >> /etc/hosts'

# 2. Démarre le container (build la première fois)
docker compose up -d --build

# 3. Installe les dépendances Composer dans le container
docker compose exec web composer install

# 4. Initialise le schéma SQLite
docker compose exec web php bin/init_db.php

# 5. Ouvre l'app
open http://vinyle-dev.dpc.li
```

Au premier appel à la racine, un **`owner_token`** est généré et tu es redirigé vers `/w/{owner_token}`. Mets cette URL en signet — c'est ta page owner (plein droits). Tout appel ultérieur à la racine nue renvoie 404 pour ne pas divulguer son existence.

---

## 3. Configuration

Tous les paramètres sont dans `config/config.php`. Pour surcharger sans toucher au fichier versionné, crée `config/local.php` (déjà ignoré par git).

### Variables d'environnement

| Variable | Défaut | Rôle |
|---|---|---|
| `APP_ENV` | `dev` | `dev` active l'affichage des erreurs. `prod` les masque. |
| `APP_BASE_URL` | `http://vinyle-dev.dpc.li` | Base URL pour générer les liens absolus. En prod : `https://vinyle.dpc.li`. |
| `APP_USER_AGENT` | `VinyleWishlist/1.0 (…)` | UA envoyé à MusicBrainz / iTunes / Cover Art Archive. |
| `APP_DB_PATH` | `/var/www/html/data/vinyle.sqlite` | Chemin du fichier SQLite. |
| `APP_COVERS_DIR` | `/var/www/html/public/covers` | Répertoire des pochettes téléchargées. |

---

## 4. Déploiement en production

### Option A — VPS avec Docker (recommandée)

```bash
ssh root@<vps>
cd /var/www
git clone <dépôt> vinyle
cd vinyle

cat > .env <<EOF
APP_ENV=prod
APP_BASE_URL=https://vinyle.dpc.li
EOF

docker compose up -d --build
docker compose exec web composer install --no-dev --optimize-autoloader
docker compose exec web php bin/init_db.php
```

### Option B — Hébergement mutualisé PHP 7.4+

L'app tourne sans Composer (fallback PSR-4 autoloader intégré dans [`public/index.php`](public/index.php)).

1. Upload les fichiers du projet via FTP/SFTP.
2. Le **DocumentRoot** de ton hébergement doit pointer vers le dossier `public/` (ou activer `.htaccess` qui est fourni).
3. Crée `config/local.php` si tu veux surcharger `APP_BASE_URL` etc.
4. Via SSH (ou une page temporaire) : exécute `php bin/init_db.php` pour créer la base.
5. Vérifie que les répertoires `data/` et `public/covers/` sont **inscriptibles** par le serveur web.

### Reverse-proxy HTTPS (Nginx minimal)

```nginx
server {
    listen 443 ssl http2;
    server_name vinyle.dpc.li;

    ssl_certificate     /etc/letsencrypt/live/vinyle.dpc.li/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vinyle.dpc.li/privkey.pem;

    location / {
        proxy_pass         http://127.0.0.1:80;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
    }
}
```

Aucun cron n'est nécessaire : le suivi des prix est entièrement manuel.

---

## 5. Utilisation

### Premier lancement
Accède à `https://vinyle.dpc.li`. Le site génère un `owner_token` et te redirige. Conserve l'URL (signet + gestionnaire de mots de passe). Pour en regénérer une autre :

```bash
docker compose exec web php bin/reset_owner.php
```

### Partager avec des proches
Depuis la navbar owner, icône 👥 **Liens de partage** → crée un lien nommé (ex. « Papa »). Copie l'URL et envoie-la. Son nom apparaîtra à côté des verrous qu'elle posera.

### Ajouter un album
Bouton **+** → tape « artiste titre » → sélectionne le résultat MusicBrainz. La pochette est récupérée automatiquement (iTunes puis Cover Art Archive en fallback) et téléchargée en local.

### Configurer les sources de prix
Depuis la navbar owner, icône 🏷️ **Sources de prix**. Tu peux :
- **Créer** une nouvelle source (nom libre + couleur du tag via color picker).
- **Modifier** une source existante (label et/ou couleur).
- **Activer / désactiver** (désactivée = n'apparaît plus sur les fiches d'album, mais les données saisies sont conservées).
- **Supprimer** (attention : supprime aussi les prix saisis pour cette source).

Les sources par défaut (Discogs, Amazon.fr, Fnac, iMusic.fr, E.Leclerc) sont une base — adapte-les à ton usage.

### Saisir un prix pour un album
Sur la fiche album, chaque source activée apparaît dans la section « Prix » avec un bouton **Saisir** / **Modifier** qui ouvre une modale demandant :
- **Prix (€)** — optionnel
- **URL du produit** — optionnelle

Au moins un des deux doit être renseigné. Pour retirer une entrée : bouton **Retirer**.

### Réserver un album (vue viewer)
Le viewer clique sur un album → **« Je réserve ce vinyle »** → un cadenas nommé apparaît pour tous. Il peut annuler son propre verrou. L'owner peut annuler n'importe quel verrou (typiquement après le passage d'une occasion : noël, anniversaire).

L'owner peut poser son propre verrou **« Marquer comme acheté »** (picto 🛍️) pour signaler aux autres qu'il est en train d'acheter.

---

## 6. Scripts `/bin/*`

Tous lancés via `docker compose exec web php bin/<script>.php`.

### `init_db.php`
Crée (ou met à jour) le schéma SQLite dans `data/vinyle.sqlite`. Idempotent : les `CREATE TABLE IF NOT EXISTS` et migrations de colonnes sont réexécutables sans risque. À lancer après un `git pull` majeur si le schéma évolue.

### `reset_owner.php`
Supprime l'`owner_token` de la table `config`. Le prochain appel à `/` regénèrera un nouveau token. À utiliser si l'URL owner fuite.

### `add_share.php "<nom>"`
Crée un share_token nommé en CLI et affiche son URL. Pratique pour pré-provisionner en masse.

### `add_test_album.php`
Insère 3 albums de démo (Miles Davis, Daft Punk, Pink Floyd) avec pochettes iTunes, sans prix. Utile pour visualiser l'UI sur une base vierge.

### `refresh_covers.php`
Passe sur tous les albums et (re-)télécharge la pochette en local si elle manque. À lancer si un doigt a effacé `public/covers/` ou après un `git pull` qui aurait touché au format de stockage.

---

## 7. Dépannage

| Symptôme | Cause probable | Solution |
|---|---|---|
| `no such table: config` | Base non initialisée | `docker compose exec web php bin/init_db.php` |
| Page blanche au `/` après config | owner_token déjà créé : le `/` renvoie 404 pour sécurité | Utilise l'URL owner mise en signet, ou `bin/reset_owner.php` |
| Pochettes lentes / 404 | Fichier local absent | `docker compose exec web php bin/refresh_covers.php` |
| Bouton « Copier » sans effet | HTTP (sans HTTPS) — `navigator.clipboard` indisponible | Fallback `execCommand('copy')` intégré. Sinon : F12 → console |
| Source supprimée par accident | CASCADE DELETE supprime aussi les prix saisis | Recréer la source (nouveau code), saisir à nouveau les prix |

---

## 8. Sécurité

- **Capability URLs** : l'owner_token et les share_tokens sont les seuls mots de passe. 256 bits de hasard chacun → impossibles à deviner. Mais si l'un fuit, il faut le regénérer.
- Les dossiers `data/`, `src/`, `config/` sont refusés par Apache via le vhost.
- Headers `X-Content-Type-Options: nosniff` et `<meta name="robots" noindex>` sur toutes les pages.
- Pas d'auth classique — le modèle capability URL est suffisant pour un outil perso à petit cercle.
