@startuml !theme plain top to bottom direction skinparam linetype ortho class AdminController { constructor(adminService: AdminService): getStats(): Promise<{users: number, contents: numbe… } class AdminModule class AdminService { constructor(usersRepository: UsersRepository, contentsRepository: ContentsRepository, categoriesRepository: CategoriesRepository): getStats(): Promise<{users: number, contents: numbe… } class AllExceptionsFilter { logger: Logger catch(exception: unknown, host: ArgumentsHost): void } class ApiKeysController { constructor(apiKeysService: ApiKeysService): create(req: AuthenticatedRequest, createApiKeyDto: CreateApiKeyDto): Promise<{name: string, key: string, exp… findAll(req: AuthenticatedRequest): Promise revoke(req: AuthenticatedRequest, id: string): Promise } class ApiKeysModule class ApiKeysRepository { constructor(databaseService: DatabaseService): create(data: {userId: string; name: string; prefix: string; keyHash: string; expiresAt?: Date}): Promise findAll(userId: string): Promise revoke(userId: string, keyId: string): Promise findActiveByKeyHash(keyHash: string): Promise updateLastUsed(id: string): Promise } class ApiKeysService { constructor(apiKeysRepository: ApiKeysRepository, hashingService: HashingService): logger: Logger create(userId: string, name: string, expiresAt?: Date): Promise<{name: string, key: string, exp… findAll(userId: string): Promise revoke(userId: string, keyId: string): Promise validateKey(key: string): Promise } class AppController { constructor(appService: AppService): getHello(): string } class AppModule { configure(consumer: MiddlewareConsumer): void } class AppService { getHello(): string } class AuditLogInDb class AuthController { constructor(authService: AuthService, bootstrapService: BootstrapService, configService: ConfigService): register(registerDto: RegisterDto): Promise<{message: string, userId: any}> login(loginDto: LoginDto, userAgent: string, req: Request, res: Response): Promise } class AuthGuard { constructor(jwtService: JwtService, configService: ConfigService): canActivate(context: ExecutionContext): Promise } class AuthModule class AuthService { constructor(usersService: UsersService, hashingService: HashingService, jwtService: JwtService, sessionsService: SessionsService, configService: ConfigService): logger: Logger generateTwoFactorSecret(userId: string): Promise<{secret: string, qrCodeDataUrl:… enableTwoFactor(userId: string, token: string): Promise<{message: string}> disableTwoFactor(userId: string, token: string): Promise<{message: string}> register(dto: RegisterDto): Promise<{message: string, userId: any}> login(dto: LoginDto, userAgent?: string, ip?: string): Promise<{message: string, requires2FA: … verifyTwoFactorLogin(userId: string, token: string, userAgent?: string, ip?: string): Promise<{message: string, access_token:… refresh(refreshToken: string): Promise<{access_token: string, refresh_… logout(): Promise<{message: string}> } class AuthenticatedRequest { user: {sub: string, username: string} } class BootstrapService { constructor(rbacService: RbacService, usersService: UsersService, configService: ConfigService): logger: Logger bootstrapToken: string | null onApplicationBootstrap(): Promise generateBootstrapToken(): void consumeToken(token: string, username: string): Promise<{message: string}> } class CategoriesController { constructor(categoriesService: CategoriesService): findAll(): Promise findOne(id: string): Promise create(createCategoryDto: CreateCategoryDto): Promise update(id: string, updateCategoryDto: UpdateCategoryDto): Promise remove(id: string): Promise } class CategoriesModule class CategoriesRepository { constructor(databaseService: DatabaseService): findAll(): Promise countAll(): Promise findOne(id: string): Promise create(data: CreateCategoryDto & {slug: string}): Promise update(id: string, data: UpdateCategoryDto & {slug?: string; updatedAt: Date}): Promise remove(id: string): Promise } class CategoriesService { constructor(categoriesRepository: CategoriesRepository, cacheManager: Cache): logger: Logger clearCategoriesCache(): Promise findAll(): Promise findOne(id: string): Promise create(data: CreateCategoryDto): Promise update(id: string, data: UpdateCategoryDto): Promise remove(id: string): Promise } class CategoryInDb class ClamScanner { scanStream(stream: Readable): Promise<{isInfected: boolean, viruses: … } class CommonModule class ContentInDb class ContentType { MEME: GIF: } class ContentsController { constructor(contentsService: ContentsService): create(req: AuthenticatedRequest, createContentDto: CreateContentDto): Promise getUploadUrl(req: AuthenticatedRequest, fileName: string): Promise<{url: string, key: string}> upload(req: AuthenticatedRequest, file: Express.Multer.File, uploadContentDto: UploadContentDto): Promise explore(req: AuthenticatedRequest, limit: number, offset: number, sort?: "trend" | "recent", tag?: string, category?: string, author?: string): Promise<{data: any, totalCount: any}> trends(req: AuthenticatedRequest, limit: number, offset: number): Promise<{data: any, totalCount: any}> recent(req: AuthenticatedRequest, limit: number, offset: number): Promise<{data: any, totalCount: any}> findOne(idOrSlug: string, req: AuthenticatedRequest, res: Response): Promise incrementUsage(id: string): Promise update(id: string, req: AuthenticatedRequest, updateContentDto: any): Promise remove(id: string, req: AuthenticatedRequest): Promise removeAdmin(id: string): Promise updateAdmin(id: string, updateContentDto: any): Promise } class ContentsModule class ContentsRepository { constructor(databaseService: DatabaseService): findAll(options: FindAllOptions): Promise create(data: NewContentInDb & {userId: string}, tagNames?: string[]): Promise findOne(idOrSlug: string, userId?: string): Promise count(options: {tag?: string; category?: string; author?: string; query?: string; favoritesOnly?: boolean; userId?: string}): Promise incrementViews(id: string): Promise incrementUsage(id: string): Promise softDelete(id: string, userId: string): Promise softDeleteAdmin(id: string): Promise update(id: string, data: Partial): Promise findBySlug(slug: string): Promise purgeSoftDeleted(before: Date): Promise } class ContentsService { constructor(contentsRepository: ContentsRepository, s3Service: IStorageService, mediaService: IMediaService, configService: ConfigService, cacheManager: Cache): logger: Logger clearContentsCache(): Promise getUploadUrl(userId: string, fileName: string): Promise<{url: string, key: string}> uploadAndProcess(userId: string, file: Express.Multer.File, data: UploadContentDto): Promise findAll(options: {limit: number; offset: number; sortBy?: "trend" | "recent"; tag?: string; category?: string; author?: string; query?: string; favoritesOnly?: boolean; userId?: string}): Promise<{data: any, totalCount: any}> create(userId: string, data: CreateContentDto): Promise incrementViews(id: string): Promise incrementUsage(id: string): Promise remove(id: string, userId: string): Promise removeAdmin(id: string): Promise updateAdmin(id: string, data: any): Promise update(id: string, userId: string, data: any): Promise findOne(idOrSlug: string, userId?: string): Promise generateBotHtml(content: {title: string; storageKey: string}): string generateSlug(text: string): string ensureUniqueSlug(title: string): Promise } class CrawlerDetectionMiddleware { logger: Logger SUSPICIOUS_PATTERNS: RegExp[] BOT_USER_AGENTS: RegExp[] use(req: Request, res: Response, next: NextFunction): void } class CreateApiKeyDto { name: string expiresAt: string } class CreateCategoryDto { name: string description: string iconUrl: string } class CreateContentDto { type: "meme" | "gif" title: string storageKey: string mimeType: string fileSize: number categoryId: string tags: string[] } class CreateReportDto { contentId: string tagId: string reason: "inappropriate" | "spam" | "copyright" … description: string } class CryptoModule class CryptoService { constructor(hashingService: HashingService, jwtService: JwtService, encryptionService: EncryptionService, postQuantumService: PostQuantumService): hashEmail(email: string): Promise hashIp(ip: string): Promise getPgpEncryptionKey(): string hashPassword(password: string): Promise verifyPassword(password: string, hash: string): Promise generateJwt(payload: jose.JWTPayload, expiresIn?: string): Promise verifyJwt(token: string): Promise encryptContent(content: string): Promise decryptContent(jwe: string): Promise signContent(content: string): Promise verifyContentSignature(jws: string): Promise generatePostQuantumKeyPair(): {publicKey: Uint8Array… encapsulate(publicKey: Uint8Array): {cipherText: Uint8Array, sharedSecret: … decapsulate(cipherText: Uint8Array, secretKey: Uint8Array): Uint8Array } class DatabaseModule class DatabaseService { constructor(configService: ConfigService): logger: Logger pool: Pool db: ReturnType onModuleInit(): Promise onModuleDestroy(): Promise getDatabaseConnectionString(): string } class EncryptionService { constructor(configService: ConfigService): logger: Logger jwtSecret: Uint8Array encryptionKey: Uint8Array encryptContent(content: string): Promise decryptContent(jwe: string): Promise signContent(content: string): Promise verifyContentSignature(jws: string): Promise getPgpEncryptionKey(): string } class Env class FavoriteInDb class FavoritesController { constructor(favoritesService: FavoritesService): add(req: AuthenticatedRequest, contentId: string): Promise remove(req: AuthenticatedRequest, contentId: string): Promise list(req: AuthenticatedRequest, limit: number, offset: number): Promise } class FavoritesModule class FavoritesRepository { constructor(databaseService: DatabaseService): findContentById(contentId: string): Promise add(userId: string, contentId: string): Promise remove(userId: string, contentId: string): Promise findByUserId(userId: string, limit: number, offset: number): Promise } class FavoritesService { constructor(favoritesRepository: FavoritesRepository): logger: Logger addFavorite(userId: string, contentId: string): Promise removeFavorite(userId: string, contentId: string): Promise getUserFavorites(userId: string, limit: number, offset: number): Promise } class FindAllOptions { limit: number offset: number sortBy: "trend" | "recent" tag: string category: string author: string query: string favoritesOnly: boolean userId: string } class HTTPLoggerMiddleware { logger: Logger use(request: Request, response: Response, next: NextFunction): void } class HashingService { hashEmail(email: string): Promise hashIp(ip: string): Promise hashSha256(text: string): Promise hashPassword(password: string): Promise verifyPassword(password: string, hash: string): Promise } class HealthController { constructor(databaseService: DatabaseService, cacheManager: Cache): check(): Promise } class IMailService { sendEmailValidation(email: string, token: string): Promise sendPasswordReset(email: string, token: string): Promise } class IMediaProcessorStrategy { canHandle(mimeType: string): boolean process(buffer: Buffer, options?: Record): Promise } class IMediaService { scanFile(buffer: Buffer, filename: string): Promise processImage(buffer: Buffer, format?: "webp" | "avif", resize?: {width?: number; height?: number}): Promise processVideo(buffer: Buffer, format?: "webm" | "av1"): Promise } class IStorageService { uploadFile(fileName: string, file: Buffer, mimeType: string, metaData?: Record, bucketName?: string): Promise getFile(fileName: string, bucketName?: string): Promise getFileUrl(fileName: string, expiry?: number, bucketName?: string): Promise getUploadUrl(fileName: string, expiry?: number, bucketName?: string): Promise deleteFile(fileName: string, bucketName?: string): Promise getFileInfo(fileName: string, bucketName?: string): Promise moveFile(sourceFileName: string, destinationFileName: string, sourceBucketName?: string, destinationBucketName?: string): Promise getPublicUrl(storageKey: string): string } class ImageProcessorStrategy { logger: Logger canHandle(mimeType: string): boolean process(buffer: Buffer, options?: {format: "webp" | "avif"; resize?: {width?: number; height?: number}}): Promise } class JwtService { constructor(configService: ConfigService): logger: Logger jwtSecret: Uint8Array generateJwt(payload: jose.JWTPayload, expiresIn?: string): Promise verifyJwt(token: string): Promise } class LoginDto { email: string password: string } class MailModule class MailService { constructor(mailerService: MailerService, configService: ConfigService): logger: Logger domain: string sendEmailValidation(email: string, token: string): Promise sendPasswordReset(email: string, token: string): Promise } class MediaController { constructor(s3Service: S3Service): logger: Logger getFile(path: string, res: Response): Promise } class MediaModule class MediaProcessingResult { buffer: Buffer mimeType: string extension: string width: number height: number size: number } class MediaProcessingResult { buffer: Buffer mimeType: string extension: string width: number height: number size: number } class MediaService { constructor(configService: ConfigService, imageProcessor: ImageProcessorStrategy, videoProcessor: VideoProcessorStrategy): logger: Logger clamscan: ClamScanner | null isClamAvInitialized: boolean initClamScan(): Promise scanFile(buffer: Buffer, filename: string): Promise processImage(buffer: Buffer, format?: "webp" | "avif", resize?: {width?: number; height?: number}): Promise processVideo(buffer: Buffer, format?: "webm" | "av1"): Promise } class NewAuditLogInDb class NewCategoryInDb class NewContentInDb class NewFavoriteInDb class NewReportInDb class NewTagInDb class NewUserInDb class OptionalAuthGuard { constructor(jwtService: JwtService, configService: ConfigService): canActivate(context: ExecutionContext): Promise } class PostQuantumService { generatePostQuantumKeyPair(): {publicKey: Uint8Array… encapsulate(publicKey: Uint8Array): {cipherText: Uint8Array, sharedSecret: … decapsulate(cipherText: Uint8Array, secretKey: Uint8Array): Uint8Array } class PurgeService { constructor(sessionsRepository: SessionsRepository, reportsRepository: ReportsRepository, usersRepository: UsersRepository, contentsRepository: ContentsRepository): logger: Logger purgeExpiredData(): Promise } class RbacRepository { constructor(databaseService: DatabaseService): findRolesByUserId(userId: string): Promise findPermissionsByUserId(userId: string): Promise countRoles(): Promise countAdmins(): Promise createRole(name: string, slug: string, description?: string): Promise assignRole(userId: string, roleSlug: string): Promise } class RbacService { constructor(rbacRepository: RbacRepository): logger: Logger onApplicationBootstrap(): Promise seedRoles(): Promise getUserRoles(userId: string): Promise getUserPermissions(userId: string): Promise countAdmins(): Promise assignRoleToUser(userId: string, roleSlug: string): Promise } class RefreshDto { refresh_token: string } class RegisterDto { username: string displayName: string email: string password: string } class ReportInDb class ReportReason { INAPPROPRIATE: SPAM: COPYRIGHT: OTHER: } class ReportStatus { PENDING: REVIEWED: RESOLVED: DISMISSED: } class ReportsController { constructor(reportsService: ReportsService): create(req: AuthenticatedRequest, createReportDto: CreateReportDto): Promise findAll(limit: number, offset: number): Promise updateStatus(id: string, updateReportStatusDto: UpdateReportStatusDto): Promise } class ReportsModule class ReportsRepository { constructor(databaseService: DatabaseService): create(data: {reporterId: string; contentId?: string; tagId?: string; reason: "inappropriate" | "spam" | "copyright" | "other"; description?: string}): Promise findAll(limit: number, offset: number): Promise updateStatus(id: string, status: "pending" | "reviewed" | "resolved" | "dismissed"): Promise purgeObsolete(now: Date): Promise } class ReportsService { constructor(reportsRepository: ReportsRepository): logger: Logger create(reporterId: string, data: CreateReportDto): Promise findAll(limit: number, offset: number): Promise updateStatus(id: string, status: "pending" | "reviewed" | "resolved" | "dismissed"): Promise } class RequestWithUser { user: {sub?: string, username?: string, id?: … } class RolesGuard { constructor(reflector: Reflector, rbacService: RbacService): canActivate(context: ExecutionContext): Promise } class S3Module class S3Service { constructor(configService: ConfigService): logger: Logger minioClient: Minio.Client bucketName: string onModuleInit(): Promise ensureBucketExists(bucketName: string): Promise uploadFile(fileName: string, file: Buffer, mimeType: string, metaData?: Minio.ItemBucketMetadata, bucketName?: string): Promise getFile(fileName: string, bucketName?: string): Promise getFileUrl(fileName: string, expiry?: number, bucketName?: string): Promise getUploadUrl(fileName: string, expiry?: number, bucketName?: string): Promise deleteFile(fileName: string, bucketName?: string): Promise getFileInfo(fileName: string, bucketName?: string): Promise moveFile(sourceFileName: string, destinationFileName: string, sourceBucketName?: string, destinationBucketName?: string): Promise getPublicUrl(storageKey: string): string } class ScanResult { isInfected: boolean virusName: string } class ScanResult { isInfected: boolean virusName: string } class SessionData { accessToken: string refreshToken: string userId: string } class SessionsModule class SessionsRepository { constructor(databaseService: DatabaseService): create(data: {userId: string; refreshToken: string; userAgent?: string; ipHash?: string | null; expiresAt: Date}): Promise findValidByRefreshToken(refreshToken: string): Promise update(sessionId: string, data: Record): Promise revoke(sessionId: string): Promise revokeAllByUserId(userId: string): Promise purgeExpired(now: Date): Promise } class SessionsService { constructor(sessionsRepository: SessionsRepository, hashingService: HashingService, jwtService: JwtService): createSession(userId: string, userAgent?: string, ip?: string): Promise refreshSession(oldRefreshToken: string): Promise revokeSession(sessionId: string): Promise revokeAllUserSessions(userId: string): Promise } class TagInDb class TagsController { constructor(tagsService: TagsService): findAll(limit: number, offset: number, query?: string, sort?: "popular" | "recent"): Promise } class TagsModule class TagsRepository { constructor(databaseService: DatabaseService): findAll(options: {limit: number; offset: number; query?: string; sortBy?: "popular" | "recent"}): Promise } class TagsService { constructor(tagsRepository: TagsRepository): logger: Logger findAll(options: {limit: number; offset: number; query?: string; sortBy?: "popular" | "recent"}): Promise } class UpdateCategoryDto class UpdateConsentDto { termsVersion: string privacyVersion: string } class UpdateReportStatusDto { status: "pending" | "reviewed" | "resolved" | "… } class UpdateUserDto { displayName: string bio: string avatarUrl: string status: "active" | "verification" | "suspended"… role: string } class UploadContentDto { type: "meme" | "gif" title: string categoryId: string tags: string[] } class UserInDb class UsersController { constructor(usersService: UsersService, authService: AuthService): findAll(limit: number, offset: number): Promise<{data: any, totalCount: any}> findPublicProfile(username: string): Promise findMe(req: AuthenticatedRequest): Promise exportMe(req: AuthenticatedRequest): Promise updateAvatar(req: AuthenticatedRequest, file: Express.Multer.File): Promise updateConsent(req: AuthenticatedRequest, consentDto: UpdateConsentDto): Promise removeMe(req: AuthenticatedRequest): Promise removeAdmin(uuid: string): Promise updateAdmin(uuid: string, updateUserDto: UpdateUserDto): Promise setup2fa(req: AuthenticatedRequest): Promise<{secret: string, qrCodeDataUrl:… enable2fa(req: AuthenticatedRequest, token: string): Promise<{message: string}> disable2fa(req: AuthenticatedRequest, token: string): Promise<{message: string}> } class UsersModule class UsersRepository { constructor(databaseService: DatabaseService): create(data: {username: string; email: string; passwordHash: string; emailHash: string}): Promise findByEmailHash(emailHash: string): Promise findOneWithPrivateData(uuid: string): Promise countAll(): Promise findAll(limit: number, offset: number): Promise findByUsername(username: string): Promise findOne(uuid: string): Promise update(uuid: string, data: Partial): Promise getTwoFactorSecret(uuid: string): Promise getUserContents(uuid: string): Promise getUserFavorites(uuid: string): Promise softDeleteUserAndContents(uuid: string): Promise purgeDeleted(before: Date): Promise } class UsersService { constructor(usersRepository: UsersRepository, cacheManager: Cache, rbacService: RbacService, mediaService: IMediaService, s3Service: IStorageService): logger: Logger clearUserCache(username?: string): Promise create(data: {username: string; email: string; passwordHash: string; emailHash: string}): Promise findByEmailHash(emailHash: string): Promise findOneWithPrivateData(uuid: string): Promise findAll(limit: number, offset: number): Promise<{data: any, totalCount: any}> findPublicProfile(username: string): Promise findOne(uuid: string): Promise update(uuid: string, data: UpdateUserDto): Promise updateAvatar(uuid: string, file: Express.Multer.File): Promise updateConsent(uuid: string, termsVersion: string, privacyVersion: string): Promise setTwoFactorSecret(uuid: string, secret: string): Promise toggleTwoFactor(uuid: string, enabled: boolean): Promise getTwoFactorSecret(uuid: string): Promise exportUserData(uuid: string): Promise } class Verify2faDto { userId: string token: string } class VideoProcessorStrategy { logger: Logger canHandle(mimeType: string): boolean process(buffer: Buffer, options?: {format: "webm" | "av1"}): Promise } AdminController -[#595959,dashed]-> AdminService AdminService -[#595959,dashed]-> CategoriesRepository AdminService -[#595959,dashed]-> ContentsRepository AdminService -[#595959,dashed]-> UsersRepository AllExceptionsFilter -[#595959,dashed]-> RequestWithUser ApiKeysController -[#595959,dashed]-> ApiKeysService ApiKeysController -[#595959,dashed]-> AuthenticatedRequest ApiKeysController -[#595959,dashed]-> CreateApiKeyDto ApiKeysRepository -[#595959,dashed]-> DatabaseService ApiKeysService -[#595959,dashed]-> ApiKeysRepository ApiKeysService -[#595959,dashed]-> ApiKeysService ApiKeysService -[#595959,dashed]-> HashingService AppController -[#595959,dashed]-> AppService AppModule -[#595959,dashed]-> CrawlerDetectionMiddleware AppModule -[#595959,dashed]-> HTTPLoggerMiddleware AuthController -[#595959,dashed]-> AuthService AuthController -[#595959,dashed]-> BootstrapService AuthController -[#595959,dashed]-> LoginDto AuthController -[#595959,dashed]-> RegisterDto AuthController -[#595959,dashed]-> SessionData AuthController -[#595959,dashed]-> Verify2faDto AuthGuard -[#595959,dashed]-> JwtService AuthGuard -[#595959,dashed]-> SessionData AuthService -[#595959,dashed]-> AuthService AuthService -[#595959,dashed]-> HashingService AuthService -[#595959,dashed]-> JwtService AuthService -[#595959,dashed]-> LoginDto AuthService -[#595959,dashed]-> RegisterDto AuthService -[#595959,dashed]-> SessionsService AuthService -[#595959,dashed]-> UsersService BootstrapService -[#595959,dashed]-> BootstrapService BootstrapService -[#595959,dashed]-> RbacService BootstrapService -[#595959,dashed]-> UsersService CategoriesController -[#595959,dashed]-> AuthGuard CategoriesController -[#595959,dashed]-> CategoriesService CategoriesController -[#595959,dashed]-> CreateCategoryDto CategoriesController -[#595959,dashed]-> RolesGuard CategoriesController -[#595959,dashed]-> UpdateCategoryDto CategoriesRepository -[#595959,dashed]-> CreateCategoryDto CategoriesRepository -[#595959,dashed]-> DatabaseService CategoriesRepository -[#595959,dashed]-> UpdateCategoryDto CategoriesService -[#595959,dashed]-> CategoriesRepository CategoriesService -[#595959,dashed]-> CategoriesService CategoriesService -[#595959,dashed]-> CreateCategoryDto CategoriesService -[#595959,dashed]-> UpdateCategoryDto ContentsController -[#595959,dashed]-> AuthGuard ContentsController -[#595959,dashed]-> AuthenticatedRequest ContentsController -[#595959,dashed]-> ContentsService ContentsController -[#595959,dashed]-> CreateContentDto ContentsController -[#595959,dashed]-> OptionalAuthGuard ContentsController -[#595959,dashed]-> RolesGuard ContentsController -[#595959,dashed]-> UploadContentDto ContentsRepository -[#595959,dashed]-> DatabaseService ContentsRepository -[#595959,dashed]-> FindAllOptions ContentsRepository -[#595959,dashed]-> NewContentInDb ContentsService -[#595959,dashed]-> ContentsRepository ContentsService -[#595959,dashed]-> ContentsService ContentsService -[#595959,dashed]-> CreateContentDto ContentsService -[#595959,dashed]-> IMediaService ContentsService -[#595959,dashed]-> IStorageService ContentsService -[#595959,dashed]-> MediaProcessingResult ContentsService -[#595959,dashed]-> MediaService ContentsService -[#595959,dashed]-> S3Service ContentsService -[#595959,dashed]-> UploadContentDto CryptoService -[#595959,dashed]-> EncryptionService CryptoService -[#595959,dashed]-> HashingService CryptoService -[#595959,dashed]-> JwtService CryptoService -[#595959,dashed]-> PostQuantumService DatabaseService -[#595959,dashed]-> DatabaseService EncryptionService -[#595959,dashed]-> EncryptionService FavoritesController -[#595959,dashed]-> AuthenticatedRequest FavoritesController -[#595959,dashed]-> FavoritesService FavoritesRepository -[#595959,dashed]-> DatabaseService FavoritesService -[#595959,dashed]-> FavoritesRepository FavoritesService -[#595959,dashed]-> FavoritesService HealthController -[#595959,dashed]-> DatabaseService IMediaProcessorStrategy -[#595959,dashed]-> MediaProcessingResult IMediaService -[#595959,dashed]-> MediaProcessingResult IMediaService -[#595959,dashed]-> ScanResult ImageProcessorStrategy -[#008200,dashed]-^ IMediaProcessorStrategy ImageProcessorStrategy -[#595959,dashed]-> ImageProcessorStrategy ImageProcessorStrategy -[#595959,dashed]-> MediaProcessingResult JwtService -[#595959,dashed]-> JwtService MailService -[#008200,dashed]-^ IMailService MailService -[#595959,dashed]-> MailService MediaController -[#595959,dashed]-> MediaController MediaController -[#595959,dashed]-> S3Service MediaService -[#595959,dashed]-> ClamScanner MediaService -[#008200,dashed]-^ IMediaService MediaService -[#595959,dashed]-> ImageProcessorStrategy MediaService -[#595959,dashed]-> MediaProcessingResult MediaService -[#595959,dashed]-> MediaService MediaService -[#595959,dashed]-> ScanResult MediaService -[#595959,dashed]-> VideoProcessorStrategy OptionalAuthGuard -[#595959,dashed]-> JwtService OptionalAuthGuard -[#595959,dashed]-> SessionData PurgeService -[#595959,dashed]-> ContentsRepository PurgeService -[#595959,dashed]-> PurgeService PurgeService -[#595959,dashed]-> ReportsRepository PurgeService -[#595959,dashed]-> SessionsRepository PurgeService -[#595959,dashed]-> UsersRepository RbacRepository -[#595959,dashed]-> DatabaseService RbacService -[#595959,dashed]-> RbacRepository RbacService -[#595959,dashed]-> RbacService ReportsController -[#595959,dashed]-> AuthGuard ReportsController -[#595959,dashed]-> AuthenticatedRequest ReportsController -[#595959,dashed]-> CreateReportDto ReportsController -[#595959,dashed]-> ReportsService ReportsController -[#595959,dashed]-> RolesGuard ReportsController -[#595959,dashed]-> UpdateReportStatusDto ReportsRepository -[#595959,dashed]-> DatabaseService ReportsService -[#595959,dashed]-> CreateReportDto ReportsService -[#595959,dashed]-> ReportsRepository ReportsService -[#595959,dashed]-> ReportsService RolesGuard -[#595959,dashed]-> RbacService S3Service -[#008200,dashed]-^ IStorageService S3Service -[#595959,dashed]-> S3Service SessionsRepository -[#595959,dashed]-> DatabaseService SessionsService -[#595959,dashed]-> HashingService SessionsService -[#595959,dashed]-> JwtService SessionsService -[#595959,dashed]-> SessionsRepository TagsController -[#595959,dashed]-> TagsService TagsRepository -[#595959,dashed]-> DatabaseService TagsService -[#595959,dashed]-> TagsRepository TagsService -[#595959,dashed]-> TagsService UsersController -[#595959,dashed]-> AuthGuard UsersController -[#595959,dashed]-> AuthService UsersController -[#595959,dashed]-> AuthenticatedRequest UsersController -[#595959,dashed]-> RolesGuard UsersController -[#595959,dashed]-> UpdateConsentDto UsersController -[#595959,dashed]-> UpdateUserDto UsersController -[#595959,dashed]-> UsersService UsersRepository -[#595959,dashed]-> DatabaseService UsersService -[#595959,dashed]-> IMediaService UsersService -[#595959,dashed]-> IStorageService UsersService -[#595959,dashed]-> MediaService UsersService -[#595959,dashed]-> RbacService UsersService -[#595959,dashed]-> S3Service UsersService -[#595959,dashed]-> UpdateUserDto UsersService -[#595959,dashed]-> UsersRepository UsersService -[#595959,dashed]-> UsersService VideoProcessorStrategy -[#008200,dashed]-^ IMediaProcessorStrategy VideoProcessorStrategy -[#595959,dashed]-> MediaProcessingResult VideoProcessorStrategy -[#595959,dashed]-> VideoProcessorStrategy @enduml