add gitlab ci

This commit is contained in:
m
2026-09-16 14:47:10 +02:00
parent 906d48702c
commit cd4f0179fc
32 changed files with 1462 additions and 40 deletions
+7 -3
View File
@@ -1,6 +1,6 @@
# VaultDrop — Contrat API V1 (autoritatif)
Status : **autoritatif**. Le client mobile est la source de vérité : `mobile/api/types.ts` + `mobile/api/client.ts` sont implémentés et testés ; le serveur Go doit les matcher exactement (méthode + path + enveloppe), il ne re-négocie pas. Document consolidé à partir de ces deux fichiers — toute divergence de ce doc doit être portée dans le client d'abord.
Status : **autoritatif**. Le client mobile Kotlin est la source de vérité : `data/remote/ApiService.kt` + `data/remote/dto/Dtos.kt` (contrat Retrofit/Moshi) sont implémentés ; le serveur Go doit les matcher exactement (méthode + path + enveloppe), il ne re-négocie pas. Document consolidé depuis le client — toute divergence de ce doc doit être portée dans le client d'abord.
Références : `V2.md` (modèle cible), `mobile/services/db/` (conventions sync), `AGENTS.md` (structure + commandes).
@@ -8,7 +8,7 @@ Références : `V2.md` (modèle cible), `mobile/services/db/` (conventions sync)
## 1. Base et transport
- Base URL serveur : schéma + host configurés côté client via `EXPO_PUBLIC_API_BASE_URL` (défaut `http://localhost:8080/api/v1`).
- Base URL serveur : configurable à l'exécution dans les Réglages de l'appli (défaut `http://10.0.2.2:8080/api/v1`).
- JSON partout, sauf `POST /files/upload` (multipart).
- Enveloppe succès : `{ "data": T, "meta"?: { "page": int, "pageSize": int, "total": int } }` (`meta` présent sur les listes paginées).
- Erreur : `{ "error": { "code": string, "message": string } }` + statut HTTP adéquat.
@@ -43,10 +43,11 @@ Références : `V2.md` (modèle cible), `mobile/services/db/` (conventions sync)
| POST | `/files/upload` | multipart : `file` (uri/name/type), `folderId?` | `FileDto` | `FILE_TOO_LARGE` |
| POST | `/ocr/jobs` | `{ "fileId": "…" }` | `OcrJob` | — |
| GET | `/ocr/jobs/:id` | — | `OcrJob` | `NOT_FOUND` |
| GET | `/files/:id/ocr` | — | `FileOcr` (`{ text, updatedAt }`) | `NOT_FOUND` |
| POST | `/sync/ops` | voir §6 | voir §6 | — |
| GET | `/sync/permissions` | query `after?` (cached_at ms) | `ResourcePermission[]` | — |
> **Lecture partagée** : `GET /files`, `GET /files/:id`, `GET /files/search` et `GET /files/folders` couvrent les ressources **possédées ET partagées** (accès `viewer+`, §6.2). Le rename d'une ressource partagée passe par l'outbox `update_metadata` (nécessite `editor+`, §6.1). `DELETE /files/:id` reste owner-only.
> **Lecture partagée** : `GET /files`, `GET /files/:id`, `GET /files/search`, `GET /files/:id/ocr` et `GET /files/folders` couvrent les ressources **possédées ET partagées** (accès `viewer+`, §6.2). Le rename d'une ressource partagée passe par l'outbox `update_metadata` (nécessite `editor+`, §6.1). `DELETE /files/:id` reste owner-only.
### DTOs (copie conforme de `mobile/api/types.ts`)
@@ -64,6 +65,7 @@ type FileDto = {
type FolderDto = { id: string; name: string; parentId?: string | null };
type OcrJobStatus = 'queued' | 'processing' | 'done' | 'failed';
type OcrJob = { id: string; status: OcrJobStatus; text?: string | null; error?: string | null };
type FileOcr = { text: string; updatedAt?: string | null };
```
## 4. Upload
@@ -79,6 +81,8 @@ type OcrJob = { id: string; status: OcrJobStatus; text?: string | null; error?:
- `GET /ocr/jobs/:id` → statut. Le mobile **poll toutes les 3s** jusqu'à `done`/`failed` (`hooks/useUpload.ts`). Cycle : `queued → processing → done | failed` ; `done` renvoie `text`, `failed` renvoie `error`.
- Moteur : **Tesseract en appel système** (`ocr/tesseract.go`), langue `OCR_LANG` (défaut `fra+eng`). Les images sont passées directement à `tesseract` ; les **PDF** subissent une extraction du calque texte (`ledongthuc/pdf`, déjà en go.mod) — un PDF scanné produit un texte vide plutôt qu'un rendu/OCR (hors scope V1).
- `fileId` inconnu/pas du user → `NOT_FOUND`. Fichier physique introuvable (ex. suppression manuelle sous `UPLOAD_DIR`) → job `failed` `"file not readable"`. Le job est créé par le device courant (`ocr_jobs.device_id`) mais la validation de la ressource est scopée par le **user**.
- **Le texte atterrit sur la ressource** : à la complétion (`done` avec texte non vide), le worker écrit l'extrait trimé dans `resources.ocr_text` (et **bump `updated_at`**, d'où une injection dans le delta outbox/snapshot). `ocr_jobs.text` reste l'historique du job.
- **`GET /files/:id/ocr`** → `FileOcr { text, updatedAt }` : l'extrait server-side du fichier, lisible par le **possédant ET les grantees** (`viewer+`, même portée que `GET /files/:id` ; `text` `""` si pas encore extrait). Un **second device** rapatrie ainsi le texte d'un OCR fait par un autre device (ou par la ressource partagée) **sans re-téléverser les octets ni re-soumettre de job** — le client Kotlin le fait en tête de `OcrRepository.syncFromServer`/`processAuto` et du viewer (`OcrViewModel`).
## 6. Contrat de sync (outbox + snapshot)