À la demande explicite de l'utilisateur après avoir constaté que tripoli.cloud affichait un 404 vide (le frontend n'avait jamais été déployé) : déploiement du frontend, puis remplacement complet des mocks par de vraies données, des deux côtés. Backend (tripoli-api) : - users.name, files.category (file/photo/backup), table notifications (migration 0002). - POST /api/auth/guest : compte invité réel et réutilisé (invite@tripoli.local), pas une identité simulée côté frontend. - GET /api/files?category=... , GET /api/notifications (+ read-all/ {id}/read), GET /api/storage (used_bytes réel, available_bytes via statvfs - jamais un quota inventé). - Notifications émises réellement sur upload/suppression. Frontend (app/) : - src/api/*.ts appellent réellement tripoli-api (plus de mockFetch). - Upload réel (XHR, vraie progression), suppression avec bouton dédié, vraies miniatures photo (<img src="/api/files/:id">). - Session restaurée au chargement (GET /api/auth/me), logout réel. - Retiré plutôt que simulé : 2FA, clé de récupération, accès partagés, albums, dossiers, "4 appareils sauvegardés", chiffres marketing et claim AES-256 côté client sur la page de connexion (jamais vrai - remplacé par le vrai : HTTPS + Argon2). Déploiement : - Frontend construit et déployé sur le serveur (le build local échouait spécifiquement sur cette machine - rolldown/esbuild natif ne répondait plus, cause non résolue ; contourné en buildant sur le serveur, comme déjà fait pour les crates Rust). - Caddy : /var/www/tripoli-cloud servi en statique pour tripoli.cloud, /api/*, /healthz, /metrics toujours proxyés vers tripoli-edge. - Pipeline CI étendu : le job deploy construit et déploie maintenant aussi le frontend, via un script sudo dédié supplémentaire (tripoli-deploy-frontend.sh) sur le compte deploy sans privilège. - Vérifié en navigateur réel (Chrome) : inscription, connexion, invité, upload avec progression, photos, sauvegardes, notifications, suppression, déconnexion - tous testés en conditions réelles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
98 lines
4.8 KiB
Markdown
98 lines
4.8 KiB
Markdown
# 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 | `{name, email, password}` -> crée le compte + session |
|
|
| `POST /api/auth/login` | non | `{email, password}` -> session |
|
|
| `POST /api/auth/guest` | non | connecte (crée si besoin) le compte invité réel `invite@tripoli.local` |
|
|
| `POST /api/auth/logout` | oui | invalide la session |
|
|
| `GET /api/auth/me` | oui | utilisateur courant |
|
|
| `GET /api/files` | oui | fichiers de l'utilisateur courant, `?category=file\|photo\|backup` |
|
|
| `POST /api/files` | oui | upload multipart, champs `file` et `category` (optionnel) |
|
|
| `GET /api/files/{id}` | oui | téléchargement (vérifie la propriété) |
|
|
| `DELETE /api/files/{id}` | oui | suppression (vérifie la propriété) |
|
|
| `GET /api/notifications` | oui | notifications de l'utilisateur (émises par upload/suppression) |
|
|
| `POST /api/notifications/read-all` | oui | marque tout comme lu |
|
|
| `POST /api/notifications/{id}/read` | oui | marque une notification comme lue |
|
|
| `GET /api/storage` | oui | `used_bytes` (somme réelle des fichiers), `available_bytes` (`statvfs`) |
|
|
|
|
## 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/*.sql`) s'appliquent automatiquement au
|
|
démarrage (`sqlx::migrate!`) - `0002_full_features.sql` ajoute `name`
|
|
aux utilisateurs, `category` aux fichiers, et la table
|
|
`notifications`.
|
|
|
|
## Compte invité
|
|
|
|
"Visiter en tant qu'invité" se connecte à un compte réel
|
|
(`invite@tripoli.local`), créé une seule fois puis réutilisé - jamais
|
|
une identité simulée côté frontend. Son mot de passe est un UUID
|
|
aléatoire jamais communiqué : personne ne peut s'y connecter autrement
|
|
qu'en passant par `POST /api/auth/guest`.
|
|
|
|
## 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).
|