Documentation
Référence technique de la plateforme Timline — architecture, structure des données, zones dynamiques, sécurité et limites par plan.
Architecture générale
Stack technique
Timline est une application web full-stack construite avec Next.js 15 (App Router), React 19, TypeScript 5 et Tailwind CSS. L'éditeur visuel repose sur Fabric.js 6. La base de données est PostgreSQL via Prisma ORM (hébergée sur Neon). L'authentification utilise NextAuth v5. Les médias sont stockés sur Cloudflare R2 ou en local selon la configuration.
Environnements
Développement : http://localhost:3000. Production : défini par la variable d'environnement NEXT_PUBLIC_APP_URL. Le build de production exécute automatiquement prisma migrate deploy avant next build.
Routes API
L'API REST interne expose ~68 routes organisées par domaine : projets, joueurs, équipes, matchs, sponsors, catégories, templates, compositions, visuels, médias, IA, abonnements, administration, santé du service. Toutes les routes sont protégées par session NextAuth sauf les webhooks Stripe et le endpoint /api/health.
Modèle de données
Entités principales
User · Account · Session · Project · VisualSet · Visual · Player · SportTeam · Jersey · Sponsor · Category · Match · MatchLineup · MatchStat · ProjectImage · Template · UserComposition · ImportLog · Team · TeamMember.
Projet
Un projet est le conteneur principal. Il est lié à un sport, une palette de couleurs, une image de couverture, et possède sa propre base de données sportive (joueurs, équipes, matchs, sponsors, médias). Il appartient à un User ou à une Team selon le plan.
Visual et VisualSet
Un Visual représente un artboard sauvegardé. Un VisualSet regroupe plusieurs Visuals liés par format (Post 1:1, Story 9:16, Paysage 16:9) sous une même clé setKey. Le contenu du canvas est sérialisé en JSON Fabric.js et stocké en base.
Match et compositions
Un Match lie deux SportTeam avec un score, un statut (UPCOMING / LIVE / FINISHED), une date et un lieu. MatchLineup stocke la composition avec le rôle (STARTER / SUBSTITUTE) et le flag gardien. MatchStat stocke les statistiques par joueur ou en entrée libre.
Jersey
Plusieurs maillots peuvent être associés à une SportTeam. Chaque Jersey a un type (joueur ou gardien), un mode (SVG builder ou image) et des propriétés graphiques (fond, logos, numéro, nom, overlays).
Artboards et formats
Formats standards
LANDSCAPE : 1920 × 1080 px (16:9) · SQUARE : 1080 × 1080 px (1:1) · STORY : 1080 × 1920 px (9:16). Ces trois artboards peuvent coexister dans un même Visual et sont affichés côte à côte sur le canvas Fabric.js.
Formats personnalisés
Un artboard peut avoir des dimensions libres. Les formats personnalisés ne bénéficient pas de la synchronisation automatique avec les formats standards.
Positions canvas
Les artboards sont positionnés horizontalement avec un espacement de 60 px. La position de chaque artboard sur le canvas est calculée depuis src/lib/artboards.ts.
Zones dynamiques
Types de zones
player — affiche un joueur selon le mode visuel configuré. team — affiche le logo ou le nom d'une équipe. sponsor — affiche le logo d'un sponsor. score — affiche le score du match. teamName — affiche le nom d'une équipe. matchText — affiche un champ texte lié au match (date, lieu, compétition, journée…).
Synchronisation inter-artboards
Chaque zone dynamique est identifiée par un identifiant logique (slotId). Lorsqu'une zone est assignée sur un artboard, les zones ayant le même slotId sur les autres artboards reçoivent automatiquement la même assignation.
Modes visuels joueur
photo · cutout (photo détourée) · jersey (maillot joueur) · jersey-gk (maillot gardien) · helmet (casque) · helmet-gk (casque gardien) · gloves (gants gardien). Le mode est stocké dans les customData de l'objet Fabric.js.
Champs sport-spécifiques
Football : round (Journée/Tour), competition. Basketball : period (Quart-temps/Mi-temps), competition. Volleyball : set, competition. Hockey : period, competition. Rugby / Handball : half, competition. Karting : circuit.
Import de données
Formats supportés
.csv (séparateur virgule ou point-virgule, auto-détecté) · .xls · .xlsx. Traitement via la librairie xlsx côté serveur.
Entités importables
Joueurs · Équipes · Matchs · Statistiques de match.
Wizard d'import
5 étapes : (1) Sélection du type d'entité → (2) Upload du fichier → (3) Mapping des colonnes avec presets suggérés → (4) Prévisualisation ligne par ligne → (5) Résultat avec journal d'erreurs. Les mappings sont mémorisés par type via UserImportMapping.
Déduplication
Les doublons sont détectés sur des clés métier (email, nom + numéro pour les joueurs, nom pour les équipes). En cas de doublon, la ligne existante est mise à jour plutôt que dupliquée. Les équipes référencées mais absentes de la base peuvent être créées automatiquement.
Statuts de photo joueur
Après import, les photos joueurs ont un statut de détourage : PENDING (pas encore traité) · DONE (cutout disponible) · FAILED (échec du traitement). Le cutout peut être déclenché manuellement depuis la fiche joueur.
Détourage — implémentation
Technologie
Le détourage s'exécute entièrement côté navigateur via MediaPipe Vision Tasks. Deux modèles sont utilisés : ImageSegmenter (segmentation automatique, modèle selfie_multiclass_256x256) et InteractiveSegmenter (segmentation guidée par points). Le runtime est WASM / CPU, sans GPU requis.
Assets ONNX
Les modèles ONNX sont servis via une route proxy interne (/api/background-removal-assets/[...assetPath]) avec des en-têtes de cache long terme (Cache-Control: public, max-age=31536000, immutable). Ils sont téléchargés une seule fois et mis en cache par le navigateur.
Modes
Automatique : détection dominante des bords + segmentation. Interactif : placement de points include/exclude avec algorithme de clustering. Pinceau : tracés manuels inclusion/exclusion. Baguette magique : sélection par tolérance de couleur (flood fill).
Stockage
La photo originale est conservée. La version détourée est stockée séparément sous players/cutouts/{id}. Les deux URLs sont référencées dans le modèle Player (photo et cutoutPhoto). Le statut est tracé dans le champ cutoutStatus.
Textures procédurales
Implémentation
Les 18 motifs sont générés en JavaScript via l'API Canvas 2D et appliqués comme overlay sur les objets Fabric.js. Le générateur est dans src/lib/texturePatterns.ts.
Motifs disponibles
carbone · papier · déchirure · béton · bois · métal brossé · tissu · bruit · vagues · hexagones · lignes diagonales · points · hachures · marbre · cuir · grille · chevrons · circuits.
Paramètres
Chaque motif expose : primaryColor (couleur principale), secondaryColor (couleur secondaire), opacity (0–1), scale (taille du motif), rotation (orientation en degrés), intensity (force de l'effet). Les paramètres sont persistés dans customData.textureOverlay de l'objet Fabric.js.
Rendu
L'overlay est un objet Fabric.js de type FabricImage positionné automatiquement au-dessus de l'objet cible et lié à ses dimensions. Il n'est pas exporté comme calque indépendant — il fait partie de l'objet parent.
Stockage médias
Drivers disponibles
local : stockage dans le dossier public/uploads/ (développement ou serveur auto-hébergé). r2 : Cloudflare R2 (production recommandée). Le driver est sélectionné via la variable d'environnement MEDIA_STORAGE_DRIVER.
Structure des dossiers
projects/{id}/backgrounds · projects/{id}/assets · projects/{id}/logos · players/photos · players/cutouts · jerseys/thumbnails · visuals/thumbnails · visuals/canvas.
Proxy médias
Toutes les URLs médias passent par /api/media/[...key] qui résout le bon driver (local ou R2) et sert le fichier. Cela évite d'exposer les URLs R2 directement et permet de changer de driver sans modifier le frontend.
Proxy images externes
/api/proxy-image?url=... permet d'importer des images depuis des URLs externes. La route valide la sécurité de la cible (liste noire de plages réseau sensibles) et contourne les restrictions CORS.
Sécurité
Authentification
NextAuth v5 avec sessions JWT. Le middleware Edge vérifie le cookie de session sur toutes les routes protégées (/dashboard, /editor) sans requête base de données, pour un contrôle d'accès ultra-rapide.
En-têtes HTTP
Content-Security-Policy stricte · X-Frame-Options: DENY · X-Content-Type-Options: nosniff · Referrer-Policy: strict-origin-when-cross-origin · Permissions-Policy · HSTS (production uniquement, max-age 2 ans).
Rate limiting
Les routes sensibles (login, register, reset password, 2FA, export) sont protégées par un rate limiter en mémoire basé sur l'IP. Les limites sont configurées par route dans src/lib/rate-limit.ts.
Journal d'audit admin
Toutes les actions admin sensibles (modification de plan, suspension de compte, suppression de données…) sont enregistrées dans AdminAuditLog avec l'acteur, la cible, l'action et le timestamp.
2FA
La double authentification par email génère un code à usage unique valable 10 minutes. Des codes de récupération permanents sont générés à l'activation et peuvent être révoqués depuis les paramètres.
Plans et limites
FREE
1 projet actif · 3 templates · Éditeur visuel de base · Export PNG standard · Pas d'automatisation assistée · Pas de fond transparent.
PRO — 19 €/mois
Projets illimités · Templates premium · Export HD · Fond transparent · Zones dynamiques avancées · Automatisation assistée · Détourage avancé (tous les modes) · Support prioritaire.
TEAM — 49 €/mois
Tout le plan Pro · Jusqu'à 5 membres · Accès et rôles partagés (OWNER, ADMIN, MEMBER) · Médiathèque commune · Facturation centralisée · Invitations par email.
Gestion des abonnements
Les abonnements sont gérés via Stripe. Le portail Stripe permet de consulter les factures, modifier le mode de paiement et résilier. Les webhooks Stripe mettent à jour le statut en temps réel dans la base de données.