Files
Nexus_Mat/backend/services/userService.ts
SpecialX d2468e9fca
Some checks failed
docker-push / build-and-push (push) Failing after 18s
docker-push / deploy (push) Has been skipped
fix: user registration with email support
2025-11-28 19:29:49 +08:00

155 lines
3.6 KiB
TypeScript

import prisma from '../../lib/prisma';
import { UserDTO, UserRole } from '../../types';
import { hashPassword, verifyPassword } from '../../lib/auth';
export const UserService = {
/**
* Retrieves a user by ID with specific fields selected.
*/
async getUserById(id: string): Promise<UserDTO | null> {
const user = await prisma.user.findUnique({
where: { id },
select: {
id: true,
username: true,
avatarUrl: true,
role: true,
status: true,
createdAt: true,
lastLogin: true
}
});
if (!user) return null;
return {
...user,
role: user.role as UserRole, // Cast Prisma enum to TS enum
createdAt: user.createdAt.toISOString(),
lastLogin: user.lastLogin?.toISOString() || new Date().toISOString(),
avatarUrl: user.avatarUrl || ''
};
},
/**
* Get user by username (for login)
*/
async getUserByUsername(username: string) {
return prisma.user.findUnique({
where: { username }
});
},
/**
* Get user by email
*/
async getUserByEmail(email: string) {
return prisma.user.findUnique({
where: { email }
});
},
/**
* Create a new user with hashed password
*/
async createUser(username: string, password: string, email?: string) {
const hashedPassword = await hashPassword(password);
// Generate a random avatar URL using DiceBear or similar service
const avatarUrl = `https://api.dicebear.com/7.x/avataaars/svg?seed=${username}`;
return prisma.user.create({
data: {
username,
password: hashedPassword,
email,
avatarUrl,
role: 'CREATOR',
status: 'ACTIVE'
}
});
},
/**
* Authenticate user with username and password
* Returns user if credentials are valid, null otherwise
*/
async authenticateUser(username: string, password: string): Promise<UserDTO | null> {
const user = await this.getUserByUsername(username);
if (!user || !user.password) {
return null;
}
const isValid = await verifyPassword(password, user.password);
if (!isValid) {
return null;
}
// Update last login time
await prisma.user.update({
where: { id: user.id },
data: { lastLogin: new Date() }
});
// Return user DTO (without password)
return {
id: user.id,
username: user.username,
avatarUrl: user.avatarUrl || '',
role: user.role as UserRole,
status: user.status as any,
createdAt: user.createdAt.toISOString(),
lastLogin: new Date().toISOString()
};
},
/**
* Admin: Get all users
*/
async getAllUsers() {
return prisma.user.findMany({
orderBy: { createdAt: 'desc' }
});
},
/**
* Updates user profile settings
*/
async updateUser(id: string, data: { username?: string; avatarUrl?: string }) {
return prisma.user.update({
where: { id },
data: {
username: data.username,
avatarUrl: data.avatarUrl
}
});
},
/**
* Admin: Update user role
*/
async updateUserRole(id: string, role: 'USER' | 'CREATOR' | 'MANAGER' | 'ADMIN') {
return prisma.user.update({
where: { id },
data: { role }
});
},
/**
* Admin: Toggle Ban Status
*/
async toggleUserStatus(id: string) {
const user = await prisma.user.findUnique({ where: { id } });
if (!user) throw new Error('User not found');
const newStatus = user.status === 'ACTIVE' ? 'BANNED' : 'ACTIVE';
return prisma.user.update({
where: { id },
data: { status: newStatus }
});
}
};