Add `email` and `status` fields to user schema for better data handling. Update `UsersService` with new service dependencies (`RbacService`, `MediaService`, `S3Service`, `ConfigService`) for enhanced functionality. Mock dependencies in tests for improved coverage. Adjust user model with optional and extended fields for flexibility. Streamline and update import statements.
164 lines
3.9 KiB
TypeScript
164 lines
3.9 KiB
TypeScript
import { Injectable } from "@nestjs/common";
|
|
import { and, eq, lte, sql } from "drizzle-orm";
|
|
import { DatabaseService } from "../../database/database.service";
|
|
import { contents, favorites, users } from "../../database/schemas";
|
|
|
|
@Injectable()
|
|
export class UsersRepository {
|
|
constructor(private readonly databaseService: DatabaseService) {}
|
|
|
|
async create(data: {
|
|
username: string;
|
|
email: string;
|
|
passwordHash: string;
|
|
emailHash: string;
|
|
}) {
|
|
const [newUser] = await this.databaseService.db
|
|
.insert(users)
|
|
.values(data)
|
|
.returning();
|
|
return newUser;
|
|
}
|
|
|
|
async findByEmailHash(emailHash: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
uuid: users.uuid,
|
|
username: users.username,
|
|
email: users.email,
|
|
passwordHash: users.passwordHash,
|
|
status: users.status,
|
|
isTwoFactorEnabled: users.isTwoFactorEnabled,
|
|
})
|
|
.from(users)
|
|
.where(eq(users.emailHash, emailHash))
|
|
.limit(1);
|
|
return result[0] || null;
|
|
}
|
|
|
|
async findOneWithPrivateData(uuid: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
uuid: users.uuid,
|
|
username: users.username,
|
|
email: users.email,
|
|
displayName: users.displayName,
|
|
avatarUrl: users.avatarUrl,
|
|
bio: users.bio,
|
|
status: users.status,
|
|
isTwoFactorEnabled: users.isTwoFactorEnabled,
|
|
createdAt: users.createdAt,
|
|
updatedAt: users.updatedAt,
|
|
})
|
|
.from(users)
|
|
.where(eq(users.uuid, uuid))
|
|
.limit(1);
|
|
return result[0] || null;
|
|
}
|
|
|
|
async countAll() {
|
|
const result = await this.databaseService.db
|
|
.select({ count: sql<number>`count(*)` })
|
|
.from(users);
|
|
return Number(result[0].count);
|
|
}
|
|
|
|
async findAll(limit: number, offset: number) {
|
|
return await this.databaseService.db
|
|
.select({
|
|
uuid: users.uuid,
|
|
username: users.username,
|
|
email: users.email,
|
|
displayName: users.displayName,
|
|
avatarUrl: users.avatarUrl,
|
|
status: users.status,
|
|
createdAt: users.createdAt,
|
|
})
|
|
.from(users)
|
|
.limit(limit)
|
|
.offset(offset);
|
|
}
|
|
|
|
async findByUsername(username: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
uuid: users.uuid,
|
|
username: users.username,
|
|
displayName: users.displayName,
|
|
avatarUrl: users.avatarUrl,
|
|
bio: users.bio,
|
|
createdAt: users.createdAt,
|
|
})
|
|
.from(users)
|
|
.where(eq(users.username, username))
|
|
.limit(1);
|
|
return result[0] || null;
|
|
}
|
|
|
|
async findOne(uuid: string) {
|
|
const result = await this.databaseService.db
|
|
.select()
|
|
.from(users)
|
|
.where(eq(users.uuid, uuid))
|
|
.limit(1);
|
|
return result[0] || null;
|
|
}
|
|
|
|
async update(uuid: string, data: Partial<typeof users.$inferInsert>) {
|
|
return await this.databaseService.db
|
|
.update(users)
|
|
.set({ ...data, updatedAt: new Date() })
|
|
.where(eq(users.uuid, uuid))
|
|
.returning();
|
|
}
|
|
|
|
async getTwoFactorSecret(uuid: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
secret: users.twoFactorSecret,
|
|
})
|
|
.from(users)
|
|
.where(eq(users.uuid, uuid))
|
|
.limit(1);
|
|
return result[0]?.secret || null;
|
|
}
|
|
|
|
async getUserContents(uuid: string) {
|
|
return await this.databaseService.db
|
|
.select()
|
|
.from(contents)
|
|
.where(eq(contents.userId, uuid));
|
|
}
|
|
|
|
async getUserFavorites(uuid: string) {
|
|
return await this.databaseService.db
|
|
.select()
|
|
.from(favorites)
|
|
.where(eq(favorites.userId, uuid));
|
|
}
|
|
|
|
async softDeleteUserAndContents(uuid: string) {
|
|
return await this.databaseService.db.transaction(async (tx) => {
|
|
const userResult = await tx
|
|
.update(users)
|
|
.set({ status: "deleted", deletedAt: new Date() })
|
|
.where(eq(users.uuid, uuid))
|
|
.returning();
|
|
|
|
await tx
|
|
.update(contents)
|
|
.set({ deletedAt: new Date() })
|
|
.where(eq(contents.userId, uuid));
|
|
|
|
return userResult;
|
|
});
|
|
}
|
|
|
|
async purgeDeleted(before: Date) {
|
|
return await this.databaseService.db
|
|
.delete(users)
|
|
.where(and(eq(users.status, "deleted"), lte(users.deletedAt, before)))
|
|
.returning();
|
|
}
|
|
}
|