Appearance
Sources — Import fichier vers table PostgreSQL
Le module Sources permet de charger un fichier déposé (CSV, Excel, JSON) directement dans une table PostgreSQL dédiée à l'entreprise, sans écrire de SQL.
Vue d'ensemble
mermaid
sequenceDiagram
participant Admin
participant Frontend
participant Backend
participant S3
participant PostgreSQL
Admin->>Frontend: Sélectionne fichier + nom source + nom table
Frontend->>Backend: POST /sources {documentUuid, name, tableName}
Backend->>Backend: Vérifie accès document (enterprise scope)
Backend->>S3: Télécharge le buffer du fichier
S3-->>Backend: Buffer binaire
Backend->>Backend: Parse CSV / Excel / JSON → headers + rows
Backend->>Backend: Sanitise les en-têtes (unicode → ASCII, dédup)
Backend->>PostgreSQL: CREATE SCHEMA IF NOT EXISTS schema_{uuid}
Backend->>PostgreSQL: DROP TABLE IF EXISTS + CREATE TABLE
Backend->>PostgreSQL: INSERT par batch de 1000 lignes (transaction)
Backend->>Backend: Sauvegarde sources (Prisma)
Backend-->>Frontend: SourceResponseDto {uuid, rowCount, status...}
Frontend-->>Admin: Notification "N lignes chargées"Schéma PostgreSQL dédié
Chaque entreprise dispose d'un schéma PostgreSQL isolé :
schema_{uuid_enterprise_sans_tirets}Exemple : l'entreprise 550e8400-e29b-41d4-a716-446655440000 → schéma schema_550e8400e29b41d4a716446655440000.
Ce schéma est créé à la première source (CREATE SCHEMA IF NOT EXISTS). Toutes les tables de l'entreprise y sont stockées.
Flux détaillé
1. Vérification d'accès et résolution du document
Fichier : back/src/api/sources/sources.service.ts
Le service récupère le document depuis prisma.tool.documents en incluant l'entreprise du dossier. Pour un admin non super-admin, la requête est scopée à son enterprise_id. Pour un super-admin, tous les documents sont accessibles.
Si le document est introuvable ou que le dossier/entreprise est manquant → SourceDocumentNotFoundException (404).
2. Téléchargement depuis S3
Fichier : back/src/domains/s3Helper/downloadFileBuffer.ts
Le buffer binaire est téléchargé depuis Scaleway S3 via le client AWS SDK. Le chemin S3 est stocké dans documents.path.
3. Parsing du fichier
Fichier : back/src/api/sources/services/file-parser.service.ts
| Format | Bibliothèque | Détection |
|---|---|---|
| CSV | PapaParse | Auto-détection délimiteur (, ou ;) |
| Excel (.xlsx / .xls) | xlsx | Première feuille uniquement |
| JSON | natif | Tableau d'objets racine |
Retourne { headers: string[], rows: Record<string, string | null>[] }.
En cas d'erreur (fichier corrompu, JSON invalide, CSV sans en-tête) → SourceParseException (400) avec le message de l'erreur sous-jacente.
4. Sanitisation des en-têtes
Fichier : back/src/api/sources/services/schema-table.service.ts — sanitizeHeaders()
Chaque en-tête est normalisé pour être un identifiant PostgreSQL valide :
- Suppression des accents (NFD)
- Passage en minuscules
- Remplacement des caractères non alphanumériques par
_ - Suppression du préfixe non-alphabétique
- Troncature à 63 caractères
- Déduplication : si deux colonnes donnent le même identifiant après sanitisation →
col,col_2,col_3...
5. Chargement de la table
Fichier : back/src/api/sources/services/schema-table.service.ts — loadTable()
sql
-- 1. Création du schéma si absent
CREATE SCHEMA IF NOT EXISTS "schema_abc123..."
-- 2. Dans une transaction :
DROP TABLE IF EXISTS "schema_abc123..."."ma_table"
CREATE TABLE "schema_abc123..."."ma_table" (
_id SERIAL PRIMARY KEY,
"colonne_1" TEXT,
"colonne_2" TEXT,
...
)
-- 3. Insertion par batch de 1000 lignes (configurable via SOURCE_INSERT_BATCH_SIZE)
INSERT INTO "schema_abc123..."."ma_table" ("colonne_1", "colonne_2") VALUES (...)Tous les identifiants (schéma, table, colonnes) passent par pg-format pour prévenir les injections SQL. La transaction garantit l'atomicité : en cas d'erreur à l'insertion, la table est droppée avec les données partielles.
6. Persistance Prisma
Une entrée est créée dans prisma.tool.sources avec le statut ACTIVE, le nombre de lignes, le nom du schéma, le nom de la table, et les relations vers le document et l'entreprise.
Vérification de table existante
Endpoint : GET /sources/table-check?tableName=xxx&documentUuid=yyy
Permet au frontend d'avertir l'admin avant soumission. Le backend :
- Résout le schéma de l'entreprise depuis
documentUuid - Interroge
information_schema.tables:
sql
SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'schema_...' AND table_name = 'ma_table'
)Retourne { exists: boolean }.
Le frontend déclenche cet appel avec un debounce de 400 ms quand le champ tableName de l'étape 2 du wizard change. Si exists = true, un banner orange non bloquant est affiché.
Fichiers concernés
| Fichier | Rôle |
|---|---|
back/src/api/sources/sources.controller.ts | Endpoints REST + Swagger |
back/src/api/sources/sources.service.ts | Orchestration métier |
back/src/api/sources/services/file-parser.service.ts | Parsing CSV/Excel/JSON |
back/src/api/sources/services/schema-table.service.ts | Gestion schémas et tables PostgreSQL |
back/src/api/sources/dto/source.dto.ts | DTO entrants et sortants |
back/src/api/sources/source.presenter.ts | Projection Prisma → DTO |
back/src/exceptions/source.exceptions.ts | Exceptions custom (6 types) |
back/prisma/schema.prisma | Modèle sources |
front/src/pages/admin/sources/AdminSourcesPage.vue | Page admin liste des sources |
front/src/components/sources/SourceCreationDialog.vue | Wizard de création (3 étapes) |
front/src/stores/source-store.js | Store Pinia sources |
front/src/router/sourcesGuard.js | Guard route /sources (admin uniquement) |
Variables d'environnement
| Variable | Rôle | Défaut |
|---|---|---|
SOURCE_INSERT_BATCH_SIZE | Nombre de lignes par batch d'insertion | 1000 |
DATABASE_URL_TOOL | Connexion PostgreSQL données métier (Prisma) | — |
UPLOAD_S3_BUCKET | Bucket S3 Scaleway | — |
UPLOAD_S3_ENDPOINT | Endpoint S3 Scaleway | — |
UPLOAD_S3_REGION | Région S3 | — |
UPLOAD_S3_ACCESS_KEY | Clé d'accès S3 | — |
UPLOAD_S3_SECRET_ACCESS_KEY | Secret S3 | — |
Décisions d'architecture liées
- ADR-01 : PostgreSQL indépendant pour les données métier — les tables sources y sont créées dans des schémas dédiés par entreprise.
- ADR-02 : Scaleway Object Storage (S3) pour les fichiers — le module Sources télécharge depuis S3 avant parsing.
- ADR-03 : Upload via NestJS — le buffer transite par le backend, jamais directement côté client.
