Files
Kazier/V1.md
T

14 KiB

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