talas-group/03_APPS_&_SERVICES/SCHEMA_BASE_DE_DONNEES.md

1016 lines
31 KiB
Markdown
Raw Permalink Normal View History

# Schéma Base de Données Veza
> Référence complète du schéma PostgreSQL — 60+ tables, UUIDs, soft deletes.
> Source : `veza-backend-api/internal/models/` (68 fichiers Go) et `migrations/` (115 fichiers SQL)
> ORM : GORM v1 (mapping uniquement, migrations 100% SQL)
## Stratégie de migration
- **Migrations SQL pures** dans `veza-backend-api/migrations/` (001 à 115+)
- **GORM** utilisé uniquement pour le mapping Go ↔ PostgreSQL, pas pour la création de schéma
- **AutoMigrate()** est vide — tout est géré par SQL
- **Outil** : `go run ./cmd/migrate_tool/main.go up` (ou `down` pour rollback)
- Table `schema_migrations` pour le suivi des migrations appliquées
## Conventions
- **Clés primaires** : UUID v4 (`uuid.UUID` avec `google/uuid`)
- **Soft delete** : Colonne `deleted_at` indexée sur 20+ tables
- **Timestamps** : `created_at`, `updated_at` sur toutes les tables
- **JSONB** : Utilisé pour les données flexibles (social_links, specs, metadata)
- **Enums** : Types PostgreSQL custom (user_role, track_status, etc.)
- **Foreign keys** : Contraintes PostgreSQL natives, toujours actives
---
## A. Utilisateurs & Authentification
### `users`
Table centrale — profil utilisateur.
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `username` | VARCHAR(30) | NOT NULL | |
| `email` | VARCHAR | NOT NULL, UNIQUE | |
| `password_hash` | VARCHAR | NOT NULL | Masqué en JSON |
| `token_version` | INT | DEFAULT 0 | Pour révocation de tokens |
| `first_name` | VARCHAR | | |
| `last_name` | VARCHAR | | |
| `avatar` | TEXT | | URL avatar |
| `banner_url` | TEXT | | URL bannière |
| `bio` | TEXT | | |
| `location` | VARCHAR | | |
| `birthdate` | TIMESTAMP | NULLABLE | |
| `gender` | VARCHAR(20) | | |
| `role` | user_role ENUM | DEFAULT 'user' | |
| `is_active` | BOOL | DEFAULT true | |
| `is_verified` | BOOL | DEFAULT false | |
| `is_banned` | BOOL | DEFAULT false | |
| `is_admin` | BOOL | DEFAULT false | |
| `is_public` | BOOL | DEFAULT true | |
| `last_login_at` | TIMESTAMP | NULLABLE | |
| `login_count` | INT | DEFAULT 0 | |
| `password_changed_at` | TIMESTAMP | NULLABLE | |
| `social_links` | JSONB | DEFAULT '{}' | |
| `created_at` | TIMESTAMP | | |
| `updated_at` | TIMESTAMP | | |
| `deleted_at` | TIMESTAMP | INDEX | Soft delete |
**Relations** : `roles` (M2M via `user_roles`), `track_likes` (has many, CASCADE)
### `sessions`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users, INDEX | |
| `token_hash` | VARCHAR | UNIQUE INDEX | Token hashé |
| `ip_address` | VARCHAR | | |
| `user_agent` | VARCHAR | | |
| `revoked_at` | TIMESTAMP | NULLABLE | |
| `expires_at` | TIMESTAMP | NOT NULL | |
| `created_at` | TIMESTAMP | | |
### `refresh_tokens`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE), NOT NULL, INDEX | |
| `token_hash` | VARCHAR | NOT NULL, INDEX | |
| `expires_at` | TIMESTAMP | NOT NULL | |
| `created_at` | TIMESTAMP | | |
| `updated_at` | TIMESTAMP | | |
| `deleted_at` | TIMESTAMP | | Soft delete |
### `roles`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `name` | VARCHAR(50) | UNIQUE, NOT NULL | |
| `display_name` | VARCHAR(100) | NOT NULL | |
| `description` | TEXT | | |
| `is_system` | BOOL | | Rôle système (non supprimable) |
| `is_active` | BOOL | DEFAULT true | |
| `created_at` | TIMESTAMP | | |
| `updated_at` | TIMESTAMP | | |
**Relations** : `users` (M2M), `permissions` (M2M)
### `permissions`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `name` | VARCHAR(100) | UNIQUE, NOT NULL | |
| `resource` | VARCHAR(50) | NOT NULL | |
| `action` | VARCHAR(50) | NOT NULL | |
| `description` | TEXT | | |
| `created_at` | TIMESTAMP | | |
### `user_roles` (table pivot)
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE), UNIQUE(user_id, role_id) | |
| `role_id` | UUID | FK → roles (CASCADE) | |
| `role` | VARCHAR(50) | | Nom du rôle (dénormalisé) |
| `assigned_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | |
| `assigned_by` | UUID | NULLABLE, INDEX | |
| `expires_at` | TIMESTAMP | NULLABLE | |
| `is_active` | BOOL | DEFAULT true | |
### `role_permissions` (table pivot)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `role_id` | UUID | FK → roles (CASCADE), UNIQUE(role_id, permission_id) |
| `permission_id` | UUID | FK → permissions (CASCADE) |
### `mfa_configs`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | NOT NULL, UNIQUE INDEX | |
| `secret` | VARCHAR | NOT NULL | Masqué en JSON |
| `backup_codes` | TEXT | | JSON array |
| `is_enabled` | BOOL | DEFAULT false | |
| `last_used_at` | TIMESTAMP | NULLABLE | |
| `created_at` | TIMESTAMP | | |
| `updated_at` | TIMESTAMP | | |
| `deleted_at` | TIMESTAMP | | Soft delete |
### `recovery_codes`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | INDEX |
| `code` | VARCHAR | Masqué |
| `is_used` | BOOL | DEFAULT false |
| `used_at` | TIMESTAMP | NULLABLE |
| `expires_at` | TIMESTAMP | |
| `created_at` | TIMESTAMP | |
### `webauthn_credentials`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | NOT NULL | |
| `credential_id` | BYTEA | UNIQUE, NOT NULL | |
| `public_key` | BYTEA | NOT NULL | |
| `attestation_type` | VARCHAR(50) | DEFAULT 'none' | |
| `aaguid` | BYTEA | | |
| `sign_count` | INT | DEFAULT 0 | |
| `name` | VARCHAR(100) | DEFAULT 'My Passkey' | |
| `created_at` | TIMESTAMP | | |
| `last_used_at` | TIMESTAMP | NULLABLE | |
### `federated_identities`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE), INDEX | |
| `provider` | VARCHAR | | google, github, facebook, twitter |
| `provider_id` | VARCHAR | | |
| `email` | VARCHAR | | |
| `display_name` | VARCHAR | | |
| `avatar_url` | VARCHAR | | |
| `access_token` | TEXT | | Masqué |
| `refresh_token` | TEXT | | Masqué |
| `expires_at` | TIMESTAMP | NULLABLE | |
### `api_keys`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | INDEX |
| `name` | VARCHAR(100) | |
| `prefix` | VARCHAR(16) | INDEX |
| `hashed_key` | VARCHAR(128) | Masqué |
| `scopes` | TEXT[] | PostgreSQL array |
| `last_used_at` | TIMESTAMP | NULLABLE |
| `expires_at` | TIMESTAMP | NULLABLE |
| `created_at` | TIMESTAMP | |
### `user_settings`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE |
| `email_notifications` | BOOL | |
| `push_notifications` | BOOL | |
| `browser_notifications` | BOOL | |
| `email_on_follow` | BOOL | |
| `email_on_like` | BOOL | |
| `email_on_comment` | BOOL | |
| `email_on_message` | BOOL | |
| `email_on_mention` | BOOL | |
| `email_marketing` | BOOL | |
| `allow_search_indexing` | BOOL | |
| `show_activity` | BOOL | |
| `explicit_content` | BOOL | |
| `autoplay` | BOOL | |
### `user_profiles`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE |
| `language` | VARCHAR | DEFAULT 'en' |
| `timezone` | VARCHAR | DEFAULT 'UTC' |
| `theme` | VARCHAR | DEFAULT 'auto' |
### `user_presence`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `user_id` | UUID | PK | |
| `status` | VARCHAR(20) | DEFAULT 'offline' | online/away/busy/offline |
| `last_seen_at` | TIMESTAMP | | |
| `status_message` | TEXT | | |
| `track_id` | UUID | NULLABLE | Piste en écoute |
| `track_title` | TEXT | | |
| `invisible` | BOOL | DEFAULT false | |
| `updated_at` | TIMESTAMP | | |
---
## B. Pistes audio
### `tracks`
Table principale des pistes audio.
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `creator_id` | UUID | FK → users (CASCADE), INDEX | |
| `file_id` | UUID | NULLABLE | |
| `title` | VARCHAR(255) | NOT NULL | |
| `artist` | VARCHAR(255) | | |
| `album` | VARCHAR(255) | | |
| `duration` | INT | | Secondes |
| `genre` | VARCHAR(100) | | |
| `tags` | TEXT[] | | PostgreSQL array |
| `year` | INT | DEFAULT 0 | |
| `bpm` | INT | NULLABLE | |
| `musical_key` | VARCHAR(10) | | |
| `file_path` | VARCHAR(500) | | |
| `file_size` | BIGINT | | Octets |
| `format` | VARCHAR(10) | | wav, mp3, flac... |
| `bitrate` | INT | | kbps |
| `sample_rate` | INT | | Hz |
| `waveform_path` | VARCHAR | | |
| `waveform_url` | VARCHAR | | |
| `cover_art_path` | VARCHAR | | |
| `is_public` | BOOL | DEFAULT true | |
| `status` | track_status ENUM | | uploading/processing/completed/failed |
| `status_message` | TEXT | | |
| `stream_status` | VARCHAR | DEFAULT 'pending' | pending/processing/ready/error |
| `stream_manifest_url` | VARCHAR | | URL playlist HLS |
| `play_count` | BIGINT | | Non exposé en API |
| `like_count` | BIGINT | | Non exposé en API |
| `created_at` | TIMESTAMP | | |
| `updated_at` | TIMESTAMP | | |
| `deleted_at` | TIMESTAMP | INDEX | Soft delete |
**Relations** : `user` (belongs to), `playlists` (M2M), `likes` (has many), `shares` (has many), `versions` (has many), `hls_streams` (has many)
### `track_versions`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `track_id` | UUID | FK → tracks (CASCADE), UNIQUE(track_id, version_number) |
| `version_number` | INT | |
| `file_path` | VARCHAR(500) | |
| `file_size` | BIGINT | Octets |
| `changelog` | TEXT | |
### `track_likes`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | FK → users (CASCADE), UNIQUE(user_id, track_id) |
| `track_id` | UUID | FK → tracks (CASCADE) |
| `created_at` | TIMESTAMP | |
### `track_plays`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | FK → tracks, INDEX | |
| `user_id` | UUID | FK → users (SET NULL), NULLABLE, INDEX | Anonyme si non connecté |
| `duration` | INT | | Secondes jouées |
| `played_at` | TIMESTAMP | INDEX | |
| `device` | VARCHAR(100) | | |
| `ip_address` | VARCHAR(45) | | |
### `track_shares`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `track_id` | UUID | INDEX |
| `user_id` | UUID | INDEX |
| `share_token` | VARCHAR(255) | UNIQUE |
| `permissions` | VARCHAR(50) | DEFAULT 'read' (read/download) |
| `expires_at` | TIMESTAMP | NULLABLE |
| `access_count` | BIGINT | DEFAULT 0 |
### `track_comments`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX | |
| `user_id` | UUID | FK → users (CASCADE), INDEX | |
| `parent_id` | UUID | FK → track_comments (CASCADE), NULLABLE | Réponses |
| `content` | TEXT | NOT NULL | |
| `timestamp` | FLOAT | | Position en secondes |
| `is_edited` | BOOL | DEFAULT false | |
**Relations** : `parent` (self-ref), `replies` (has many self-ref)
### `track_reposts`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | FK → users (CASCADE), INDEX |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX |
| `created_at` | TIMESTAMP | INDEX |
### `track_stems`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX |
| `name` | VARCHAR(100) | |
| `file_path` | VARCHAR(500) | |
| `format` | VARCHAR(10) | |
| `size_bytes` | BIGINT | |
| `created_at` | TIMESTAMP | |
| `deleted_at` | TIMESTAMP | Soft delete |
### `track_lyrics`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `track_id` | UUID | FK → tracks (CASCADE), UNIQUE |
| `content` | TEXT | NOT NULL |
### `track_history`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | INDEX | |
| `user_id` | UUID | FK → users (SET NULL) | |
| `action` | ENUM | INDEX | created/updated/deleted/published/unpublished/restored |
| `old_value` | TEXT | | |
| `new_value` | TEXT | | |
### `tags`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `name` | VARCHAR(30) | UNIQUE |
| `use_count` | INT | DEFAULT 0 |
### `track_tags` (pivot)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `track_id` | UUID | PK composite |
| `tag_id` | UUID | PK composite |
| `created_at` | TIMESTAMP | |
### `genres`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `slug` | VARCHAR(50) | PK |
| `name` | VARCHAR(100) | |
### `track_genres` (pivot)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `track_id` | UUID | PK composite |
| `genre_slug` | VARCHAR(50) | PK composite |
| `position` | INT | DEFAULT 0 |
### `playback_analytics`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX | |
| `user_id` | UUID | FK → users (CASCADE), INDEX | |
| `play_time` | INT | | Secondes |
| `pause_count` | INT | | |
| `seek_count` | INT | | |
| `completion_rate` | DECIMAL(5,2) | | 0-100% |
| `started_at` | TIMESTAMP | | |
| `ended_at` | TIMESTAMP | NULLABLE | |
---
## C. Playlists
### `playlists`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE), INDEX | |
| `name` | VARCHAR(200) | | Titre (colonne DB: `name`) |
| `description` | TEXT | | |
| `is_public` | BOOL | DEFAULT true | |
| `cover_url` | VARCHAR(500) | | |
| `track_count` | INT | DEFAULT 0 | |
| `follower_count` | INT | DEFAULT 0 | |
| `is_editorial` | BOOL | DEFAULT false | Playlist éditoriale |
| `is_default_favorites` | BOOL | DEFAULT false | Playlist favoris auto |
| `deleted_at` | TIMESTAMP | | Soft delete |
**Relations** : `user` (belongs to), `tracks` (has many PlaylistTrack), `collaborators` (has many, CASCADE)
### `playlist_tracks` (pivot ordonné)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `playlist_id` | UUID | FK → playlists (CASCADE), INDEX |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX |
| `position` | INT | Ordre dans la playlist |
| `added_by` | UUID | |
| `added_at` | TIMESTAMP | |
### `playlist_collaborators`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `playlist_id` | UUID | FK → playlists, INDEX | |
| `user_id` | UUID | FK → users, INDEX | |
| `permission` | VARCHAR(20) | DEFAULT 'read' | read/write/admin |
### `playlist_follows`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `playlist_id` | UUID | INDEX |
| `user_id` | UUID | INDEX |
### `playlist_share_links`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `playlist_id` | UUID | INDEX |
| `user_id` | UUID | INDEX |
| `share_token` | VARCHAR(255) | UNIQUE |
| `expires_at` | TIMESTAMP | NULLABLE |
| `access_count` | BIGINT | DEFAULT 0 |
### `playlist_versions`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `playlist_id` | UUID | INDEX | |
| `user_id` | UUID | FK → users (SET NULL) | |
| `version` | INT | | |
| `action` | ENUM | | Type de modification |
| `title` | VARCHAR(200) | | |
| `description` | TEXT | | |
| `is_public` | BOOL | | |
| `cover_url` | VARCHAR(500) | | |
| `tracks_snapshot` | TEXT | | JSON des pistes à cette version |
### `user_genre_follows` (pivot)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `user_id` | UUID | PK composite |
| `genre_slug` | VARCHAR | PK composite |
### `user_tag_follows` (pivot)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `user_id` | UUID | PK composite |
| `tag_id` | UUID | PK composite |
---
## D. Chat & Messagerie
### `rooms`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `name` | VARCHAR(255) | | |
| `description` | TEXT | | |
| `room_type` | VARCHAR | DEFAULT 'public' | public/private/dm |
| `is_private` | BOOL | DEFAULT false | |
| `creator_id` | UUID | FK → users (CASCADE) | |
| `deleted_at` | TIMESTAMP | | Soft delete |
**Relations** : `members` (has many, CASCADE), `messages` (has many, CASCADE)
### `room_members`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `room_id` | UUID | FK → rooms (CASCADE) |
| `user_id` | UUID | FK → users (CASCADE) |
| `role` | VARCHAR | DEFAULT 'member' |
| `joined_at` | TIMESTAMP | |
### `room_invitations`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `room_id` | UUID | FK → rooms |
| `inviter_id` | UUID | FK → users |
| `invitee_id` | UUID | NULLABLE |
| `token` | UUID | UNIQUE |
| `status` | VARCHAR(20) | DEFAULT 'pending' (pending/accepted/expired) |
| `expires_at` | TIMESTAMP | |
### `messages`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `room_id` | UUID | FK → rooms | |
| `sender_id` | UUID | FK → users | |
| `content` | TEXT | | |
| `message_type` | VARCHAR | DEFAULT 'text' | text/image/audio/system |
| `reply_to_id` | UUID | NULLABLE, self-ref | Réponse à |
| `is_edited` | BOOL | DEFAULT false | |
| `is_deleted` | BOOL | DEFAULT false | |
| `is_pinned` | BOOL | DEFAULT false | |
| `edited_at` | TIMESTAMP | NULLABLE | |
| `status` | VARCHAR(20) | DEFAULT 'sent' | |
| `metadata` | JSONB | NULLABLE | |
| `content_tsv` | TSVECTOR | | Recherche full-text |
### `message_reactions`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE(user_id, message_id, emoji) |
| `message_id` | UUID | |
| `emoji` | VARCHAR(50) | |
### `read_receipts`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE(user_id, message_id) |
| `message_id` | UUID | |
| `read_at` | TIMESTAMP | |
### `delivered_status`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE(user_id, message_id) |
| `message_id` | UUID | |
| `delivered_at` | TIMESTAMP | |
---
## E. Streaming & Collaboration
### `hls_streams`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | FK → tracks (CASCADE), INDEX | |
| `playlist_url` | VARCHAR(500) | | |
| `segments_count` | INT | DEFAULT 0 | |
| `bitrates` | JSONB | | Liste des bitrates disponibles |
| `status` | VARCHAR(20) | DEFAULT 'pending', INDEX | pending/processing/ready/failed |
### `hls_transcode_queue`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `track_id` | UUID | INDEX | |
| `priority` | INT | DEFAULT 5 | |
| `status` | VARCHAR(20) | DEFAULT 'pending', INDEX | pending/processing/completed/failed |
| `retry_count` | INT | | |
| `max_retries` | INT | | |
| `error_message` | TEXT | NULLABLE | |
| `started_at` | TIMESTAMP | NULLABLE | |
| `completed_at` | TIMESTAMP | NULLABLE | |
### `live_streams`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE) | |
| `title` | VARCHAR(200) | | |
| `description` | TEXT | | |
| `category` | VARCHAR(100) | | |
| `thumbnail_url` | VARCHAR(500) | | |
| `stream_key` | VARCHAR(100) | | Masqué en JSON |
| `streamer_name` | VARCHAR(100) | | |
| `is_live` | BOOL | DEFAULT false | |
| `started_at` | TIMESTAMP | NULLABLE | |
| `ended_at` | TIMESTAMP | NULLABLE | |
| `viewer_count` | INT | DEFAULT 0 | |
| `tags` | JSONB | DEFAULT '[]' | |
| `scheduled_at` | TIMESTAMP | NULLABLE | |
| `stream_url` | TEXT | DEFAULT '' | |
| `is_vod` | BOOL | DEFAULT false | |
| `deleted_at` | TIMESTAMP | | Soft delete |
### `co_listening_sessions`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `host_id` | UUID | FK → users, INDEX |
| `track_id` | UUID | FK → tracks, INDEX |
| `expires_at` | TIMESTAMP | |
### `queues`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | UNIQUE | |
| `current_track_id` | UUID | NULLABLE | |
| `current_position` | INT | DEFAULT 0 | |
| `is_playing` | BOOL | DEFAULT false | |
| `shuffle` | BOOL | DEFAULT false | |
| `repeat_mode` | VARCHAR(20) | DEFAULT 'off' | off/one/all |
| `volume` | INT | DEFAULT 100 | |
### `queue_items`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `queue_id` | UUID | FK → queues |
| `track_id` | UUID | FK → tracks |
| `position` | INT | |
### `queue_sessions` (sessions collaboratives)
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `share_token` | VARCHAR(32) | UNIQUE |
| `creator_id` | UUID | INDEX |
### `shared_queue_items`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `session_id` | UUID | INDEX |
| `track_id` | UUID | |
| `position` | INT | |
---
## F. Fichiers & Stockage
### `user_files`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users | |
| `folder_id` | UUID | FK → user_folders (SET NULL), NULLABLE | |
| `filename` | VARCHAR(255) | | |
| `s3_key` | VARCHAR(500) | | Clé MinIO/S3 |
| `size_bytes` | BIGINT | DEFAULT 0 | |
| `mime_type` | VARCHAR(100) | DEFAULT 'application/octet-stream' | |
### `user_folders`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE) | |
| `name` | VARCHAR(255) | | |
| `parent_id` | UUID | FK → user_folders (CASCADE), NULLABLE | Hiérarchie |
**Relations** : `parent` (self-ref), `children` (has many), `files` (has many)
### `cloud_file_versions`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `file_id` | UUID | FK → user_files (CASCADE) |
| `version` | INT | |
| `storage_key` | TEXT | |
| `size_bytes` | BIGINT | DEFAULT 0 |
### `cloud_file_shares`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `file_id` | UUID | FK → user_files (CASCADE) |
| `token` | VARCHAR(64) | UNIQUE |
| `permissions` | VARCHAR(20) | DEFAULT 'read' |
| `expires_at` | TIMESTAMP | |
### `user_storage_quotas`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `user_id` | UUID | PK | |
| `max_bytes` | BIGINT | DEFAULT 5368709120 | ~5 Go |
| `used_bytes` | BIGINT | DEFAULT 0 | |
---
## G. Équipement (Gear)
### `gear_items`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users | |
| `name` | VARCHAR(200) | | |
| `category` | VARCHAR(100) | | micro, interface, casque... |
| `brand` | VARCHAR | | |
| `model` | VARCHAR | | |
| `serial_number` | VARCHAR | | |
| `image` | VARCHAR(500) | | |
| `images` | JSONB | DEFAULT '[]' | |
| `status` | VARCHAR(50) | | actif, en réparation, vendu |
| `condition` | VARCHAR(50) | | neuf, bon, usé |
| `purchase_date` | TIMESTAMP | NULLABLE | |
| `purchase_price` | DECIMAL(12,2) | | |
| `currency` | VARCHAR(3) | DEFAULT 'USD' | |
| `vendor` | VARCHAR | | |
| `order_number` | VARCHAR | | |
| `warranty_start` | TIMESTAMP | NULLABLE | |
| `warranty_expire` | TIMESTAMP | NULLABLE | |
| `warranty_type` | VARCHAR | | |
| `warranty_notes` | TEXT | | |
| `support_contact` | VARCHAR | | |
| `specs` | JSONB | | Spécifications techniques |
| `notes` | TEXT | | |
| `documents` | JSONB | | |
| `maintenance_history` | JSONB | | |
| `is_public` | BOOL | DEFAULT false | Visible sur le profil |
| `deleted_at` | TIMESTAMP | | Soft delete |
### `gear_images`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `gear_id` | UUID | FK → gear_items (CASCADE) |
| `image_url` | VARCHAR(500) | |
| `position` | INT | DEFAULT 0 |
### `gear_repairs`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `gear_id` | UUID | FK → gear_items (CASCADE) |
| `repair_date` | DATE | |
| `description` | TEXT | |
| `cost_cents` | INT | DEFAULT 0 |
| `currency` | VARCHAR(3) | DEFAULT 'EUR' |
| `provider` | VARCHAR(255) | |
| `notes` | TEXT | |
### `gear_documents`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `gear_id` | UUID | FK → gear_items (CASCADE) |
| `type` | VARCHAR(50) | DEFAULT 'invoice' |
| `storage_key` | TEXT | |
| `filename` | VARCHAR(255) | |
---
## H. Paiements & Royalties
### `seller_stripe_accounts`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `user_id` | UUID | UNIQUE |
| `stripe_account_id` | VARCHAR(255) | UNIQUE |
| `charges_enabled` | BOOL | |
| `payouts_enabled` | BOOL | |
| `onboarding_completed` | BOOL | |
| `kyc_status` | VARCHAR(32) | DEFAULT 'not_started' |
| `kyc_verification_session_id` | VARCHAR(255) | |
| `kyc_verified_at` | TIMESTAMP | NULLABLE |
| `kyc_last_error` | TEXT | |
### `royalty_records`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `content_id` | UUID | INDEX | |
| `creator_id` | UUID | INDEX | |
| `period` | VARCHAR | INDEX | Période (ex: 2026-03) |
| `plays` | BIGINT | | |
| `revenue` | FLOAT | | |
| `royalty_amount` | FLOAT | | |
| `royalty_rate` | FLOAT | | |
| `status` | VARCHAR | DEFAULT 'calculated' | calculated/paid |
| `calculated_at` | TIMESTAMP | | |
| `paid_at` | TIMESTAMP | NULLABLE | |
### `royalty_payouts`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `payout_id` | VARCHAR | UNIQUE |
| `creator_id` | UUID | INDEX |
| `amount` | FLOAT | |
| `currency` | VARCHAR | DEFAULT 'EUR' |
| `period` | VARCHAR | INDEX |
| `status` | VARCHAR | DEFAULT 'pending' |
| `payment_method` | VARCHAR | |
| `transaction_id` | VARCHAR | |
| `processed_at` | TIMESTAMP | |
| `estimated_arrival` | TIMESTAMP | |
| `notes` | VARCHAR | |
### `royalty_rates`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `content_type` | VARCHAR | UNIQUE |
| `rate` | FLOAT | |
| `description` | VARCHAR | |
| `is_active` | BOOL | DEFAULT true |
### `creator_royalty_rates`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `creator_id` | UUID | UNIQUE | |
| `rate` | FLOAT | | Taux personnalisé |
| `reason` | VARCHAR | | |
| `is_active` | BOOL | DEFAULT true | |
### `royalty_config`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `platform_fee_rate` | FLOAT | DEFAULT 0.15 | 15% frais plateforme |
| `minimum_payout_amount` | FLOAT | DEFAULT 50.0 | Seuil minimum versement |
| `payout_schedule` | VARCHAR | DEFAULT 'monthly' | |
| `processing_delay` | INT | DEFAULT 3 | Jours |
| `currency` | VARCHAR | DEFAULT 'EUR' | |
| `is_active` | BOOL | DEFAULT true | |
---
## I. Modération & Administration
### `reports`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `reporter_id` | UUID | |
| `reported_user_id` | UUID | NULLABLE |
| `content_type` | VARCHAR(50) | |
| `content_id` | UUID | NULLABLE |
| `reason` | TEXT | |
| `status` | VARCHAR(20) | DEFAULT 'pending' |
| `resolved_by` | UUID | NULLABLE |
| `resolved_at` | TIMESTAMP | NULLABLE |
### `announcements`
| Colonne | Type | Contraintes |
|---------|------|-------------|
| `id` | UUID | PK |
| `title` | VARCHAR(200) | |
| `content` | TEXT | |
| `type` | VARCHAR(20) | DEFAULT 'info' |
| `is_active` | BOOL | DEFAULT true |
| `starts_at` | TIMESTAMP | |
| `ends_at` | TIMESTAMP | NULLABLE |
| `created_by` | UUID | NULLABLE |
### `notifications`
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | FK → users (CASCADE), INDEX | |
| `type` | VARCHAR(50) | INDEX | follow, like, comment, system... |
| `title` | VARCHAR(255) | | |
| `content` | TEXT | | |
| `link` | VARCHAR(500) | | |
| `read` | BOOL | DEFAULT false | INDEX composite avec user_id |
| `read_at` | TIMESTAMP | NULLABLE | |
| `created_at` | TIMESTAMP | INDEX | |
---
## J. Exports de données
### `data_exports` (RGPD)
| Colonne | Type | Contraintes | Description |
|---------|------|-------------|-------------|
| `id` | UUID | PK | |
| `user_id` | UUID | | |
| `status` | VARCHAR(20) | DEFAULT 'pending' | pending/processing/completed/failed |
| `s3_key` | TEXT | NULLABLE | |
| `file_size_bytes` | BIGINT | NULLABLE | |
| `expires_at` | TIMESTAMP | | |
| `completed_at` | TIMESTAMP | NULLABLE | |
| `error_message` | TEXT | NULLABLE | |
---
## Utilisation de Redis
Redis est **optionnel** — dégradation gracieuse si indisponible.
| Cas d'usage | Pattern de clé | TTL | Fallback |
|-------------|---------------|-----|----------|
| **Sessions** | `sessions:{token_hash}` | Configurable | Table `sessions` |
| **Cache HTTP** | `http_cache:{sha256(url)}` | 5-15 min | Pas de cache |
| **Rate limiting** | `ratelimit:{key}` | Variable | In-memory avec mutex |
| **Verrouillage compte** | `lockout:{email}:count`, `lockout:{email}:locked_until` | Configurable | In-memory map |
| **Token blacklist** | `token_blacklist:{jti}` | Durée JWT | Pas de révocation |
| **Présence utilisateur** | `presence:{user_id}` | Variable | Table `user_presence` |
| **Cache applicatif** | Via `CacheService` | Variable | Pas de cache |
Headers de cache : `X-Cache: HIT` ou `X-Cache: MISS`
---
## Statistiques
| Métrique | Valeur |
|----------|--------|
| Fichiers modèles Go | 68 |
| Tables PostgreSQL | 60+ |
| Migrations SQL | 115 |
| Tables avec soft delete | 20+ |
| Tables avec UUIDs | 95%+ |
| Cas d'usage Redis | 6 principaux |
---
## Documents liés
- [[ARCHITECTURE_VEZA]] — Architecture globale
- [[ROUTES_API]] — Endpoints API complets
- [[SERVEUR_STREAMING_RUST]] — Serveur streaming
- [[CONFIGURATION_ENVIRONNEMENT]] — Variables d'environnement