chore: purge README.md et V1.md (specs obsolètes) — références basculées vers docs/api-v1.md + V2.md
- README.md (303 l) et V1.md (317 l) : specs aspirationales périmées (SQLite V1, image-picker/MMKV, endpoints tags, OCR Python) - grep complet du repo : 2 seules références, les labels « obsolètes » de AGENTS.md:89 et docs/api-v1.md:5 — rien dans .github/, docker-compose, scripts npm - AGENTS.md:89 + docs/api-v1.md:5 → pointeurs docs/api-v1.md / V2.md
This commit is contained in:
@@ -86,5 +86,4 @@ cd mobile && npm run test:db
|
|||||||
## References
|
## References
|
||||||
|
|
||||||
- `docs/api-v1.md` — **contrat API V1** (autoritatif, consolidé depuis `mobile/api/types.ts`)
|
- `docs/api-v1.md` — **contrat API V1** (autoritatif, consolidé depuis `mobile/api/types.ts`)
|
||||||
- `README.md` / `V1.md` — specs **obsolètes** (bannières en tête de fichier)
|
|
||||||
- `V2.md` — modèle cible Postgres/ReBAC (identifiants en TEXT 32-hex, cf. `docs/api-v1.md`)
|
- `V2.md` — modèle cible Postgres/ReBAC (identifiants en TEXT 32-hex, cf. `docs/api-v1.md`)
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
# VaultDrop — Application de Gestion de Fichiers V1
|
|
||||||
|
|
||||||
> ⚠️ **Obsolète** — cette spec décrit une V1 single-user (SQLite). Référentiel actuel : `docs/api-v1.md` (contrat API) et `V2.md` (modèle cible Postgres/ReBAC).
|
|
||||||
|
|
||||||
## Vision
|
|
||||||
|
|
||||||
Application mobile tout-en-un permettant de centraliser, organiser et retrouver ses documents. Upload depuis l'appareil, scan caméra avec OCR, tagging et recherche rapide. Le back-end Go assure le traitement asynchrone (OCR, indexing) et la persistence. Le front React Native reste léger : il affiche, interagit et met en cache.
|
|
||||||
|
|
||||||
## Scope V1
|
|
||||||
|
|
||||||
On livre un produit fonctionnel utilisable au quotidien, pas un framework. V1 = gestion de fichiers avec scan OCR intégré. Pas de mode collaboratif, pas de plugins, pas de partage avancé.
|
|
||||||
|
|
||||||
## Non-Goals (V1)
|
|
||||||
|
|
||||||
- Mode collaboratif / multi-utilisateur
|
|
||||||
- Système de plugins
|
|
||||||
- Synchronisation offline complexe
|
|
||||||
- Plateforme de partage publique
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Vue d'Ensemble Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────┐ ┌─────────────────────┐
|
|
||||||
│ React Native App │ ────> │ API Go Backend │
|
|
||||||
│ (Expo managed) │ HTTPS │ (REST) │
|
|
||||||
│ - Upload │ │ - Upload fichiers │
|
|
||||||
│ - Caméra / Scan │ │ - OCR async │
|
|
||||||
│ - Tags / Recherche│ │ - Index/search │
|
|
||||||
│ - Affichage │ │ - Persistance │
|
|
||||||
└─────────────────────┘ └─────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
Le mobile ne contient pas de logique métier. Tout traitement lourd (OCR, extraction de texte, indexation) est géré côté back-end.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fonctionnalités Attendues
|
|
||||||
|
|
||||||
### Upload de fichiers
|
|
||||||
- Sélection depuis la galerie appareil
|
|
||||||
- Upload par drag-and-drop interne
|
|
||||||
- Support des formats : PDF, images (JPG, PNG)
|
|
||||||
- Feedback visuel pendant l'envoi (progress)
|
|
||||||
- Retry automatique en cas d'échec réseau
|
|
||||||
|
|
||||||
### Scan Documents (Caméra)
|
|
||||||
- Prise de photo depuis l'app
|
|
||||||
- Recadrage et orientation automatique
|
|
||||||
- Envoi direct vers le back-end pour OCR
|
|
||||||
- Retour du texte extrait affiché à l'utilisateur
|
|
||||||
|
|
||||||
### Tagging
|
|
||||||
- Ajout de tags manuels sur chaque fichier
|
|
||||||
- Suggestion de tags basée sur le contenu OCR
|
|
||||||
- Filtrage par tag dans la liste
|
|
||||||
|
|
||||||
### Recherche
|
|
||||||
- Recherche full-text sur le contenu OCR
|
|
||||||
- Recherche par nom de fichier
|
|
||||||
- Filtres combinés (tag + texte)
|
|
||||||
|
|
||||||
### Listing
|
|
||||||
- Liste des fichiers uploadés avec aperçu
|
|
||||||
- Tri par date, nom, tag
|
|
||||||
- Pagination côté serveur
|
|
||||||
|
|
||||||
### Partage (Optionnel, Phase Later)
|
|
||||||
- Génération de lien temporaire
|
|
||||||
- Pas prioritaire en V1
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Exigences UX
|
|
||||||
|
|
||||||
- UI claire et épurée, minimaliste
|
|
||||||
- Temps de réponse < 2s pour les actions principales
|
|
||||||
- Feedback immédiat sur toutes les interactions
|
|
||||||
- Pas d'écran de chargement > 3s sans spinner
|
|
||||||
- Mode offline minimal : liste des fichiers déjà chargée visible même sans réseau
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Attentes API REST (Back-end Go)
|
|
||||||
|
|
||||||
### Endpoints
|
|
||||||
|
|
||||||
| Méthode | Path | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| GET | /api/v1/files | Liste des fichiers (pagination) |
|
|
||||||
| POST | /api/v1/files/upload | Upload d'un fichier |
|
|
||||||
| GET | /api/v1/files/:id | Détail d'un fichier |
|
|
||||||
| DELETE | /api/v1/files/:id | Suppression d'un fichier |
|
|
||||||
| GET | /api/v1/files/search?q= | Recherche full-text |
|
|
||||||
| POST | /api/v1/files/:id/tags | Ajout de tags |
|
|
||||||
| GET | /api/v1/files/:id/tags | Tags d'un fichier |
|
|
||||||
| POST | /api/v1/ocr/jobs | Soumettre un job OCR |
|
|
||||||
| GET | /api/v1/ocr/jobs/:id | Statut d'un job OCR |
|
|
||||||
| GET | /api/v1/health | Health check |
|
|
||||||
|
|
||||||
### Format de réponse standard
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": { ... },
|
|
||||||
"meta": {
|
|
||||||
"page": 1,
|
|
||||||
"total": 42
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Erreurs
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error": {
|
|
||||||
"code": "FILE_TOO_LARGE",
|
|
||||||
"message": "Fichier dépasse la limite de 50 Mo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Flux de Données
|
|
||||||
|
|
||||||
```
|
|
||||||
[Capture photo] ──> [Upload API] ──> [Back-end stocke]
|
|
||||||
│
|
|
||||||
v
|
|
||||||
[Job OCR créé]
|
|
||||||
│
|
|
||||||
v (async)
|
|
||||||
[Traitement OCR]
|
|
||||||
[Extraction texte]
|
|
||||||
[Indexation search]
|
|
||||||
│
|
|
||||||
v
|
|
||||||
[App affiche fichier] <──── [Polling statut] <── [Résultat prêt]
|
|
||||||
```
|
|
||||||
|
|
||||||
1. L'utilisateur prend une photo ou sélectionne un fichier
|
|
||||||
2. Le fichier est uploadé vers POST /api/v1/files/upload
|
|
||||||
3. Le back-end crée un job OCR et retourne immédiatement un ID
|
|
||||||
4. L'app poll GET /api/v1/ocr/jobs/:id ou utilise un webhook
|
|
||||||
5. Une fois l'OCR terminé, le texte est indexé et le fichier apparaît avec son contenu searchable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Choix Techniques
|
|
||||||
|
|
||||||
### Front-end (React Native)
|
|
||||||
- Expo managed workflow (développement rapide, build plus simple)
|
|
||||||
- TanStack Query (gestion serveur state, cache, refetch)
|
|
||||||
- React Navigation (navigation entre écrans)
|
|
||||||
- react-native-image-picker (sélection + caméra)
|
|
||||||
- MMKV (stockage clé-valeur pour cache local)
|
|
||||||
|
|
||||||
### Back-end (Go)
|
|
||||||
- Framework HTTP : Gin ou Fiber (au choix implémenteur)
|
|
||||||
- Persistance : SQLite pour V1 (volumétrie faible attendue)
|
|
||||||
- OCR : Tesseract en ligne de commande (appel système)
|
|
||||||
- Search : SQLite FTS5 pour la recherche full-text
|
|
||||||
- Stockage fichiers : disque local avec chemin référencé en base
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Structure de Dossiers Suggérée
|
|
||||||
|
|
||||||
### Front-end (React Native / Expo)
|
|
||||||
|
|
||||||
```
|
|
||||||
mobile/
|
|
||||||
├── app/ # Expo Router ou navigation
|
|
||||||
│ ├── index.tsx # Ecran principal (liste)
|
|
||||||
│ ├── upload.tsx # Ecran upload
|
|
||||||
│ ├── scan.tsx # Ecran scan caméra
|
|
||||||
│ └── search.tsx # Ecran recherche
|
|
||||||
├── components/
|
|
||||||
│ ├── FileCard.tsx
|
|
||||||
│ ├── TagChip.tsx
|
|
||||||
│ └── UploadProgress.tsx
|
|
||||||
├── hooks/
|
|
||||||
│ ├── useFiles.ts # TanStack Query hooks
|
|
||||||
│ ├── useSearch.ts
|
|
||||||
│ └── useUpload.ts
|
|
||||||
├── api/
|
|
||||||
│ └── client.ts # Client API (axios ou fetch)
|
|
||||||
├── types/
|
|
||||||
│ └── index.ts # Types TypeScript
|
|
||||||
├── constants/
|
|
||||||
│ └── api.ts # URLs, clés API
|
|
||||||
└── package.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Back-end (Go)
|
|
||||||
|
|
||||||
```
|
|
||||||
backend/
|
|
||||||
├── cmd/
|
|
||||||
│ └── server/
|
|
||||||
│ └── main.go # Point d'entrée
|
|
||||||
├── internal/
|
|
||||||
│ ├── handlers/ # Handlers HTTP
|
|
||||||
│ ├── models/ # Modèles de données
|
|
||||||
│ ├── repository/ # Accès données
|
|
||||||
│ ├── service/ # Logique métier
|
|
||||||
│ └── ocr/ # Module OCR
|
|
||||||
├── pkg/
|
|
||||||
│ └── api/
|
|
||||||
│ └── response.go # Helpers réponse
|
|
||||||
├── uploads/ # Fichiers stockés
|
|
||||||
├── go.mod
|
|
||||||
└── go.sum
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Prérequis
|
|
||||||
|
|
||||||
- Node.js latest lts
|
|
||||||
- npm ou yarn
|
|
||||||
- Expo CLI (`npm install -g expo-cli`)
|
|
||||||
- Go latest lts
|
|
||||||
- Tesseract OCR installé (`apt install tesseract-ocr` sur Debian/Ubuntu)
|
|
||||||
|
|
||||||
### Installation (Front-end)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd mobile
|
|
||||||
npm install
|
|
||||||
npx expo start
|
|
||||||
```
|
|
||||||
|
|
||||||
### Installation (Back-end)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
go mod download
|
|
||||||
go run cmd/server/main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build APK Android (Expo)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx expo run:android --variant release
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Variables d'Environnement
|
|
||||||
|
|
||||||
### Front-end (.env)
|
|
||||||
|
|
||||||
```
|
|
||||||
API_BASE_URL=http://localhost:8080/api/v1
|
|
||||||
TESSERACT_LANG=fr+eng
|
|
||||||
```
|
|
||||||
|
|
||||||
### Back-end (.env)
|
|
||||||
|
|
||||||
```
|
|
||||||
PORT=8080
|
|
||||||
UPLOAD_DIR=./uploads
|
|
||||||
MAX_FILE_SIZE_MB=50
|
|
||||||
OCR_LANG=fra+eng
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Itérations Futures
|
|
||||||
|
|
||||||
### V2 — Fiabilité et Performance
|
|
||||||
- Remplacement SQLite par PostgreSQL
|
|
||||||
- Upload chunked pour gros fichiers
|
|
||||||
- Compression d'images côté client
|
|
||||||
|
|
||||||
### V3 — Organisation Avancée
|
|
||||||
- Dossiers virtuels / hiérarchie
|
|
||||||
- Tags suggérés par IA
|
|
||||||
- OCR multilingue amélioré
|
|
||||||
|
|
||||||
### V4 — Collaboration
|
|
||||||
- Comptes utilisateurs
|
|
||||||
- Partage avec lien temporaire
|
|
||||||
- Rôle et permissions
|
|
||||||
|
|
||||||
### V5 — Plateforme
|
|
||||||
- Système de plugins (event bus)
|
|
||||||
- API publique
|
|
||||||
- Extensions tierces
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Statut du Projet
|
|
||||||
|
|
||||||
Phase : Conception et prototypage
|
|
||||||
Backend Go : En cours de structuration
|
|
||||||
Frontend React Native : À initier
|
|
||||||
@@ -1,317 +0,0 @@
|
|||||||
# VaultDrop Backend — Spécification V1 (État Existant)
|
|
||||||
|
|
||||||
> ⚠️ **Obsolète** — référentiel actuel : `docs/api-v1.md` (contrat API) et `V2.md` (modèle cible). L'OCR est décidé en **Tesseract (appel système)**, pas de microservice Python.
|
|
||||||
|
|
||||||
## 1. Vue d'ensemble
|
|
||||||
|
|
||||||
Application de gestion de documents : upload, scan OCR, tagging, recherche, organisation en dossiers. Backend Go avec API REST, base PostgreSQL, microservice OCR Python séparé.
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────┐ ┌──────────────────────┐
|
|
||||||
│ Mobile App │ HTTPS │ Go Backend (Gin) │
|
|
||||||
│ (React Native) │────────>│ REST API │
|
|
||||||
│ │ │ - Upload / Tags │
|
|
||||||
│ │ │ - Auth (PASETO) │
|
|
||||||
│ │ │ - Thumbnails │
|
|
||||||
└──────────────────┘ │ - Conversion PDF │
|
|
||||||
└────────┬─────────────┘
|
|
||||||
│ HTTP
|
|
||||||
┌────────▼─────────────┐
|
|
||||||
│ OCR Server (Python) │
|
|
||||||
│ PaddleOCR + FastAPI │
|
|
||||||
└──────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Choix Techniques
|
|
||||||
|
|
||||||
| Composant | Technologie |
|
|
||||||
|---|---|
|
|
||||||
| Framework HTTP | Gin v1.12 |
|
|
||||||
| Base de données | PostgreSQL (lib/pq) |
|
|
||||||
| ORM/Query | sqlc v1.31 (généré) |
|
|
||||||
| Migrations | golang-migrate v4 |
|
|
||||||
| Auth | PASETO v4 (go-paseto) + Argon2id |
|
|
||||||
| OCR | PaddleOCR (microservice Python séparé) |
|
|
||||||
| Conversion PDF | LibreOffice + pdftoppm (appels système) |
|
|
||||||
| Checksum | SHA-256 |
|
|
||||||
| Stockage fichiers | Disque local (`./uploads/`) |
|
|
||||||
| URLs signées | HMAC-SHA256 avec expiry |
|
|
||||||
|
|
||||||
## 3. Structure du Code
|
|
||||||
|
|
||||||
```
|
|
||||||
backend/
|
|
||||||
├── cmd/server/main.go # Point d'entrée
|
|
||||||
├── internal/
|
|
||||||
│ ├── auth/ # Auth (3 fichiers)
|
|
||||||
│ │ ├── service.go # Register, Login, Refresh, Logout, ValidateAccessToken
|
|
||||||
│ │ ├── handler.go # HTTP handlers auth
|
|
||||||
│ │ └── middleware.go # RequireAuth() middleware
|
|
||||||
│ ├── config/config.go # Config struct (11 vars d'env)
|
|
||||||
│ ├── db/
|
|
||||||
│ │ ├── connect.go # Connexion PostgreSQL
|
|
||||||
│ │ ├── migrate.go # golang-migrate
|
|
||||||
│ │ ├── db.go # sqlc Queries interface
|
|
||||||
│ │ ├── models.go # sqlc models (généré)
|
|
||||||
│ │ ├── files.sql.go # sqlc queries fichiers (généré)
|
|
||||||
│ │ ├── tags.sql.go # sqlc queries tags (généré)
|
|
||||||
│ │ ├── thumbnails.sql.go # sqlc queries thumbnails (généré)
|
|
||||||
│ │ ├── health.sql.go # sqlc health check
|
|
||||||
│ │ ├── auth.sql.go # sqlc queries auth (généré)
|
|
||||||
│ │ ├── queries/ # SQL sources (5 fichiers .sql)
|
|
||||||
│ │ └── migrations/ # 11 migrations (001→011)
|
|
||||||
│ ├── handler/
|
|
||||||
│ │ ├── handler.go # Handler struct + constructor
|
|
||||||
│ │ ├── router.go # SetupRoutes()
|
|
||||||
│ │ ├── files.go # 14 handlers fichiers/thumbnails
|
|
||||||
│ │ ├── ocr.go # OCR stubs (501/404)
|
|
||||||
│ │ └── health.go # Health check
|
|
||||||
│ ├── model/
|
|
||||||
│ │ ├── file.go # File, Tag, UploadResult structs
|
|
||||||
│ │ ├── ocrjob.go # OcrJob struct
|
|
||||||
│ │ ├── tag.go # (vide)
|
|
||||||
│ │ └── thumbnail.go # Thumbnail struct
|
|
||||||
│ ├── ocr/
|
|
||||||
│ │ ├── client.go # Client HTTP PaddleOCR + détection doc type
|
|
||||||
│ │ └── types.go # OCR request/response types
|
|
||||||
│ └── service/
|
|
||||||
│ ├── file.go # FileService: upload, list, get, delete, tags, folders, thumbnails, dedup
|
|
||||||
│ ├── ocr.go # OCRService: worker async, enqueue, process
|
|
||||||
│ ├── conversion.go # ConversionService: PDF→images, thumbnails multi-résolution
|
|
||||||
│ ├── url.go # URLService: signed URLs HMAC
|
|
||||||
│ └── checksum.go # SHA-256 hash helpers
|
|
||||||
├── pkg/api/response.go # Helpers: Success, Created, Paginated, Error
|
|
||||||
├── ocr-server/
|
|
||||||
│ ├── server.py # FastAPI + PaddleOCR
|
|
||||||
│ └── Dockerfile
|
|
||||||
├── uploads/ # Stockage fichiers + thumbnails
|
|
||||||
├── sqlc.yaml
|
|
||||||
├── Dockerfile
|
|
||||||
├── go.mod
|
|
||||||
└── .env
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Base de Données (Schéma Final)
|
|
||||||
|
|
||||||
### Tables
|
|
||||||
|
|
||||||
**files**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| name | TEXT | NOT NULL |
|
|
||||||
| mime_type | TEXT | NOT NULL DEFAULT '' |
|
|
||||||
| size | BIGINT | NOT NULL DEFAULT 0 |
|
|
||||||
| storage_key | TEXT | NOT NULL DEFAULT '' |
|
|
||||||
| checksum | TEXT | NOT NULL DEFAULT '' |
|
|
||||||
| ocr_text | TEXT | NOT NULL DEFAULT '' |
|
|
||||||
| is_folder | BOOLEAN | NOT NULL DEFAULT false |
|
|
||||||
| parent_file_id | TEXT | FK → files(id) |
|
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
|
|
||||||
**tags**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| parent_tag_id | TEXT | FK → tags(id) |
|
|
||||||
| tag_name | TEXT | NOT NULL |
|
|
||||||
| tag_type | TEXT | NOT NULL DEFAULT 'none' |
|
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
|
|
||||||
**file_tags**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| tag_id | TEXT | FK → tags(id) |
|
|
||||||
| file_id | TEXT | FK → files(id) |
|
|
||||||
|
|
||||||
**users**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| username | TEXT | NOT NULL, UNIQUE |
|
|
||||||
| password_hash | TEXT | NOT NULL |
|
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
|
|
||||||
**refresh_tokens**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| user_id | TEXT | FK → users(id), ON DELETE CASCADE |
|
|
||||||
| token_hash | TEXT | NOT NULL |
|
|
||||||
| expires_at | TIMESTAMP | NOT NULL |
|
|
||||||
| revoked | BOOLEAN | DEFAULT FALSE |
|
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
|
|
||||||
**thumbnails**
|
|
||||||
|
|
||||||
| Colonne | Type | Contraintes |
|
|
||||||
|---|---|---|
|
|
||||||
| id | TEXT | PK, DEFAULT gen_random_uuid() |
|
|
||||||
| file_id | TEXT | FK → files(id), ON DELETE CASCADE |
|
|
||||||
| page_number | INTEGER | NOT NULL |
|
|
||||||
| resolution_label | TEXT | NOT NULL |
|
|
||||||
| width | INTEGER | NOT NULL |
|
|
||||||
| height | INTEGER | NOT NULL |
|
|
||||||
| storage_key | TEXT | NOT NULL |
|
|
||||||
| mime_type | TEXT | DEFAULT 'image/jpeg' |
|
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
|
|
||||||
|
|
||||||
### Index
|
|
||||||
|
|
||||||
- `idx_refresh_tokens_user_id` ON refresh_tokens(user_id)
|
|
||||||
- `idx_refresh_tokens_token_hash` ON refresh_tokens(token_hash)
|
|
||||||
- `idx_thumbnails_file_id` ON thumbnails(file_id)
|
|
||||||
- `idx_thumbnails_unique` UNIQUE ON thumbnails(file_id, page_number, resolution_label)
|
|
||||||
|
|
||||||
## 5. API Endpoints
|
|
||||||
|
|
||||||
### Public
|
|
||||||
|
|
||||||
| Méthode | Path | Handler | Description |
|
|
||||||
|---|---|---|---|
|
|
||||||
| GET | `/api/v1/health` | HealthHandler.Check | Retourne `{status: "healthy"}` |
|
|
||||||
| POST | `/api/v1/auth/register` | AuthHandler.Register | Créer un compte (username + password) |
|
|
||||||
| POST | `/api/v1/auth/login` | AuthHandler.Login | Login → access_token + refresh_token |
|
|
||||||
| POST | `/api/v1/auth/refresh` | AuthHandler.Refresh | Renouveler les tokens |
|
|
||||||
| POST | `/api/v1/auth/logout` | AuthHandler.Logout | Révoquer le refresh token |
|
|
||||||
| GET | `/api/v1/files/download/:id` | FileHandler.Download | Télécharger un fichier (URL signée) |
|
|
||||||
| GET | `/api/v1/thumbnails/:id` | FileHandler.ServeThumbnail | Télécharger une thumbnail (URL signée) |
|
|
||||||
|
|
||||||
### Protégées (Bearer token)
|
|
||||||
|
|
||||||
| Méthode | Path | Handler | Description |
|
|
||||||
|---|---|---|---|
|
|
||||||
| GET | `/api/v1/files` | FileHandler.List | Lister tous les fichiers (racine) |
|
|
||||||
| POST | `/api/v1/files/upload` | FileHandler.Upload | Upload multi-fichiers (multipart) |
|
|
||||||
| POST | `/api/v1/files/move` | FileHandler.MoveFiles | Déplacer des fichiers vers un dossier |
|
|
||||||
| POST | `/api/v1/files/folders` | FileHandler.CreateFolder | Créer un dossier |
|
|
||||||
| GET | `/api/v1/files/folders` | FileHandler.ListFolders | Lister les dossiers |
|
|
||||||
| GET | `/api/v1/files/folders/:id/files` | FileHandler.ListFilesByParent | Lister les fichiers d'un dossier |
|
|
||||||
| DELETE | `/api/v1/files/:id` | FileHandler.Delete | Supprimer un fichier (+ thumbnails + fichier disque) |
|
|
||||||
| GET | `/api/v1/files/:id` | FileHandler.Get | Détail d'un fichier (+ tags + thumbnails) |
|
|
||||||
| POST | `/api/v1/files/:id/tags` | FileHandler.AddTags | Ajouter des tags à un fichier |
|
|
||||||
| GET | `/api/v1/files/:id/tags` | FileHandler.GetTags | Récupérer les tags d'un fichier |
|
|
||||||
| POST | `/api/v1/files/:id/thumbnails` | FileHandler.GetThumbnails | Lister les thumbnails d'un fichier |
|
|
||||||
| POST | `/api/v1/files/dedup-check` | FileHandler.CheckDuplicates | Vérifier les doublons (nom + taille) |
|
|
||||||
| POST | `/api/v1/ocr/jobs` | OCRHandler.CreateJob | **STUB** — retourne 501 |
|
|
||||||
| GET | `/api/v1/ocr/jobs/:id` | OCRHandler.GetJobStatus | **STUB** — retourne 404 |
|
|
||||||
|
|
||||||
### Format de réponse
|
|
||||||
|
|
||||||
Succès : `{ "data": ... }` ou `{ "data": ..., "meta": { "page": N, "total": N } }`
|
|
||||||
Erreur : `{ "error": { "code": "...", "message": "..." } }`
|
|
||||||
|
|
||||||
## 6. Services Métier
|
|
||||||
|
|
||||||
### FileService (service/file.go — 349 lignes)
|
|
||||||
|
|
||||||
- `Upload(file)` — Upload multipart, checksum SHA-256, détection doublons par checksum, stockage disque
|
|
||||||
- `List()` — Tous les fichiers racine (parent_file_id IS NULL), avec tags
|
|
||||||
- `Get(id)` — Fichier par ID avec tags
|
|
||||||
- `Delete(id)` — Suppression DB (handler gère suppression disque + thumbnails)
|
|
||||||
- `GetStoragePath(id)` — Chemin disque du fichier
|
|
||||||
- `UpdateOCRText(id, text)` — Mise à jour du champ ocr_text
|
|
||||||
- `AddTags(fileID, tags, tagType)` — Création auto des tags + liaison
|
|
||||||
- `GetTagsByFileID(fileID)` — Tags d'un fichier
|
|
||||||
- `MoveFiles(fileIDs, parentFileID)` — Déplacer vers un dossier (ou racine si nil)
|
|
||||||
- `CreateFolder(name)` — Créer un dossier
|
|
||||||
- `ListFolders()` — Lister tous les dossiers
|
|
||||||
- `ListFilesByParentID(parentID)` — Fichiers d'un dossier
|
|
||||||
- `GetThumbnailsByFileID(fileID)` — Thumbnails d'un fichier
|
|
||||||
- `GetThumbnailStoragePath(id)` — Chemin disque thumbnail
|
|
||||||
- `GetBestThumbnail(fileID, label)` — Meilleure thumbnail (page 1, label préféré ou fallback)
|
|
||||||
- `FindDuplicatesByNameSize(name, size)` — Recherche doublons par nom+taille
|
|
||||||
|
|
||||||
### OCRService (service/ocr.go — 95 lignes)
|
|
||||||
|
|
||||||
- `Start()` — Lance le worker goroutine
|
|
||||||
- `Stop()` — Ferme le channel jobs
|
|
||||||
- `Enqueue(fileID, filePath)` — Ajoute un job OCR (channel buffer 100)
|
|
||||||
- `process(job)` — Lecture fichier → Client OCR → Flatten text → UpdateOCRText
|
|
||||||
- `RecognizeFromBytes(data)` — OCR direct (pas utilisé par les handlers)
|
|
||||||
- `HealthCheck()` — Vérifie l'OCR server
|
|
||||||
|
|
||||||
### ConversionService (service/conversion.go — 253 lignes)
|
|
||||||
|
|
||||||
- `Start()` / `Stop()` — Worker goroutine
|
|
||||||
- `Enqueue(fileID, filePath, mimeType)` — Conversion async (channel buffer 100)
|
|
||||||
- `process(job)` :
|
|
||||||
1. Si document Office → conversion vers PDF via LibreOffice headless
|
|
||||||
2. Si PDF → direct
|
|
||||||
3. PDF → images via pdftoppm à 2 résolutions : "thumbnail" (21 DPI) et "full" (200 DPI)
|
|
||||||
4. Création des enregistrements thumbnails en DB
|
|
||||||
- Supporte : PDF, .docx, .xlsx, .pptx, formats OpenDocument
|
|
||||||
|
|
||||||
### URLService (service/url.go — 67 lignes)
|
|
||||||
|
|
||||||
- `GenerateDownloadURL(fileUUID)` — URL signée HMAC avec expiry (défaut 60 min)
|
|
||||||
- `GenerateThumbnailURL(thumbUUID)` — Idem pour thumbnails
|
|
||||||
- `Validate(fileID, sig, expires)` — Vérifie signature HMAC + expiry
|
|
||||||
|
|
||||||
### Auth (auth/ — 250+135+41 lignes)
|
|
||||||
|
|
||||||
- **Register** : validation username (3-30 chars) + password (8+ chars), Argon2id hash, PASETO v4 tokens
|
|
||||||
- **Login** : vérification credentials, émission token pair
|
|
||||||
- **Refresh** : révoque l'ancien refresh token, émet un nouveau pair
|
|
||||||
- **Logout** : révoque le refresh token
|
|
||||||
- **Middleware** : Bearer token extraction → ValidateAccessToken → set userID in context
|
|
||||||
- Access token TTL : 30 min | Refresh token TTL : 7 jours
|
|
||||||
|
|
||||||
## 7. OCR Server (Python)
|
|
||||||
|
|
||||||
**ocr-server/server.py** — FastAPI + PaddleOCR
|
|
||||||
|
|
||||||
- `GET /health` — health check
|
|
||||||
- `POST /ocr` — OCR via JSON base64
|
|
||||||
- `POST /ocr/upload` — OCR via upload fichier
|
|
||||||
- Supporte images (JPEG/PNG) et PDF (conversion vers images via pypdfium2)
|
|
||||||
- Langue par défaut : français (`OCR_LANG=fr`)
|
|
||||||
- Timeout client Go : 120 secondes
|
|
||||||
|
|
||||||
## 8. Détection de Doublons
|
|
||||||
|
|
||||||
Deux mécanismes :
|
|
||||||
|
|
||||||
1. **Par checksum (SHA-256)** : lors de l'upload, si un fichier avec le même checksum existe déjà, le fichier uploadé est supprimé et l'ID existant retourné
|
|
||||||
2. **Par nom+taille** : endpoint `POST /files/dedup-check` pour vérifier avant upload
|
|
||||||
|
|
||||||
## 9. Variables d'Environnement
|
|
||||||
|
|
||||||
| Variable | Défaut | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| PORT | `8080` | Port du serveur |
|
|
||||||
| DATABASE_URL | `postgres://localhost:5432/vaultdrop?sslmode=disable` | URL PostgreSQL |
|
|
||||||
| OCR_ENDPOINT | `http://localhost:9090` | URL du microservice OCR |
|
|
||||||
| UPLOAD_DIR | `./uploads` | Répertoire de stockage |
|
|
||||||
| HMAC_SECRET | `thisismyrandomstring` | Secret pour URLs signées |
|
|
||||||
| SERVER_HOST | `http://192.168.1.17:8080` | Host public du serveur |
|
|
||||||
| PASETO_KEY | (clé hex 64 car.) | Clé symétrique PASETO v4 |
|
|
||||||
| LIBREOFFICE_PATH | `/usr/bin/libreoffice` | Chemin LibreOffice |
|
|
||||||
| PDFTOPPM_PATH | `/usr/bin/pdftoppm` | Chemin pdftoppm |
|
|
||||||
| THUMBNAIL_DIR | `./uploads/thumbnails` | Répertoire thumbnails |
|
|
||||||
| URL_EXPIRY_MINUTES | `60` | Durée de vie des URLs signées |
|
|
||||||
|
|
||||||
## 10. Docker
|
|
||||||
|
|
||||||
**Backend** : Multi-stage build (golang:1.24-alpine → alpine:3.21 avec libreoffice-core + poppler-utils)
|
|
||||||
|
|
||||||
**OCR Server** : Dockerfile séparé dans `ocr-server/`
|
|
||||||
|
|
||||||
## 11. Ce qui manque / Stubs
|
|
||||||
|
|
||||||
- `OCRHandler.CreateJob` → retourne 501 NOT_IMPLEMENTED
|
|
||||||
- `OCRHandler.GetJobStatus` → retourne 404 JOB_NOT_FOUND
|
|
||||||
- Pas de recherche full-text (pas de FTS5/PostgreSQL tsvector)
|
|
||||||
- Pas de pagination côté DB (tout est chargé en mémoire)
|
|
||||||
- Pas de gestion d'erreurs robuste dans le worker OCR (logs seulement)
|
|
||||||
- Les fichiers n'ont pas de lien avec les utilisateurs (pas de user_id sur files)
|
|
||||||
- Le health check ne vérifie pas la DB ni l'OCR server
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
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 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.
|
||||||
|
|
||||||
Références : `V2.md` (modèle cible), `mobile/services/db/` (conventions sync), `README.md`/`V1.md` (**obsolètes**).
|
Références : `V2.md` (modèle cible), `mobile/services/db/` (conventions sync), `AGENTS.md` (structure + commandes).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user