Module Users
Module de gestion des utilisateurs pour la MyTelevision API.
Vue d'ensemble
Le module Users gere :
- Operations CRUD sur les utilisateurs
- Systeme de comptes multi-profils (Netflix-grade)
- Gestion des profils (standard et enfants)
- Gestion des appareils
- Sessions actives
- Preferences utilisateur
- Controle parental (PIN, restrictions d'age, horaires)
- Scoring de risque
Architecture
Hierarchie du Systeme
Tenant (white-label brand)
└── Account (email/password)
└── Profile (max 4, dont le profil par defaut)
├── Restrictions (controle parental)
├── Signals (donnees de recommandation)
└── Sessions (connexions actives)
└── Device (suivi par fingerprint)
Structure des Fichiers
src/
├── application/
│ ├── dtos/
│ │ └── users/
│ │ ├── user.dto.ts
│ │ ├── create-user.dto.ts
│ │ ├── update-user.dto.ts
│ │ └── user-response.dto.ts
│ └── services/
│ └── users/
│ ├── users.service.ts
│ └── users.service.spec.ts
└── presentation/
├── controllers/
│ └── api/v2/
│ └── users.controller.ts
└── modules/
└── users.module.ts
Composants Principaux
UserService
@Injectable()
export class UserService {
async findById(id: string): Promise<User>;
async findByEmail(email: string): Promise<User>;
async create(dto: CreateUserDto): Promise<User>;
async update(id: string, dto: UpdateUserDto): Promise<User>;
async delete(id: string): Promise<void>;
async updatePreferences(id: string, prefs: UserPreferencesDto): Promise<User>;
}
Modele de Donnees
User
model User {
id String @id @default(uuid())
email String @unique
password String?
firstName String?
lastName String?
avatarUrl String?
role UserRole @default(USER)
status UserStatus @default(ACTIVE)
emailVerified Boolean @default(false)
firebaseUid String? @unique
// Relations
accounts Account[]
socialAccounts SocialAccount[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum UserRole {
USER
MODERATOR
ADMIN
SUPER_ADMIN
}
enum UserStatus {
ACTIVE
SUSPENDED
LOCKED
PENDING_VERIFICATION
}
DTOs
CreateUserDto
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
}
UpdateUserDto
export class UpdateUserDto {
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
@IsOptional()
@IsUrl()
avatarUrl?: string;
}
UserResponseDto
export class UserResponseDto {
id: string;
email: string;
firstName: string;
lastName: string;
avatarUrl: string;
role: UserRole;
emailVerified: boolean;
createdAt: Date;
}
Endpoints API
Utilisateurs
| Methode | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/v2/users/me | Obtenir l'utilisateur courant | Requis |
| PATCH | /api/v2/users/me | Mettre a jour l'utilisateur courant | Requis |
| GET | /api/v2/users/:id | Obtenir un utilisateur par ID | Admin |
| DELETE | /api/v2/users/me | Supprimer le compte | Requis |
Profils
| Methode | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/v2/profiles | Lister les profils du compte | Requis |
| GET | /api/v2/profiles/picker | Obtenir les profils pour le selecteur UI | Requis |
| POST | /api/v2/profiles | Creer un profil | Requis |
| POST | /api/v2/profiles/kids | Creer un profil enfant | Requis |
| GET | /api/v2/profiles/me | Obtenir le profil actuel | Requis |
| PATCH | /api/v2/profiles/:id | Mettre a jour un profil | Requis |
| DELETE | /api/v2/profiles/:id | Supprimer un profil (pas le defaut) | Requis |
| POST | /api/v2/profiles/:id/switch | Changer de profil | Requis |
| POST | /api/v2/profiles/:id/pin | Definir/modifier le PIN | Requis |
| POST | /api/v2/profiles/:id/verify-pin | Verifier le PIN | Requis |
Appareils
| Methode | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/v2/devices | Lister les appareils | Requis |
| POST | /api/v2/devices | Enregistrer un appareil | Requis |
| GET | /api/v2/devices/current | Obtenir l'appareil courant | Requis |
| PATCH | /api/v2/devices/:id | Modifier le nom de l'appareil | Requis |
| DELETE | /api/v2/devices/:id | Revoquer un appareil | Requis |
Sessions
| Methode | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/v2/sessions | Lister les sessions | Requis |
| GET | /api/v2/sessions/current | Obtenir la session courante | Requis |
| DELETE | /api/v2/sessions/:id | Revoquer une session | Requis |
| DELETE | /api/v2/sessions | Revoquer toutes les sessions | Requis |
| POST | /api/v2/sessions/current/heartbeat | Mettre a jour l'activite | Requis |
Cycle de Vie de l'Utilisateur
Controle Parental (Profils Enfants)
| Restriction | Description |
|---|---|
| Age Rating | Restriction du contenu par classification d'age (7+, 13+, 16+) |
| Categories | Blocage de categories specifiques (horreur, violence, actualites) |
| Horaires | Autorisation de visionnage uniquement pendant des creneaux definis |
| Limite quotidienne | Temps de visionnage maximum par jour |
| Types de medias | Activation/desactivation par type (films, series, TV, actualites) |
| Protection PIN | PIN a 4-6 chiffres pour l'acces au profil |
| Anti brute-force | 5 tentatives echouees = blocage 15 minutes |
Scoring de Risque
| Signal | Impact Score | Description |
|---|---|---|
| Nouvel appareil | +15 | Connexion depuis un appareil non reconnu |
| Voyage impossible | +40 | >500 km en moins d'1 heure |
| Echecs de connexion | +25 | 5+ tentatives en 10 min |
| VPN/Proxy | +20 | Connexion depuis un VPN/proxy connu |
| Sessions simultanees | +30 | Depasse le max de sessions |
| Rate limit depasse | +15 | Requetes constamment proches du max |
| Plage de Score | Niveau | Action |
|---|---|---|
| 0-30 | LOW | ALLOW |
| 31-60 | MEDIUM | STEP_UP |
| 61-85 | HIGH | CHALLENGE |
| 86-100 | CRITICAL | BLOCK |
Suppression de Compte (RGPD)
Le systeme de suppression implemente la conformite RGPD avec une periode de retractation de 30 jours :
- L'utilisateur demande la suppression - statut passe a
PENDING_DELETION - Date de suppression planifiee (date actuelle + 30 jours)
- L'utilisateur peut annuler a tout moment avant la date
- Si l'utilisateur se connecte pendant la retractation, le compte est automatiquement restaure
- Avertissement 3 jours avant la suppression (cron a 1h du matin)
- Apres 30 jours, le cron effectue la suppression reelle (a 2h du matin)
Actions de suppression
- Toutes les sessions revoquees
- Tous les appareils marques comme revoques
- Tous les profils marques comme supprimes
- Email anonymise (
deleted_<accountId>@deleted.local) - Compte soft-delete avec timestamp
deletedAt
Strategie de Cache
Donnees utilisateur mises en cache dans Redis :
| Pattern de Cle | TTL | Description |
|---|---|---|
user:{id} | 5 min | Utilisateur par ID |
user:email:{email} | 5 min | Utilisateur par email |
user:{id}:preferences | 10 min | Preferences utilisateur |
L'invalidation du cache se produit lors de :
- Mise a jour de l'utilisateur
- Changement de role
- Changement de statut
Rate Limiting (Par Profil)
| Tier | Limite | Fenetre |
|---|---|---|
| Short | 3 requetes | 1 seconde |
| Medium | 20 requetes | 10 secondes |
| Long | 100 requetes | 1 minute |
- Cle :
(tenant_id, profile_id, tier) - Distribue : Base sur Redis, compatible cluster
- Non contournable : Impossible a contourner via changement de profil/appareil/session
Tests
describe('UserService', () => {
it('should find user by id', async () => {
const user = await userService.findById('user-id');
expect(user).toBeDefined();
expect(user.email).toBe('[email protected]');
});
it('should update user profile', async () => {
const updated = await userService.update('user-id', {
firstName: 'John',
lastName: 'Doe',
});
expect(updated.firstName).toBe('John');
});
});