155 lines
3.6 KiB
TypeScript
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 }
|
|
});
|
|
}
|
|
};
|