> ⛔ **NE PAS COMMITER CE FICHIER TANT QUE LA CONFIGURATION DÉCRITE N'EST PAS EN PLACE ET VALIDÉE.**
> C'est un plan de travail / aide-mémoire avec des éléments d'infra (IP, chemins, ports). On le supprime ou on le déplace hors du dépôt une fois la bascule faite.

# Configuration — connecter PhotoSync à son NAS

> Guide pour **un utilisateur** (toi, ou quelqu'un qui installerait l'app un jour). Décrit ce qu'il faut mettre en place côté serveur, comment l'app s'y connecte, sur quels NAS ça marche, et comment activer l'accès depuis l'extérieur.

---

## 0. Le principe en une image

```
   [Téléphone + app PhotoSync]
            │  HTTP(S), header X-PhotoSync-Token
            ▼
   [Agent PhotoSync (Node.js)]   ←  c'est LUI que l'app contacte, jamais le NAS directement
            │  lit/écrit des fichiers sur un dossier monté
            ▼
   [NAS]  (monté en CIFS/SMB ou NFS sous /mnt/nas)
```

**Points clés :**
- L'app **ne parle qu'à l'agent**, en HTTP, avec une seule URL + un token. Elle ne connaît ni SMB, ni les API du NAS.
- L'agent est un petit serveur **Node.js** qui monte le NAS comme un **dossier de fichiers** (`NAS_ROOT`, par défaut `/mnt/nas/...`) et expose une API HTTP (galerie, upload, corbeille, logs, sauvegarde de config).
- **Aucune donnée ne transite par le développeur.** Tout reste entre le téléphone et le serveur de l'utilisateur.

---

## 1. Est-ce que ça marche sur tous les NAS ? — **Oui (presque)**

L'app et l'agent **ne dépendent d'aucune marque**. L'agent ne parle pas l'API QNAP : il lit/écrit des fichiers sur un **chemin monté** (`NAS_ROOT`, configurable, voir [pi-agent/src/server.js](pi-agent/src/server.js#L186)). Les mentions « QNAP » dans le code ne servent qu'à **reproduire des détails** (permissions, corbeille `@Recycle`), pas à imposer ce modèle.

➡️ **Tout NAS que l'hôte de l'agent peut monter fonctionne** :
- **QNAP, Synology, Asustor, TrueNAS, OpenMediaVault…** via **SMB/CIFS** ou **NFS**.
- Même un **simple disque USB** ou un dossier local : il suffit que `NAS_ROOT` pointe sur un chemin lisible/inscriptible.

**Condition réelle** : ce n'est pas « le NAS doit être compatible », c'est « **l'hôte qui fait tourner l'agent doit pouvoir monter le stockage** ». Le NAS, lui, n'a qu'à exposer un partage réseau standard.

---

## 2. Faut-il forcément passer par le Pi ? — **Non, mais il faut *un* hôte pour l'agent**

Le « Pi » dans mon installation n'est qu'**un petit Linux allumé en permanence qui fait tourner l'agent et monte le NAS**. Ce n'est pas un Raspberry Pi obligatoire. L'agent peut tourner sur :

| Où tourne l'agent | Pi séparé nécessaire ? | Remarque |
|---|---|---|
| **Raspberry Pi** (mon cas) | — | Petit, silencieux, peu cher, mount CIFS vers le NAS |
| **N'importe quel mini-PC / vieux Linux allumé** | Non | Idem, plus de puissance |
| **Directement sur le NAS** (Docker / Container Station QNAP, Docker Synology) | **Non — supprime le Pi** | Le NAS doit pouvoir exécuter **Node.js ou Docker** |
| **Un VPS** (si le NAS y est accessible) | Non | Utile pour l'accès distant (cf. §4) |

➡️ **Verdict** : le Pi n'est pas obligatoire **en tant que matériel**. Ce qui est obligatoire, c'est qu'**un hôte exécute l'agent**. Si le NAS sait faire tourner Docker, l'agent peut vivre **dans le NAS** et il n'y a plus de boîtier séparé. Sinon, un petit Linux dédié (Pi ou autre) est le plus simple.

> ⚠️ L'app ne sait **pas** parler au NAS sans l'agent. Pas d'agent = pas de connexion. Supprimer l'agent voudrait dire réécrire l'app pour qu'elle parle SMB directement — gros chantier, non prévu.

### 2.1 Le scénario sans Pi : l'agent en **Docker sur le NAS**

C'est probablement **le mode d'installation le plus simple à proposer à un autre utilisateur** : pas de boîtier en plus, l'agent tourne dans le NAS et accède au stockage **localement** (pas même besoin de monter un partage réseau — on bind-monte un dossier interne).

**Pré-requis** : un NAS qui sait faire tourner des conteneurs :
- **QNAP** → *Container Station*
- **Synology** → *Container Manager* (ex-Docker)
- **Asustor** → *Portainer / Docker*
- **TrueNAS SCALE / OMV** → Docker natif

**Principe** : on bind-monte le dossier de médias du NAS dans le conteneur à l'emplacement attendu par l'agent (`NAS_ROOT`), et on expose le port.

```dockerfile
# pi-agent/Dockerfile (à ajouter)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
EXPOSE 8080
CMD ["node", "src/server.js"]
```

```yaml
# docker-compose.yml — à lancer sur le NAS
services:
  photosync-agent:
    build: ./pi-agent          # ou image: ghcr.io/<toi>/photosync-agent si tu publies
    restart: always
    ports:
      - "8080:8080"
    environment:
      NAS_ROOT: /data
      PHOTOSYNC_TOKEN: "MON_TOKEN_SECRET"   # toujours définir un token
    volumes:
      # le dossier réel des médias sur le NAS -> /data dans le conteneur
      - /share/Multimedia:/data            # QNAP ex. ; Synology: /volume1/photo, etc.
```

**Détails à soigner** :
- **Chemin du volume** : remplacer `/share/Multimedia` par le vrai chemin du NAS (QNAP : `/share/...` ; Synology : `/volume1/...`).
- **Droits (uid/gid)** : le conteneur doit écrire dans le dossier → adapter l'utilisateur (`user:` dans compose) aux permissions du partage NAS, comme le fait mon installation Pi (qui reproduit les droits QNAP).
- **Corbeille / `@Recycle`** : sur QNAP, désactiver la corbeille réseau sur le partage utilisé (sinon elle intercepte les suppressions et fausse l'espace libéré — c'est un piège déjà rencontré).
- **Démarrage auto** : `restart: always` suffit ; l'agent repart au reboot du NAS.

➡️ Avantages : **zéro matériel en plus**, accès disque **local** (plus rapide qu'un mount CIFS), une seule machine à maintenir. C'est le mode à recommander en premier à quelqu'un dont le NAS supporte Docker.

---

## 3. Mise en place côté serveur (checklist agent)

Pour un nouvel utilisateur, une fois choisi l'hôte de l'agent :

- [ ] **Installer Node.js** (ou Docker) sur l'hôte.
- [ ] **Monter le NAS** sur l'hôte (CIFS/SMB ou NFS), p.ex. `/mnt/nas` :
  ```bash
  # exemple CIFS (à adapter : IP du NAS, partage, identifiants)
  sudo mount -t cifs //IP_DU_NAS/partage /mnt/nas -o username=USER,password=PASS,uid=1000,gid=1000
  ```
- [ ] **Récupérer l'agent** (`pi-agent/`) sur l'hôte.
- [ ] **Configurer les variables d'environnement** :
  | Variable | Rôle | Défaut |
  |---|---|---|
  | `NAS_ROOT` | dossier racine de navigation (le mount) | `/mnt/nas/taaazzz` |
  | `PORT` | port d'écoute de l'agent | `8080` |
  | `PHOTOSYNC_TOKEN` | **secret partagé** exigé sur chaque appel | *(vide)* |
  | `PHOTOSYNC_ALLOW_OPEN` | `1` = autorise SANS token (réseau de confiance) | *(non posé)* |
  | `PHOTOSYNC_DEFAULT_SHARE` | partage par défaut pour la sync | `homes` |

  > 🔒 **Sécurité** : si `PHOTOSYNC_TOKEN` est défini, l'agent exige ce token sur tous les endpoints (comparaison à **temps constant**). S'il est **vide**, l'agent est **fail-closed** : il **refuse tout** (401) sauf si `PHOTOSYNC_ALLOW_OPEN=1` est posé explicitement. → **Toujours définir un token.**
- [ ] **Lancer l'agent en service** (systemd, ou conteneur Docker `restart: always`) pour qu'il redémarre tout seul.
- [ ] **Ouvrir le port** (`8080`) uniquement sur le réseau voulu (LAN, ou interface VPN — **pas** en grand ouvert sur internet sans token).

---

## 3 bis. Le token : créer, retrouver, et l'enregistrer **une seule fois**

Le token est le **secret partagé** qui protège l'agent (sans lui, n'importe qui sur le réseau pourrait lire/écrire sur le NAS via l'agent). C'est une longue chaîne aléatoire — **on ne la retient pas, et c'est normal** : on la manipule **une seule fois**, puis l'app la garde.

### Créer un token
Sur l'hôte de l'agent (ou n'importe quelle machine), générer une chaîne aléatoire solide :
```bash
openssl rand -hex 24      # 48 caractères hexadécimaux
# ou : head -c 24 /dev/urandom | base64
```

### Le poser sur l'agent
Sur le Pi, il vit dans le fichier d'environnement du service **`/etc/photosync-agent.env`** :
```bash
echo 'PHOTOSYNC_TOKEN=<le-token-genere>' | sudo tee -a /etc/photosync-agent.env
sudo systemctl restart photosync-agent
```

### Le retrouver plus tard (si oublié)
Pas besoin de le mémoriser — il est lisible sur le Pi à tout moment :
```bash
sudo grep PHOTOSYNC_TOKEN /etc/photosync-agent.env
```

### L'enregistrer **une fois** dans l'app → on ne le retape plus
1. Dans l'app : **Réglages → Serveur & connexion → champ Token** → coller le token → valider.
2. L'app le **stocke sur l'appareil**. **Tu ne le retapes jamais**, tant que tu ne réinstalles pas l'app (il n'est PAS sauvegardé sur le NAS — c'est un choix de sécurité, cf. [SECURITE.md](SECURITE.md)). Après une réinstallation, on le recolle une fois (ou on le relit sur le Pi).

> 💡 Les URLs avec `&token=…` (ex. téléchargement manuel d'un APK) ne servent **que** pour un accès ponctuel hors de l'app. **Dans l'usage normal, le token vit dans l'app et tu ne le vois jamais.**

---

## 4. Côté app : connexion (ce que l'utilisateur fait dans PhotoSync)

Très simple côté téléphone — toute la complexité est sur le serveur :

1. **Ouvrir l'app** → écran de configuration.
2. **Saisir l'URL de l'agent** (`piUrl`), p.ex. :
   - sur le **même WiFi** que l'hôte : `http://192.168.1.136:8080`
   - via **VPN** (accès distant, §5) : l'IP VPN de l'hôte, p.ex. `http://100.x.y.z:8080` (Tailscale) ou `http://10.8.0.2:8080` (WireGuard).
3. **Saisir le token** (`X-PhotoSync-Token`) — le même que `PHOTOSYNC_TOKEN` côté agent.
   > Le token **n'est pas sauvegardé** sur le NAS (ce serait un secret derrière la porte qu'il ouvre) : il est à **re-saisir après une réinstallation** de l'app.
4. Le reste de la config (serveurs, règles de dossiers, options Wi-Fi only / tri par date) **se restaure automatiquement** depuis le NAS via l'agent — pas besoin de tout refaire après réinstallation (sauf le token et l'URL). Voir [mobile/lib/config.js](mobile/lib/config.js).

➡️ **En résumé, l'utilisateur ne fournit que 2 choses : l'URL de l'agent + le token.**

---

## 5. Accès depuis l'extérieur (4G / hors domicile)

Sur le **même WiFi** que l'hôte, rien à faire : l'app joint l'IP LAN directement. Pour y accéder **depuis l'extérieur**, il faut **un chemin réseau** vers l'agent. **Tailscale n'est PAS obligatoire** — c'est juste le plus simple. Options :

| Méthode | Difficulté | Avantages | Inconvénients |
|---|---|---|---|
| **Tailscale** (mon cas actuel) | 🟢 Facile | Aucune config routeur, marche derrière CGNAT, app iOS native | Dépend du serveur de coordination Tailscale (métadonnées, pas le contenu) |
| **WireGuard auto-hébergé** (via VPS) | 🟠 Moyen | 100 % maître du chemin, app iOS native | Nécessite un VPS + config (voir [WIREGUARD-MIGRATION.md](WIREGUARD-MIGRATION.md)) |
| **Redirection de port + DDNS** | 🟠 Moyen | Pas de tiers | **Expose l'agent sur internet** (token obligatoire, plus risqué), souvent bloqué par CGNAT |
| **Cloudflare Tunnel / Twingate…** | 🟠 Moyen | Pas d'ouverture de port | Dépendance à un tiers |

➡️ **Chaque utilisateur choisit** selon ses préférences. Tailscale est recommandé pour la simplicité ; WireGuard pour la souveraineté ; redirection de port à éviter sauf token solide.

> Quel que soit le tunnel, **le contenu des photos est protégé** : Tailscale et WireGuard chiffrent de bout en bout (ils ne lisent pas les photos). C'est détaillé dans [WIREGUARD-MIGRATION.md](WIREGUARD-MIGRATION.md).

### 5.1 Pourquoi Tailscale a été mis en place, et pas WireGuard directement ?

**Tailscale, c'est WireGuard** — il utilise le protocole WireGuard pour le chiffrement. La différence n'est pas le tunnel, c'est **qui gère la mise en relation** des appareils :

| | **Tailscale** (en place) | **WireGuard « pur » auto-hébergé** (envisagé) |
|---|---|---|
| Chiffrement | WireGuard (identique) | WireGuard (identique) |
| Mise en relation des pairs | **Automatique** (serveur de coordination Tailscale) | **À ta charge** : il faut un point public fixe |
| Point public nécessaire | **Non** — traverse le NAT/CGNAT tout seul | **Oui** — VPS avec IP publique **ou** redirection de port sur ta box |
| Derrière un CGNAT opérateur (4G, fibre sans IP publique) | **Marche** (relais DERP en secours) | **Ne marche pas** sans VPS |
| Gestion des clés | Gérée pour toi (login) | **Manuelle** (générer/échanger les clés publiques de chaque pair) |
| Mise en place | ~5 min, installer l'app, se logger | Plusieurs étapes (VPS, firewall UDP, conf de chaque pair) |
| Dépendance externe | Serveur de coordination Tailscale (voit les **métadonnées**, pas le contenu) | **Aucune** — 100 % chez toi |

**La raison concrète** : sans IP publique fixe (cas courant en 4G / fibre grand public avec CGNAT), WireGuard pur **a besoin d'un VPS** comme point de rendez-vous, plus toute la config de clés. Tailscale fait **tout ça automatiquement** en 5 minutes. C'est donc le choix **pragmatique de départ** : ça marche tout de suite, partout, y compris derrière un CGNAT.

**Pourquoi vouloir quand même migrer vers WireGuard pur** (cf. [WIREGUARD-MIGRATION.md](WIREGUARD-MIGRATION.md)) : pour la **souveraineté** — ne plus dépendre du serveur de coordination de Tailscale (qui connaît tes appareils/IP/horaires, même s'il ne lit jamais les photos) ni de leurs binaires. **Ce n'est pas que WireGuard est « plus sûr » pour le contenu** (le chiffrement est le même) ; c'est une question d'**indépendance**, au prix d'un VPS et de plus de config. D'où le séquencement : **Tailscale d'abord (ça marche), WireGuard ensuite (si on veut tout maîtriser)**.

### Tailscale (et WireGuard) fonctionnent-ils sur iOS ? — **Oui**

- **Tailscale** a une **app iOS native** (App Store). Elle installe un profil VPN, tourne en arrière-plan, et rend l'IP `100.x.y.z` de l'hôte joignable depuis le téléphone — exactement comme sur Android.
- **WireGuard** a aussi une **app iOS officielle**.
- Côté PhotoSync iOS, il suffit alors de saisir l'**URL VPN** de l'agent. ⚠️ **Détail technique** (cf. [IOS-MIGRATION.md](IOS-MIGRATION.md)) : iOS bloque le HTTP clair par défaut (ATS). Les IP Tailscale (`100.64.0.0/10`) ne sont **pas** couvertes par l'exception « réseau local » iOS → il faudra **soit une exception ATS**, soit (mieux) **mettre l'agent en HTTPS**. À régler dans le portage iOS.

---

## 6. Récapitulatif des réponses

| Question | Réponse courte |
|---|---|
| Que doit faire l'utilisateur pour connecter l'app ? | Faire tourner l'**agent** sur un hôte qui monte son NAS, puis saisir dans l'app **l'URL de l'agent + un token**. |
| Ça marche sur tous les NAS ? | **Oui** — tout NAS montable en SMB/NFS (QNAP, Synology, TrueNAS…), pas seulement le mien. C'est l'hôte de l'agent qui monte le stockage. |
| Faut-il le Pi ? | **Non** en tant que matériel — il faut **un hôte pour l'agent** ; ça peut être un Pi, un mini-PC, **ou le NAS lui-même** s'il fait tourner Docker. |
| Accès extérieur = Tailscale obligatoire ? | **Non** — Tailscale (simple), WireGuard auto-hébergé (souverain), ou redirection de port. Au choix. |
| Tailscale marche sur iOS ? | **Oui**, app iOS native (WireGuard aussi). Attention au HTTP clair iOS → HTTPS recommandé. |

---

## 7. Mode WebDAV — mise en place pas à pas (validé sur QNAP TS-228)

Le mode **WebDAV** est le mode « grand public » : pas d'agent, pas de Docker, pas de Pi. L'utilisateur active WebDAV sur son NAS et l'app s'y connecte directement. Procédure **vérifiée bout en bout sur un QNAP TS-228 (QTS 4.3.6)** — PUT/lecture/listing/suppression confirmés.

### 7.1 Activer WebDAV sur le NAS (QNAP)
1. **Panneau de configuration → Applications → Serveur Web → onglet WebDAV** → cocher **« Activer WebDAV »**.
2. ⚠️ **LE PIÈGE À CONNAÎTRE — le mode d'autorisation.** Choisir **« Autorisation du dossier partagé »**, *PAS* « Autorisation WebDAV ».
   - Avec **« Autorisation WebDAV »** : QNAP exige un jeu de permissions WebDAV **séparé** ; tant qu'il n'est pas réglé, **la racine WebDAV est vide et les partages renvoient 404** même si le compte a accès au dossier. Symptôme trompeur : l'auth réussit mais on ne voit **aucun** partage.
   - Avec **« Autorisation du dossier partagé »** : WebDAV réutilise **les permissions de partage normales** (déjà en place) → les partages apparaissent immédiatement. **C'est le bon choix.**
3. Noter le **port** (par défaut **5000** HTTP / **5001** HTTPS).

### 7.2 Donner accès au compte
- **Panneau → Privilège → Utilisateurs → (le compte) → Modifier l'autorisation du dossier partagé** : régler **RO** (lecture) ou **RW** (lecture/écriture) sur les dossiers voulus.
- **Panneau → Privilège → Utilisateurs → (le compte) → Modifier le privilège d'application** : cocher **« Accès WebDAV »**.
- (Les comptes Active Directory ne sont **pas** supportés pour WebDAV — comptes locaux uniquement.)

### 7.3 Côté app
- **URL** : `http://IP_DU_NAS:5000` (ou `https://…:5001`). ⚠️ **Sans** nom de partage à la fin — l'app liste elle-même les partages accessibles (la racine WebDAV les énumère).
- **Identifiants** : utilisateur + mot de passe du compte NAS (auth **HTTP Basic**).
- L'app navigue ensuite dans les partages autorisés, uploade (PUT), supprime (corbeille `.photosync-corbeille` par partage), etc.

### 7.4 Limites du mode WebDAV (vs mode Agent)
- ❌ Pas de **miniatures serveur** → générées côté app + cache local.
- ❌ Pas de **transcodage vidéo**, **dédup serveur**, **rangement par date serveur**.
- ⚠️ **HTTP en clair = mot de passe NAS exposé sur le réseau** → privilégier **HTTPS** (port 5001), surtout sur iOS (cf. [HTTPS-MIGRATION.md](HTTPS-MIGRATION.md), [IOS-MIGRATION.md](IOS-MIGRATION.md)).
- ⚠️ Selon le NAS/config, la **racine peut ne pas lister les partages** → l'onboarding doit permettre de saisir directement une URL avec partage (`http://NAS:5000/Download`) en repli.

---

*Voir aussi : [MODES-INSTALLATION.md](MODES-INSTALLATION.md) (choix du mode), [IOS-MIGRATION.md](IOS-MIGRATION.md) (portage iOS), [HTTPS-MIGRATION.md](HTTPS-MIGRATION.md), [WIREGUARD-MIGRATION.md](WIREGUARD-MIGRATION.md) (accès distant souverain), [README.md](README.md) (détails de l'agent).*
