# tripoli-api Auth, fichiers, métadonnées PostgreSQL. Axum + sqlx (requêtes vérifiées à l'exécution, pas les macros `query!` à vérification compile-time : elles auraient exigé une base joignable à chaque `cargo build`, y compris depuis un poste qui n'a pas le tunnel WireGuard monté - plus de complexité que ce que ça apporte pour un projet de cette taille). ## Auth Sessions opaques côté serveur (table `sessions`, cookie `tripoli_session` `HttpOnly` + `Secure` + `SameSite=Lax`), pas de JWT : révoquer une session est une suppression de ligne, pas une histoire de clé de signature à faire tourner. Mots de passe hachés avec Argon2 (`argon2` + `password-hash`, implémentations RustCrypto). ## Endpoints | Route | Auth | Rôle | |---|---|---| | `GET /healthz` | non | ping PostgreSQL | | `POST /api/auth/register` | non | `{email, password}` -> crée le compte + session | | `POST /api/auth/login` | non | `{email, password}` -> session | | `POST /api/auth/logout` | oui | invalide la session | | `GET /api/auth/me` | oui | utilisateur courant | | `GET /api/files` | oui | fichiers de l'utilisateur courant | | `POST /api/files` | oui | upload multipart, champ `file` | | `GET /api/files/{id}` | oui | téléchargement (vérifie la propriété) | | `DELETE /api/files/{id}` | oui | suppression (vérifie la propriété) | ## Stockage Chaque fichier est un blob sur disque nommé par son UUID (`TRIPOLI_BLOB_DIR`, `/var/lib/tripoli-api/blobs` par défaut) - jamais par le nom d'origine, qui ne vit qu'en métadonnée PostgreSQL. Ça évite toute question de traversée de chemin ou de caractères spéciaux dans un nom de fichier envoyé par un client. Stockage disque, pas Hetzner Object Storage pour cette v1 : le brief laissait le choix, et Object Storage aurait ajouté un client S3 et des identifiants supplémentaires sans bénéfice pour un unique VPS avec 55 Go disponibles. À revisiter si l'espace disque devient une contrainte réelle. L'upload multipart est écrit sur disque chunk par chunk (`field.chunk().await` -> `AsyncWriteExt::write_all`), jamais accumulé en mémoire : c'est ce qui rend la progression d'upload réelle côté client - le corps de la requête part effectivement au fil de l'eau. ## Configuration | Variable | Défaut | Rôle | |---|---|---| | `TRIPOLI_API_LISTEN` | `127.0.0.1:8081` | Adresse d'écoute (jamais publique - tripoli-edge est le seul appelant) | | `DATABASE_URL` | — (requis) | `postgres://...` | | `TRIPOLI_BLOB_DIR` | `/var/lib/tripoli-api/blobs` | Stockage des blobs | | `TRIPOLI_SESSION_TTL_HOURS` | `720` (30 jours) | Durée de vie d'une session | ## Déploiement (fait manuellement pour cette phase, même remarque que tripoli-edge) - PostgreSQL dans un conteneur Docker dédié (`/opt/tripoli-api/docker-compose.yml`), **séparé de celui d'iot-alarm-system** : pas de couplage entre projets qui n'ont rien à voir. Exposé uniquement sur `127.0.0.1:5433` - tripoli-api tourne nativement sur l'hôte, pas besoin de l'exposer sur un réseau Docker. Mot de passe généré côté serveur (`/opt/tripoli-api/.env`, jamais commité, jamais affiché dans un terminal). - tripoli-api tourne en systemd (`deploy/tripoli-api.service`) sous le même utilisateur système sans privilège `tripoli` que tripoli-edge. - Les migrations (`migrations/0001_init.sql`) s'appliquent automatiquement au démarrage (`sqlx::migrate!`). ## Vérifié en production ``` curl https://tripoli.cloud/healthz -> {"status":"ok"} curl -X POST https://tripoli.cloud/api/auth/register -> compte + session curl https://tripoli.cloud/api/auth/me (avec cookie) -> utilisateur courant curl -F file=@... https://tripoli.cloud/api/files -> upload curl https://tripoli.cloud/api/files/{id} -> download ``` Testé aussi : accès sans cookie (401), email déjà pris (409), mauvais mot de passe (401), double suppression (404 la seconde fois).