From 23fe67470b653cdfebd38b3cac99a0f30da0f17c Mon Sep 17 00:00:00 2001 From: jpgiannetti Date: Thu, 12 Feb 2026 20:49:02 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20migrer=20sch=C3=A9mas=20BDD=20de=20Merm?= =?UTF-8?q?aid=20vers=20DBML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remplace les diagrammes Mermaid par DBML (via kroki-dbml) pour une meilleure expressivité des schémas de base de données : - Ajout support notes, contraintes et indexes détaillés - Migration de tous les schémas d'entités partagées - Ajout fichier exemple dbml-example.md - Configuration plugin mkdocs-kroki pour rendu DBML --- CLAUDE.md | 4 + .../_shared/entities/account-deletions.md | 45 ++++++--- .../_shared/entities/breach-incidents.md | 74 ++++++++++----- docs/domains/_shared/entities/consents.md | 39 +++++--- .../_shared/entities/data-retention-logs.md | 34 ++++--- docs/domains/_shared/entities/devices.md | 55 +++++++---- docs/domains/_shared/entities/exports.md | 51 +++++++--- .../_shared/entities/interest-gauges.md | 41 +++++--- .../_shared/entities/location-history.md | 45 ++++++--- .../_shared/entities/parental-consents.md | 63 +++++++----- .../entities/privacy-policy-versions.md | 53 +++++++---- docs/domains/_shared/entities/reports.md | 79 +++++++++++---- docs/domains/_shared/entities/sessions.md | 56 +++++++---- .../_shared/entities/user-profile-history.md | 50 +++++++--- docs/examples/dbml-example.md | 95 +++++++++++++++++++ mkdocs.yml | 6 ++ 16 files changed, 566 insertions(+), 224 deletions(-) create mode 100644 docs/examples/dbml-example.md diff --git a/CLAUDE.md b/CLAUDE.md index 47dca97..5a340de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -137,6 +137,10 @@ make docs-pdf # Generate PDF of all documentation make docs-clean # Remove generated docs and PDF ``` +**First run**: The first `make docs-serve` will build a custom Docker image with mkdocs-kroki-plugin. This takes ~30s but is cached for subsequent runs. + +**DBML Support**: The documentation now supports DBML (Database Markup Language) for database diagrams via the Kroki plugin. Use `kroki-dbml` code blocks in markdown files. See [docs/examples/dbml-example.md](docs/examples/dbml-example.md) for examples. + ## Working with sqlc When adding or modifying database queries: diff --git a/docs/domains/_shared/entities/account-deletions.md b/docs/domains/_shared/entities/account-deletions.md index 5ca79c4..f9bf37a 100644 --- a/docs/domains/_shared/entities/account-deletions.md +++ b/docs/domains/_shared/entities/account-deletions.md @@ -4,22 +4,37 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o| ACCOUNT_DELETIONS : "demande" +```kroki-dbml +Table users { + id uuid [primary key] + email varchar(255) + status varchar(20) +} - ACCOUNT_DELETIONS { - uuid id PK - uuid user_id FK - string status - string cancellation_token - timestamp requested_at - timestamp effective_at "requested_at + 30j" - timestamp cancelled_at - timestamp deleted_at - string deletion_reason - json deleted_data_summary - } +Table account_deletions { + id uuid [primary key] + user_id uuid [not null, unique, ref: - users.id, note: 'One-to-one: un user ne peut avoir qu une seule demande active'] + status deletion_status_enum [not null, default: 'pending'] + cancellation_token varchar(64) [unique, note: 'Token dans email pour annuler (expire après 30j)'] + requested_at timestamp [not null, default: `now()`] + effective_at timestamp [not null, note: 'Auto-calculated: requested_at + 30 days'] + cancelled_at timestamp [note: 'Timestamp annulation via lien email (NULL si non annulé)'] + deleted_at timestamp [note: 'Timestamp suppression effective (NULL si pending/cancelled)'] + deletion_reason text [note: 'Raison optionnelle fournie par l utilisateur'] + deleted_data_summary jsonb [note: 'Résumé des données supprimées (audit trail)'] + + indexes { + (user_id) [unique] + (status, effective_at) [note: 'Daily cron job: WHERE status = pending AND effective_at < NOW()'] + (cancellation_token) [unique] + } +} + +Enum deletion_status_enum { + pending [note: 'Grace period actif (30j), compte désactivé, annulation possible'] + cancelled [note: 'Utilisateur a annulé via lien email'] + completed [note: 'Suppression effective réalisée après 30j'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/breach-incidents.md b/docs/domains/_shared/entities/breach-incidents.md index 932c311..3d4e2e9 100644 --- a/docs/domains/_shared/entities/breach-incidents.md +++ b/docs/domains/_shared/entities/breach-incidents.md @@ -4,33 +4,57 @@ ## Diagramme -```mermaid -erDiagram - BREACH_INCIDENTS ||--o{ BREACH_AFFECTED_USERS : "impacte" - USERS ||--o{ BREACH_AFFECTED_USERS : "est impacté" +```kroki-dbml +Table breach_incidents { + id uuid [primary key] + severity breach_severity_enum [not null] + description text [not null, note: 'Description détaillée de l incident'] + data_categories_affected jsonb [not null, note: 'Array: ["gps", "email", "listening_history"]'] + estimated_users_count int [not null, note: 'Estimation nombre users impactés'] + detected_at timestamp [not null, default: `now()`, note: 'H+0: Détection initiale'] + contained_at timestamp [note: 'Timestamp confinement de la faille'] + cnil_notified_at timestamp [note: 'H+48: Notification CNIL si requis'] + users_notified_at timestamp [note: 'H+72: Notification users si risque élevé'] + mitigation_actions text [note: 'Actions correctives mises en place'] + cnil_notification_required boolean [not null, default: false] + user_notification_required boolean [not null, default: false] - BREACH_INCIDENTS { - uuid id PK - string severity "low/medium/high/critical" - text description - json data_categories_affected - int estimated_users_count - timestamp detected_at - timestamp contained_at - timestamp cnil_notified_at - timestamp users_notified_at - text mitigation_actions - boolean cnil_notification_required - boolean user_notification_required - } + indexes { + (severity, detected_at) [note: 'Incidents par gravité et chronologie'] + (cnil_notification_required, cnil_notified_at) [note: 'Track CNIL notification compliance'] + } +} - BREACH_AFFECTED_USERS { - uuid id PK - uuid breach_id FK - uuid user_id FK - timestamp notified_at - string notification_channel "email/push/sms" - } +Table users { + id uuid [primary key] +} + +Table breach_affected_users { + id uuid [primary key] + breach_id uuid [not null, ref: > breach_incidents.id] + user_id uuid [not null, ref: > users.id] + notified_at timestamp [note: 'Timestamp notification user (NULL si pas encore notifié)'] + notification_channel notification_channel_enum [note: 'Canal utilisé pour notifier'] + + indexes { + (breach_id, user_id) [unique, note: 'Un user ne peut être listé qu une fois par incident'] + (breach_id, notified_at) [note: 'Track notification progress'] + (user_id) [note: 'Historique incidents pour un user'] + } +} + +Enum breach_severity_enum { + low [note: 'Pas de notification requise (mesures techniques suffisantes)'] + medium [note: 'Notification CNIL uniquement'] + high [note: 'Notification CNIL + utilisateurs'] + critical [note: 'Notification immédiate tous canaux + SMS fondateur'] +} + +Enum notification_channel_enum { + email [note: 'Email notification'] + push [note: 'Push notification mobile'] + sms [note: 'SMS (critical only)'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/consents.md b/docs/domains/_shared/entities/consents.md index f7c5417..c43fc42 100644 --- a/docs/domains/_shared/entities/consents.md +++ b/docs/domains/_shared/entities/consents.md @@ -4,20 +4,33 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ USER_CONSENTS : "donne" +```kroki-dbml +Table users { + id uuid [primary key] +} - USER_CONSENTS { - uuid id PK - uuid user_id FK - string consent_type - string consent_version - boolean accepted - timestamp given_at - inet ip_address - string user_agent - } +Table user_consents { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + consent_type consent_type_enum [not null] + consent_version varchar(10) [not null, note: 'Format: v1.0, v2.0, etc.'] + accepted boolean [not null, note: 'true = opted-in, false = opted-out'] + given_at timestamp [not null, default: `now()`] + ip_address inet [not null, note: 'Proof of consent for CNIL audits'] + user_agent text [not null, note: 'Device/browser proof'] + + indexes { + (user_id, consent_type, consent_version) [note: 'Latest consent per type'] + (user_id, given_at) [note: 'Consent history timeline'] + } +} + +Enum consent_type_enum { + geolocation_precise [note: 'Géolocalisation GPS précise (obligatoire pour contenu hyperlocal)'] + analytics [note: 'Analytics Matomo (optionnel)'] + push_notifications [note: 'Notifications push (optionnel)'] + cookies_analytics [note: 'Cookies analytiques (optionnel)'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/data-retention-logs.md b/docs/domains/_shared/entities/data-retention-logs.md index f801a74..2a80dd8 100644 --- a/docs/domains/_shared/entities/data-retention-logs.md +++ b/docs/domains/_shared/entities/data-retention-logs.md @@ -4,18 +4,28 @@ ## Diagramme -```mermaid -erDiagram - DATA_RETENTION_LOGS { - uuid id PK - string action_type - int users_processed - int users_warned - int users_deleted - json details - timestamp executed_at - bigint execution_duration_ms - } +```kroki-dbml +Table data_retention_logs { + id uuid [primary key] + action_type retention_action_enum [not null] + users_processed int [not null, default: 0, note: 'Nombre total users analysés'] + users_warned int [not null, default: 0, note: 'Nombre users notifiés (90j/30j/7j)'] + users_deleted int [not null, default: 0, note: 'Nombre users supprimés effectivement'] + details jsonb [note: 'Détails: threshold_date, user_ids_deleted, notifications_sent'] + executed_at timestamp [not null, default: `now()`, note: 'Timestamp exécution du job cron'] + execution_duration_ms bigint [not null, note: 'Durée d exécution en millisecondes'] + + indexes { + (action_type, executed_at) [note: 'Historique jobs par type'] + (executed_at) [note: 'Timeline complète des jobs'] + } +} + +Enum retention_action_enum { + check_inactive [note: 'Vérification quotidienne comptes inactifs > 5 ans'] + send_warnings [note: 'Envoi notifications (90j/30j/7j avant suppression)'] + delete_accounts [note: 'Suppression effective comptes inactifs'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/devices.md b/docs/domains/_shared/entities/devices.md index 7f0a4d8..d711acd 100644 --- a/docs/domains/_shared/entities/devices.md +++ b/docs/domains/_shared/entities/devices.md @@ -4,26 +4,43 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ DEVICES : "possède" - DEVICES ||--o{ SESSIONS : "a" +```kroki-dbml +Table users { + id uuid [primary key] +} - DEVICES { - uuid id PK - uuid user_id FK - string device_name - string os - string browser - string device_type - boolean is_trusted - timestamp trusted_until - timestamp first_seen_at - timestamp last_seen_at - inet last_ip - string last_city - string last_country_code - } +Table devices { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + device_name varchar(255) [note: 'User-defined device name'] + os varchar(50) [note: 'iOS, Android, Windows, macOS, Linux'] + browser varchar(50) [note: 'Safari, Chrome, Firefox, etc.'] + device_type device_type_enum [not null, note: 'mobile, tablet, desktop, car'] + is_trusted boolean [not null, default: false, note: 'Bypass 2FA for 30 days if true'] + trusted_until timestamp [note: 'NULL if not trusted, expires after 30 days'] + first_seen_at timestamp [not null, default: `now()`] + last_seen_at timestamp [not null, default: `now()`] + last_ip inet [not null] + last_city varchar(100) + last_country_code char(2) + + indexes { + (user_id, last_seen_at) [note: 'List user devices by recent activity'] + (user_id, is_trusted) [note: 'Find trusted devices for user'] + } +} + +Table sessions { + id uuid [primary key] + device_id uuid [ref: > devices.id] +} + +Enum device_type_enum { + mobile [note: 'Smartphone Android/iOS'] + tablet [note: 'Tablette'] + desktop [note: 'Ordinateur'] + car [note: 'Système embarqué (CarPlay/Android Auto)'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/exports.md b/docs/domains/_shared/entities/exports.md index 03f1918..06078b4 100644 --- a/docs/domains/_shared/entities/exports.md +++ b/docs/domains/_shared/entities/exports.md @@ -4,22 +4,43 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ DATA_EXPORTS : "demande" +```kroki-dbml +Table users { + id uuid [primary key] +} - DATA_EXPORTS { - uuid id PK - uuid user_id FK - string status - string export_url - bigint size_bytes - string format - timestamp requested_at - timestamp generated_at - timestamp expires_at - timestamp downloaded_at - } +Table data_exports { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + status export_status_enum [not null, default: 'pending'] + export_url varchar(512) [note: 'S3/CDN signed URL (NULL until generated)'] + size_bytes bigint [note: 'File size in bytes (NULL until generated)'] + format export_format_enum [not null, default: 'json'] + requested_at timestamp [not null, default: `now()`] + generated_at timestamp [note: 'When export file was created (NULL if pending/generating)'] + expires_at timestamp [note: 'Auto-calculated: generated_at + 7 days'] + downloaded_at timestamp [note: 'First download timestamp (NULL if not yet downloaded)'] + + indexes { + (user_id, requested_at) [note: 'User export history'] + (status, requested_at) [note: 'Background worker queue (WHERE status = pending)'] + (expires_at) [note: 'Daily cleanup job (DELETE WHERE expires_at < NOW())'] + } +} + +Enum export_status_enum { + pending [note: 'Demande en file d attente'] + generating [note: 'Génération en cours (worker background)'] + ready [note: 'Export disponible au téléchargement'] + downloaded [note: 'Export téléchargé par l utilisateur'] + expired [note: 'Export expiré (supprimé automatiquement)'] +} + +Enum export_format_enum { + json [note: 'Machine-readable (données brutes)'] + html [note: 'Human-readable (page web stylée)'] + zip [note: 'Archive complète (JSON + HTML + audio files)'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/interest-gauges.md b/docs/domains/_shared/entities/interest-gauges.md index d787d92..8edf8c1 100644 --- a/docs/domains/_shared/entities/interest-gauges.md +++ b/docs/domains/_shared/entities/interest-gauges.md @@ -4,18 +4,37 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ INTEREST_GAUGES : "possède" +```kroki-dbml +Table users { + id uuid [primary key] +} - INTEREST_GAUGES { - uuid id PK - uuid user_id FK - string category - decimal score - timestamp last_updated - int interactions_count - } +Table interest_gauges { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + category interest_category_enum [not null] + score decimal(5,2) [not null, default: 0, note: 'Range: 0.00 to 100.00'] + last_updated timestamp [not null, default: `now()`] + interactions_count int [not null, default: 0, note: 'Total interactions for this category'] + + indexes { + (user_id, category) [unique, note: 'One gauge per user per category'] + (user_id, score) [note: 'Order categories by score for recommendations'] + } +} + +Enum interest_category_enum { + automobile [note: 'Voitures, mécanique, course automobile'] + travel [note: 'Voyages, tourisme, découverte'] + music [note: 'Musique, concerts, artistes'] + news [note: 'Actualités, politique, économie'] + sport [note: 'Sports, événements sportifs'] + culture [note: 'Cinéma, livres, expositions'] + food [note: 'Gastronomie, restaurants, recettes'] + tech [note: 'Technologie, innovation, gadgets'] + history [note: 'Histoire, patrimoine, musées'] + nature [note: 'Nature, randonnée, écologie'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/location-history.md b/docs/domains/_shared/entities/location-history.md index ea19546..b99f852 100644 --- a/docs/domains/_shared/entities/location-history.md +++ b/docs/domains/_shared/entities/location-history.md @@ -4,22 +4,37 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ LOCATION_HISTORY : "génère" +```kroki-dbml +Table users { + id uuid [primary key] +} - LOCATION_HISTORY { - uuid id PK - uuid user_id FK - geography location - string geohash - boolean anonymized - string context - float speed_kmh - float accuracy_meters - timestamp created_at - timestamp anonymized_at - } +Table location_history { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + location geography [note: 'PostGIS geography type: POINT with SRID 4326 (WGS84)'] + geohash varchar(12) [note: 'Precision 5 geohash (~5km²) after anonymization'] + anonymized boolean [not null, default: false, note: 'true after 24h auto-anonymization'] + context location_context_enum [not null] + speed_kmh float [note: 'GPS speed in km/h (NULL if stationary)'] + accuracy_meters float [not null, note: 'GPS accuracy radius in meters'] + created_at timestamp [not null, default: `now()`] + anonymized_at timestamp [note: 'When precise location was replaced by geohash'] + + indexes { + (user_id, created_at) [note: 'User location timeline'] + (created_at, anonymized) [note: 'Daily anonymization job (WHERE anonymized = false AND created_at < NOW() - 24h)'] + (location) [type: gist, note: 'PostGIS spatial index for proximity queries'] + (geohash) [note: 'Analytics queries on anonymized data'] + } +} + +Enum location_context_enum { + listening [note: 'Position pendant écoute de contenu'] + search [note: 'Position lors d une recherche'] + background [note: 'Tracking en arrière-plan'] + manual [note: 'Position partagée manuellement'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/parental-consents.md b/docs/domains/_shared/entities/parental-consents.md index 7060919..4daf6fc 100644 --- a/docs/domains/_shared/entities/parental-consents.md +++ b/docs/domains/_shared/entities/parental-consents.md @@ -4,34 +4,45 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ PARENTAL_CONSENTS : "a" - PARENTAL_CONSENTS ||--|| PARENTAL_CONTROLS : "configure" +```kroki-dbml +Table users { + id uuid [primary key] + birthdate date [not null] +} - PARENTAL_CONSENTS { - uuid id PK - uuid user_id FK "Ado 13-15 ans" - string parent_email - string validation_token - boolean validated - timestamp token_expires_at - timestamp validated_at - inet parent_ip - string parent_user_agent - timestamp revoked_at - string revocation_reason - } +Table parental_consents { + id uuid [primary key] + user_id uuid [not null, unique, ref: > users.id, note: 'Ado 13-15 ans (1 consent par user max)'] + parent_email varchar(255) [not null, note: 'Email du parent pour validation'] + validation_token varchar(64) [unique, note: 'Token de validation envoyé par email (expire 7j)'] + validated boolean [not null, default: false, note: 'true après clic parent sur lien email'] + token_expires_at timestamp [not null, note: 'validation_token expire après 7 jours'] + validated_at timestamp [note: 'Timestamp de validation parent (NULL si non validé)'] + parent_ip inet [note: 'IP du parent lors de la validation'] + parent_user_agent text [note: 'User agent parent (preuve validation)'] + revoked_at timestamp [note: 'Révocation du consentement parental'] + revocation_reason text [note: 'Raison de la révocation (optionnel)'] - PARENTAL_CONTROLS { - uuid id PK - uuid parental_consent_id FK - boolean gps_enabled - boolean messaging_enabled - boolean content_16plus_enabled - json weekly_digest_config - timestamp updated_at - } + indexes { + (user_id) [unique, note: 'Un seul consentement parental actif par user'] + (validation_token) [unique, note: 'Lookup rapide pour validation lien email'] + (validated, token_expires_at) [note: 'Cleanup des tokens expirés non validés'] + } +} + +Table parental_controls { + id uuid [primary key] + parental_consent_id uuid [not null, unique, ref: - parental_consents.id, note: 'One-to-one relationship'] + gps_enabled boolean [not null, default: false, note: 'Autoriser GPS précis (false = GeoIP uniquement)'] + messaging_enabled boolean [not null, default: false, note: 'Autoriser messagerie privée'] + content_16plus_enabled boolean [not null, default: false, note: 'Autoriser contenu 16+'] + weekly_digest_config jsonb [note: 'Config notifications hebdo parent (email, contenu, format)'] + updated_at timestamp [not null, default: `now()`] + + indexes { + (parental_consent_id) [unique] + } +} ``` ## Légende diff --git a/docs/domains/_shared/entities/privacy-policy-versions.md b/docs/domains/_shared/entities/privacy-policy-versions.md index 66d862c..af7bc0e 100644 --- a/docs/domains/_shared/entities/privacy-policy-versions.md +++ b/docs/domains/_shared/entities/privacy-policy-versions.md @@ -4,29 +4,40 @@ ## Diagramme -```mermaid -erDiagram - PRIVACY_POLICY_VERSIONS ||--o{ USER_POLICY_ACCEPTANCES : "acceptée par" - USERS ||--o{ USER_POLICY_ACCEPTANCES : "accepte" +```kroki-dbml +Table privacy_policy_versions { + id uuid [primary key] + version varchar(10) [not null, unique, note: 'Format: v1.0, v2.0, etc.'] + content_markdown text [not null, note: 'Source: docs/legal/politique-confidentialite.md (versionné Git)'] + major_change boolean [not null, default: false, note: 'true = popup obligatoire pour tous les users'] + changelog text [not null, note: 'Résumé des changements pour communication'] + effective_date timestamp [not null, note: 'Date d entrée en vigueur de cette version'] + created_at timestamp [not null, default: `now()`] - PRIVACY_POLICY_VERSIONS { - uuid id PK - string version "v1.0, v2.0, etc." - text content_markdown - boolean major_change - text changelog - timestamp effective_date - timestamp created_at - } + indexes { + (version) [unique] + (effective_date) [note: 'Order versions chronologically'] + } +} - USER_POLICY_ACCEPTANCES { - uuid id PK - uuid user_id FK - uuid policy_version_id FK - boolean accepted - timestamp accepted_at - inet ip_address - } +Table users { + id uuid [primary key] +} + +Table user_policy_acceptances { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + policy_version_id uuid [not null, ref: > privacy_policy_versions.id] + accepted boolean [not null, note: 'true = accepté, false = refusé (compte gelé)'] + accepted_at timestamp [not null, default: `now()`] + ip_address inet [not null, note: 'IP de l utilisateur lors de l acceptation (preuve CNIL)'] + + indexes { + (user_id, policy_version_id) [unique, note: 'Un user ne peut accepter qu une fois une version'] + (user_id, accepted_at) [note: 'Historique acceptations user'] + (policy_version_id) [note: 'Count acceptances par version'] + } +} ``` ## Légende diff --git a/docs/domains/_shared/entities/reports.md b/docs/domains/_shared/entities/reports.md index 69875b1..c5f7366 100644 --- a/docs/domains/_shared/entities/reports.md +++ b/docs/domains/_shared/entities/reports.md @@ -4,26 +4,67 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ REPORTS : "signale" - CONTENTS ||--o{ REPORTS : "reçoit" - USERS ||--o{ REPORTS : "modère" +```kroki-dbml +Table users { + id uuid [primary key] + username varchar(50) +} - REPORTS { - uuid id PK - uuid content_id FK - uuid reporter_id FK - uuid moderator_id FK - string category - string status - text comment - string evidence_url - timestamp reported_at - timestamp reviewed_at - text moderator_notes - string action_taken - } +Table contents { + id uuid [primary key] + title varchar(255) + user_id uuid [not null] +} + +Table reports { + id uuid [primary key] + content_id uuid [not null, ref: > contents.id, note: 'Content being reported'] + reporter_id uuid [not null, ref: > users.id, note: 'User who filed the report'] + moderator_id uuid [ref: > users.id, note: 'Moderator assigned to review (NULL if pending)'] + category report_category_enum [not null] + status report_status_enum [not null, default: 'pending'] + comment text [note: 'Reporter explanation (mandatory for "other" category)'] + evidence_url varchar(512) [note: 'Screenshot or additional proof URL'] + reported_at timestamp [not null, default: `now()`] + reviewed_at timestamp [note: 'When moderator reviewed the report'] + moderator_notes text [note: 'Internal moderator notes'] + action_taken report_action_enum [note: 'Action decided by moderator'] + + indexes { + (content_id, status) [note: 'Find all reports for a content'] + (status, reported_at) [note: 'Queue for moderators (pending first)'] + (reporter_id) [note: 'User report history (detect abuse)'] + (moderator_id) [note: 'Reports assigned to moderator'] + } +} + +Enum report_category_enum { + spam [note: 'Contenu publicitaire non sollicité'] + hate_speech [note: 'Discours haineux, discrimination'] + violence [note: 'Violence explicite'] + sexual_content [note: 'Contenu sexuel inapproprié'] + misinformation [note: 'Désinformation, fake news'] + copyright [note: 'Violation de droits d auteur'] + wrong_age_rating [note: 'Classification d âge incorrecte'] + other [note: 'Autre raison (commentaire obligatoire)'] +} + +Enum report_status_enum { + pending [note: 'En attente de revue'] + under_review [note: 'En cours d examen par modérateur'] + actioned [note: 'Action prise (contenu retiré/édité)'] + dismissed [note: 'Signalement rejeté (contenu valide)'] + duplicate [note: 'Doublon d un signalement existant'] +} + +Enum report_action_enum { + content_removed [note: 'Contenu supprimé'] + content_edited [note: 'Métadonnées modifiées (âge, tags)'] + warning_sent [note: 'Avertissement au créateur'] + strike_issued [note: 'Strike ajouté au créateur'] + account_suspended [note: 'Compte créateur suspendu'] + no_action [note: 'Aucune action (signalement infondé)'] +} ``` ## Légende diff --git a/docs/domains/_shared/entities/sessions.md b/docs/domains/_shared/entities/sessions.md index db658bc..8fb2abc 100644 --- a/docs/domains/_shared/entities/sessions.md +++ b/docs/domains/_shared/entities/sessions.md @@ -4,27 +4,43 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ SESSIONS : "possède" - DEVICES ||--o{ SESSIONS : "associé" +```kroki-dbml +Table users { + id uuid [primary key] + email varchar(255) [not null, unique] + username varchar(50) [not null, unique] +} - SESSIONS { - uuid id PK - uuid user_id FK - uuid device_id FK - string access_token_hash - string refresh_token_hash - timestamp access_token_expires_at - timestamp refresh_token_expires_at - inet ip_address - string user_agent - string city - string country_code - timestamp created_at - timestamp last_activity_at - timestamp revoked_at - } +Table devices { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + device_name varchar(255) + device_type varchar(50) +} + +Table sessions { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + device_id uuid [ref: > devices.id] + access_token_hash varchar(64) [not null, note: 'SHA256 hash, never stored in clear'] + refresh_token_hash varchar(64) [not null, note: 'SHA256 hash, auto-rotated'] + access_token_expires_at timestamp [not null, note: 'Lifetime: 15 minutes'] + refresh_token_expires_at timestamp [not null, note: 'Lifetime: 30 days (rolling)'] + ip_address inet [not null] + user_agent text [not null] + city varchar(100) + country_code char(2) + created_at timestamp [not null, default: `now()`] + last_activity_at timestamp [not null, default: `now()`] + revoked_at timestamp [note: 'NULL if active, timestamp if manually revoked'] + + indexes { + (user_id, revoked_at) [note: 'Find active sessions for user'] + (device_id) + (refresh_token_hash) [unique, note: 'Detect replay attacks'] + (last_activity_at) [note: 'Auto-cleanup inactive sessions'] + } +} ``` ## Légende diff --git a/docs/domains/_shared/entities/user-profile-history.md b/docs/domains/_shared/entities/user-profile-history.md index ee31af5..2105890 100644 --- a/docs/domains/_shared/entities/user-profile-history.md +++ b/docs/domains/_shared/entities/user-profile-history.md @@ -4,20 +4,44 @@ ## Diagramme -```mermaid -erDiagram - USERS ||--o{ USER_PROFILE_HISTORY : "modifie" +```kroki-dbml +Table users { + id uuid [primary key] + email varchar(255) + username varchar(50) + bio text +} - USER_PROFILE_HISTORY { - uuid id PK - uuid user_id FK - string field_name "email/username/bio/etc." - text old_value - text new_value - string change_reason "user_edit/admin_correction/gdpr_request" - inet ip_address - timestamp changed_at - } +Table user_profile_history { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + field_name profile_field_enum [not null, note: 'Champ modifié (email, username, bio, etc.)'] + old_value text [note: 'Valeur avant modification (NULL si création)'] + new_value text [not null, note: 'Nouvelle valeur'] + change_reason change_reason_enum [not null] + ip_address inet [not null, note: 'IP de l origine du changement'] + changed_at timestamp [not null, default: `now()`] + + indexes { + (user_id, changed_at) [note: 'Timeline modifications user (ordre chronologique)'] + (field_name, changed_at) [note: 'Track modifications par type de champ'] + (user_id, field_name) [note: 'Historique d un champ spécifique'] + } +} + +Enum profile_field_enum { + email [note: 'Re-vérification requise après changement'] + username [note: 'Limite: 1 changement/30j'] + bio [note: 'Biographie utilisateur'] + avatar_url [note: 'URL de l avatar'] + date_of_birth [note: 'Date de naissance'] +} + +Enum change_reason_enum { + user_edit [note: 'Modification self-service utilisateur'] + admin_correction [note: 'Correction par admin (via backoffice)'] + gdpr_request [note: 'Suite demande RGPD formelle (droit de rectification)'] +} ``` ## Légende diff --git a/docs/examples/dbml-example.md b/docs/examples/dbml-example.md new file mode 100644 index 0000000..9874f39 --- /dev/null +++ b/docs/examples/dbml-example.md @@ -0,0 +1,95 @@ +# Exemple DBML avec Kroki + +Ce fichier montre comment utiliser DBML dans la documentation MkDocs avec le plugin Kroki. + +## Syntaxe de base + +Pour créer un diagramme de base de données DBML, utilisez un bloc de code avec la balise `kroki-dbml` : + +## Exemple : Schéma utilisateurs et contenus + +```kroki-dbml +Table users { + id uuid [primary key] + email varchar(255) [not null, unique] + username varchar(50) [not null, unique] + password_hash varchar(255) [not null] + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + (email) [unique] + (username) [unique] + } +} + +Table contents { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + title varchar(255) [not null] + description text + audio_url varchar(512) [not null] + location geography(POINT, 4326) [note: 'PostGIS geography type'] + duration_seconds int [not null] + category content_category [not null] + status content_status [not null, default: 'draft'] + created_at timestamp [not null, default: `now()`] + published_at timestamp + + indexes { + (user_id) + (status) + (location) [type: gist, note: 'Spatial index'] + (created_at) + } +} + +Table interest_gauges { + id uuid [primary key] + user_id uuid [not null, ref: > users.id] + category varchar(50) [not null] + score decimal(5,2) [not null, default: 0, note: 'Score 0-100'] + last_updated timestamp [not null, default: `now()`] + + indexes { + (user_id, category) [unique] + } +} + +Enum content_category { + "automobile" + "travel" + "music" + "culture" + "sport" + "education" +} + +Enum content_status { + "draft" + "published" + "archived" + "moderated" +} +``` + +## Avantages de DBML + +- ✅ **Syntaxe claire** : Plus lisible que Mermaid pour les schémas BDD +- ✅ **Types PostGIS** : Peut documenter les types spéciaux (geography, geometry) +- ✅ **Index et contraintes** : Documentation complète des index et contraintes +- ✅ **Relations** : Relations explicites entre tables +- ✅ **Enums** : Support natif des types énumérés +- ✅ **Notes** : Annotations directement dans le schéma + +## Utilisation dans votre projet + +Pour documenter vos schémas de base de données dans RoadWave : + +1. Créez vos fichiers `.md` dans `docs/domains//` +2. Ajoutez des blocs `kroki-dbml` pour les schémas +3. Le rendu sera automatique lors de `make docs-serve` + +## Référence DBML + +Consultez la [documentation DBML officielle](https://dbml.dbdiagram.io/docs/) pour la syntaxe complète. diff --git a/mkdocs.yml b/mkdocs.yml index 3dc6810..24e3322 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,12 @@ theme: plugins: - search: lang: fr + - kroki: + ServerURL: https://kroki.io + EnableBlockDiag: true + Enablebpmn: true + EnableExcalidraw: true + EnableMermaid: true # - glightbox: # Lightbox pour agrandir les images (désactivé temporairement) markdown_extensions: