Files
memegoat/backend/src/users/users.service.ts
Mathis HERRIOT 514bd354bf feat: add modular services and repositories for improved code organization
Introduce repository pattern across multiple services, including `favorites`, `tags`, `sessions`, `reports`, `auth`, and more. Decouple crypto functionalities into modular services like `HashingService`, `JwtService`, and `EncryptionService`. Improve testability and maintainability by simplifying dependencies and consolidating utility logic.
2026-01-14 12:11:39 +01:00

115 lines
2.8 KiB
TypeScript

import { Injectable, Logger, Inject } from "@nestjs/common";
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Cache } from "cache-manager";
import { UsersRepository } from "./repositories/users.repository";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
private readonly logger = new Logger(UsersService.name);
constructor(
private readonly usersRepository: UsersRepository,
@Inject(CACHE_MANAGER) private cacheManager: Cache,
) {}
private async clearUserCache(username?: string) {
if (username) {
await this.cacheManager.del(`users/profile/${username}`);
}
}
async create(data: {
username: string;
email: string;
passwordHash: string;
emailHash: string;
}) {
return await this.usersRepository.create(data);
}
async findByEmailHash(emailHash: string) {
return await this.usersRepository.findByEmailHash(emailHash);
}
async findOneWithPrivateData(uuid: string) {
return await this.usersRepository.findOneWithPrivateData(uuid);
}
async findAll(limit: number, offset: number) {
const [data, totalCount] = await Promise.all([
this.usersRepository.findAll(limit, offset),
this.usersRepository.countAll(),
]);
return { data, totalCount };
}
async findPublicProfile(username: string) {
return await this.usersRepository.findByUsername(username);
}
async findOne(uuid: string) {
return await this.usersRepository.findOne(uuid);
}
async update(uuid: string, data: UpdateUserDto) {
this.logger.log(`Updating user profile for ${uuid}`);
const result = await this.usersRepository.update(uuid, data);
if (result[0]) {
await this.clearUserCache(result[0].username);
}
return result;
}
async updateConsent(
uuid: string,
termsVersion: string,
privacyVersion: string,
) {
return await this.usersRepository.update(uuid, {
termsVersion,
privacyVersion,
gdprAcceptedAt: new Date(),
});
}
async setTwoFactorSecret(uuid: string, secret: string) {
return await this.usersRepository.update(uuid, {
twoFactorSecret: secret,
});
}
async toggleTwoFactor(uuid: string, enabled: boolean) {
return await this.usersRepository.update(uuid, {
isTwoFactorEnabled: enabled,
});
}
async getTwoFactorSecret(uuid: string): Promise<string | null> {
return await this.usersRepository.getTwoFactorSecret(uuid);
}
async exportUserData(uuid: string) {
const user = await this.findOneWithPrivateData(uuid);
if (!user) return null;
const [userContents, userFavorites] = await Promise.all([
this.usersRepository.getUserContents(uuid),
this.usersRepository.getUserFavorites(uuid),
]);
return {
profile: user,
contents: userContents,
favorites: userFavorites,
exportedAt: new Date(),
};
}
async remove(uuid: string) {
return await this.usersRepository.softDeleteUserAndContents(uuid);
}
}