# private-server

Serveur familial autonome : SQLite mono-fichier, zéro service externe (pas de
Nextcloud, pas de Postgres, pas de Redis). Un serveur = un propriétaire.

Architecture : un **cœur** (`src/core/` — identité, auth, setup, Caddy,
maintenance — qui ne connaît aucun module) et des **modules d'app** greffés
dessus (`src/modules/privatefamily/` — familles, invitations, médias, social,
albums). Le cœur ne référence jamais les tables d'un module ; un module peut
référencer `core.users`.

## Développement

```bash
pnpm install                # à la racine du monorepo
pnpm build                  # tsc → dist/
pnpm start                  # node dist/index.js (port 4100)
pnpm dev                    # tsx watch src/index.ts
```

Tests API : serveur lancé, puis cycle curl (voir « Vérification »).

### Migrations (drizzle)

```bash
pnpm db:generate            # build + drizzle-kit generate (le schéma est lu
                            # depuis dist/ — contrainte du loader de drizzle-kit)
```

Les migrations s'appliquent automatiquement au démarrage.

## Configuration

Deux niveaux :

- **Variables d'environnement** (`PORT`, `DATA_DIR`, `NODE_ENV`, `LOG_LEVEL`,
  `ACCESS_TOKEN_TTL`, `REFRESH_TOKEN_TTL_DAYS`, `MAINTENANCE_KEY_TTL`,
  `JWT_ACCESS_SECRET`, `JWT_REFRESH_SECRET`, `JWT_MAINTENANCE_SECRET`) —
  les secrets JWT sont optionnels : s'ils ne sont pas définis, des secrets
  aléatoires sont générés et persistés dans `config.json`.
- **`DATA_DIR/config.json`** — config d'installation : `domain`,
  `setupDoneAt`, `adminEmail` + les secrets générés. Créé automatiquement au
  premier démarrage, complété par l'assistant.

## Commandes CLI

```bash
node dist/cli.js setup                  # assistant de premier lancement
node dist/cli.js reset-admin-password   # mot de passe oublié (révoque tous
                                        # les appareils du compte)
node dist/cli.js version
```

### Assistant de premier lancement

À la première installation : choix du domaine (ou accès direct par IP),
instructions DNS (record A → IP publique), génération du Caddyfile (HTTPS
automatique Let's Encrypt via Caddy), création du compte admin + de la
famille initiale, affichage final URL + identifiants.

Non-interactif (usage scripté) :

```bash
node dist/cli.js setup --domain photos.exemple.fr --email moi@exemple.fr \
  --password secret123 --family "Ma famille" --yes --no-caddy
```

Le serveur démarre sans setup (accessible par IP) — il affiche un
avertissement « premier lancement » dans les logs jusqu'à ce que l'assistant
ait tourné. `GET /health` expose `setupDone`.

### Caddy

`src/core/proxy/caddy.ts` génère le Caddyfile et tente de l'appliquer :
`systemctl reload caddy` sur Linux, sinon `caddy reload --config`. Si aucun
Caddy n'est présent, le fichier reste écrit (`/etc/caddy/Caddyfile` si
inscriptible, sinon `DATA_DIR/Caddyfile`) — à activer manuellement.

## Clé de maintenance temporaire

Accès support à durée de vie courte, généré par le propriétaire (compte du
setup) — jamais d'accès permanent côté FamilyAlbum.

```bash
curl -X POST https://photos.exemple.fr/maintenance/keys \
  -H "Authorization: Bearer <token d'accès>" -d '{"label":"support aout 2026"}'
# → { key: "<jwt 30 min, scope maintenance, secret dédié>", expiresAt }
curl https://photos.exemple.fr/maintenance/status \
  -H "Authorization: Bearer <key>"
```

- La clé est signée avec un **secret dédié** : elle ne peut jamais passer
  l'authentification normale de l'API.
- Seul son hash est stocké ; elle n'est affichée qu'une fois.
- `GET /maintenance/keys` (liste tracée) et `DELETE /maintenance/keys/:id`
  (révocation immédiate, contrôlée à chaque usage).
- TTL : `MAINTENANCE_KEY_TTL` (défaut `30m`).

## Packaging

### Linux (Ubuntu/Debian — sert aussi de base pour les VM FamilyAlbum)

`packaging/linux/install.sh` — script d'install root : utilisateur système
`private-server`, package déployé dans `/opt/private-server` (via
`pnpm deploy`), unité systemd durcie, Caddy installé depuis le repo officiel
avec le Caddyfile du domaine, assistant de premier lancement, récap final.

```bash
sudo ./packaging/linux/install.sh --domain photos.exemple.fr \
  --admin-email moi@exemple.fr
```

Opérations : `systemctl status|restart private-server`,
`journalctl -u private-server -f`. Données : `/var/lib/private-server`.

### Windows

`packaging/windows/install.ps1` — en admin : déploiement du package dans
`C:\Program Files\private-server`, service Windows via NSSM (téléchargé
automatiquement), données dans `C:\ProgramData\private-server`, assistant de
premier lancement, récap final. Caddy en option manuelle (le Caddyfile est
généré dans le dossier de données).

## Vérification (cycle API de référence)

```bash
# 1. register (crée user + famille)  → tokens
curl -X POST http://127.0.0.1:4100/auth/register -H "Content-Type: application/json" \
  -d '{"email":"a@b.c","password":"secret123","displayName":"A","familyName":"Famille"}'
# 2. login / refresh / logout, me
curl -X POST http://127.0.0.1:4100/auth/login -H "Content-Type: application/json" \
  -d '{"email":"a@b.c","password":"secret123"}'
# 3. invitation (code) + join (POST /join, public)
# 4. upload multipart (file + capturedAt) → vérifier le fichier sur disque
curl -F "file=@photo.jpg" -F "capturedAt=2026-08-01T12:00:00Z" \
  http://127.0.0.1:4100/families/:familyId/media/upload -H "Authorization: Bearer $T"
# 5. miniature (GET /media/:id/thumb), original + Range (206/416)
# 6. commentaires / réactions / vus par (GET /media/:id/social)
# 7. albums (POST /families/:familyId/albums, POST /albums/:id/media)
# 8. réglages sociaux (PATCH /families/:id/social) → blocage serveur 403
# 9. suppression média → fichier + miniatures purgés du disque
```
