diff --git a/backend/src/database/schemas/users.ts b/backend/src/database/schemas/users.ts index 86ce9dc..ce5d20f 100644 --- a/backend/src/database/schemas/users.ts +++ b/backend/src/database/schemas/users.ts @@ -36,6 +36,8 @@ export const users = pgTable( // Sécurité twoFactorSecret: pgpEncrypted("two_factor_secret"), isTwoFactorEnabled: boolean("is_two_factor_enabled").notNull().default(false), + showOnlineStatus: boolean("show_online_status").notNull().default(true), + showReadReceipts: boolean("show_read_receipts").notNull().default(true), // RGPD & Conformité termsVersion: varchar("terms_version", { length: 16 }), // Version des CGU acceptées diff --git a/backend/src/users/repositories/users.repository.ts b/backend/src/users/repositories/users.repository.ts index 0c3d3a0..30662df 100644 --- a/backend/src/users/repositories/users.repository.ts +++ b/backend/src/users/repositories/users.repository.ts @@ -47,6 +47,8 @@ export class UsersRepository { bio: users.bio, status: users.status, isTwoFactorEnabled: users.isTwoFactorEnabled, + showOnlineStatus: users.showOnlineStatus, + showReadReceipts: users.showReadReceipts, createdAt: users.createdAt, updatedAt: users.updatedAt, }) diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts index ad010c9..7cdc0ee 100644 --- a/backend/src/users/users.module.ts +++ b/backend/src/users/users.module.ts @@ -1,13 +1,19 @@ import { forwardRef, Module } from "@nestjs/common"; import { AuthModule } from "../auth/auth.module"; import { MediaModule } from "../media/media.module"; +import { RealtimeModule } from "../realtime/realtime.module"; import { S3Module } from "../s3/s3.module"; import { UsersRepository } from "./repositories/users.repository"; import { UsersController } from "./users.controller"; import { UsersService } from "./users.service"; @Module({ - imports: [forwardRef(() => AuthModule), MediaModule, S3Module], + imports: [ + forwardRef(() => AuthModule), + MediaModule, + S3Module, + forwardRef(() => RealtimeModule), + ], controllers: [UsersController], providers: [UsersService, UsersRepository], exports: [UsersService, UsersRepository], diff --git a/backend/src/users/users.service.spec.ts b/backend/src/users/users.service.spec.ts index fdf2461..64618b2 100644 --- a/backend/src/users/users.service.spec.ts +++ b/backend/src/users/users.service.spec.ts @@ -20,6 +20,7 @@ import { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; import { RbacService } from "../auth/rbac.service"; import { MediaService } from "../media/media.service"; +import { EventsGateway } from "../realtime/events.gateway"; import { S3Service } from "../s3/s3.service"; import { UsersRepository } from "./repositories/users.repository"; import { UsersService } from "./users.service"; @@ -49,6 +50,7 @@ describe("UsersService", () => { const mockRbacService = { getUserRoles: jest.fn(), + assignRoleToUser: jest.fn(), }; const mockMediaService = { @@ -65,6 +67,11 @@ describe("UsersService", () => { get: jest.fn(), }; + const mockEventsGateway = { + isUserOnline: jest.fn(), + broadcastStatus: jest.fn(), + }; + beforeEach(async () => { jest.clearAllMocks(); @@ -77,6 +84,7 @@ describe("UsersService", () => { { provide: MediaService, useValue: mockMediaService }, { provide: S3Service, useValue: mockS3Service }, { provide: ConfigService, useValue: mockConfigService }, + { provide: EventsGateway, useValue: mockEventsGateway }, ], }).compile(); diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 4d82925..0876925 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -12,6 +12,7 @@ import { RbacService } from "../auth/rbac.service"; import type { IMediaService } from "../common/interfaces/media.interface"; import type { IStorageService } from "../common/interfaces/storage.interface"; import { MediaService } from "../media/media.service"; +import { EventsGateway } from "../realtime/events.gateway"; import { S3Service } from "../s3/s3.service"; import { UpdateUserDto } from "./dto/update-user.dto"; import { UsersRepository } from "./repositories/users.repository"; @@ -27,6 +28,8 @@ export class UsersService { private readonly rbacService: RbacService, @Inject(MediaService) private readonly mediaService: IMediaService, @Inject(S3Service) private readonly s3Service: IStorageService, + @Inject(forwardRef(() => EventsGateway)) + private readonly eventsGateway: EventsGateway, ) {} private async clearUserCache(username?: string) { @@ -137,6 +140,9 @@ export class UsersService { const { role, ...userData } = data; + // On récupère l'utilisateur actuel avant mise à jour pour comparer les préférences + const oldUser = await this.usersRepository.findOne(uuid); + const result = await this.usersRepository.update(uuid, userData); if (role) { @@ -145,6 +151,21 @@ export class UsersService { if (result[0]) { await this.clearUserCache(result[0].username); + + // Gérer le changement de préférence de statut en ligne + if ( + data.showOnlineStatus !== undefined && + data.showOnlineStatus !== oldUser?.showOnlineStatus + ) { + const isOnline = this.eventsGateway.isUserOnline(uuid); + if (isOnline) { + if (data.showOnlineStatus) { + this.eventsGateway.broadcastStatus(uuid, "online"); + } else { + this.eventsGateway.broadcastStatus(uuid, "offline"); + } + } + } } return result; } diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index 1c13a9c..74e1ab4 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -9,6 +9,8 @@ export interface User { role?: "user" | "admin" | "moderator"; status?: "active" | "verification" | "suspended" | "pending" | "deleted"; twoFactorEnabled?: boolean; + showOnlineStatus?: boolean; + showReadReceipts?: boolean; createdAt: string; }