/** * @copyright 2026 NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { User } from "@library/shared-types"; import { prisma } from "../lib/prisma"; import { SuggestionStatus } from "@prisma/client"; export class UserService { private prisma = prisma; async getAllUsers(): Promise { const users = await this.prisma.user.findMany({ orderBy: { username: "asc" }, }); return users.map((user) => ({ id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, })); } async getUserById(id: string): Promise { const user = await this.prisma.user.findUnique({ where: { id }, }); if (!user) { return null; } return { id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, }; } async banUser(id: string): Promise { const user = await this.prisma.user.update({ where: { id }, data: { isBanned: true }, }); return { id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, }; } async unbanUser(id: string): Promise { const user = await this.prisma.user.update({ where: { id }, data: { isBanned: false }, }); return { id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, }; } async isUserBanned(id: string): Promise { const user = await this.prisma.user.findUnique({ where: { id }, select: { isBanned: true }, }); return user?.isBanned ?? false; } async getUserBySlug(slug: string): Promise { const user = await this.prisma.user.findFirst({ where: { slug }, }); if (!user) { return null; } return { id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, }; } async updateUserSettings( id: string, updates: { slug?: string; displayName?: string; bio?: string; profilePublic?: boolean; } ): Promise { const user = await this.prisma.user.update({ where: { id }, data: updates, }); return { id: user.id, discordId: user.discordId, username: user.username, email: user.email, avatar: user.avatar || undefined, slug: user.slug || undefined, displayName: user.displayName || undefined, bio: user.bio || undefined, profilePublic: user.profilePublic, isAdmin: user.isAdmin, isBanned: user.isBanned, inDiscord: user.inDiscord, isVip: user.isVip, isMod: user.isMod, isStaff: user.isStaff, }; } async getUserProfile(identifier: string): Promise<{ id: string; username: string; displayName?: string | null; avatar?: string | null; bio?: string | null; slug?: string | null; isStaff: boolean; isMod: boolean; isVip: boolean; inDiscord: boolean; profilePublic: boolean; createdAt: Date; stats: { suggestionsCount: number; suggestionsAcceptedCount: number; likesCount: number; commentsCount: number; }; } | null> { // Try to find by slug first, then by id if it's a valid ObjectId const isValidObjectId = /^[0-9a-f]{24}$/i.test(identifier); const whereConditions = isValidObjectId ? [{ slug: identifier }, { id: identifier }] : [{ slug: identifier }]; const user = await this.prisma.user.findFirst({ where: { OR: whereConditions, }, include: { suggestions: { select: { id: true, status: true }, }, likes: { select: { id: true }, }, comments: { select: { id: true }, }, }, }); if (!user) { return null; } return { id: user.id, username: user.username, displayName: user.displayName, avatar: user.avatar, bio: user.bio, slug: user.slug, isStaff: user.isStaff, isMod: user.isMod, isVip: user.isVip, inDiscord: user.inDiscord, profilePublic: user.profilePublic, createdAt: user.createdAt, stats: { suggestionsCount: user.suggestions.length, suggestionsAcceptedCount: user.suggestions.filter( (suggestion) => suggestion.status === SuggestionStatus.ACCEPTED ).length, likesCount: user.likes.length, commentsCount: user.comments.length, }, }; } }