import bcrypt from 'bcryptjs' import jwt from 'jsonwebtoken' import { cookies } from 'next/headers' import { prisma } from './prisma' const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production' const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret-change-in-production' const SALT_ROUNDS = 10 export interface JWTPayload { userId: string email: string role: string } export interface RefreshTokenPayload { userId: string tokenType: 'refresh' } export async function hashPassword(password: string): Promise { return bcrypt.hash(password, SALT_ROUNDS) } export async function verifyPassword(password: string, hash: string): Promise { return bcrypt.compare(password, hash) } export function generateToken(payload: JWTPayload): string { return jwt.sign(payload, JWT_SECRET, { expiresIn: '15m' }) // Access token for 15 minutes } export function generateRefreshToken(): string { const token = jwt.sign( { tokenType: 'refresh' } as RefreshTokenPayload, JWT_REFRESH_SECRET, { expiresIn: '7d' } ) // Remove the header and signature to get a cleaner token return token.split('.')[2] || token } export function verifyToken(token: string): JWTPayload | null { try { return jwt.verify(token, JWT_SECRET) as JWTPayload } catch { return null } } export function verifyRefreshToken(token: string): RefreshTokenPayload | null { try { // Reconstruct the full JWT token const fullToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${token}.signature` return jwt.verify(fullToken, JWT_REFRESH_SECRET) as RefreshTokenPayload } catch { return null } } export async function getCurrentUser() { const cookieStore = await cookies() const token = cookieStore.get('auth-token')?.value if (!token) { return null } const payload = verifyToken(token) if (!payload) { return null } const user = await prisma.user.findUnique({ where: { id: payload.userId }, select: { id: true, email: true, username: true, displayName: true, avatarUrl: true, bio: true, role: true, createdAt: true, artist: { select: { id: true, name: true, slug: true, verified: true, }, }, label: { select: { id: true, name: true, slug: true, }, }, }, }) return user } export async function requireAuth() { const user = await getCurrentUser() if (!user) { throw new Error('Unauthorized') } return user } export async function requireArtist() { const user = await requireAuth() if (!user.artist) { throw new Error('Artist profile required') } return { user, artist: user.artist } } export function generateResetToken(): string { return crypto.randomUUID() } export async function createSession( userId: string, deviceInfo?: Record, ipAddress?: string, userAgent?: string ): Promise { const sessionToken = crypto.randomUUID() await prisma.session.create({ data: { userId, token: sessionToken, deviceInfo: deviceInfo ? JSON.stringify(deviceInfo) : undefined, ipAddress, userAgent, }, }) return sessionToken } export async function validateSession(sessionToken: string) { const session = await prisma.session.findUnique({ where: { token: sessionToken }, include: { user: { select: { id: true, email: true, username: true, displayName: true, avatarUrl: true, role: true, }, }, }, }) if (!session) { return null } // Update last activity await prisma.session.update({ where: { id: session.id }, data: { lastActivity: new Date() }, }) return session.user } export async function revokeSession(sessionToken: string) { await prisma.session.delete({ where: { token: sessionToken }, }) } export async function revokeAllSessions(userId: string) { await prisma.session.deleteMany({ where: { userId }, }) } export async function getUserSessions(userId: string) { return prisma.session.findMany({ where: { userId }, select: { id: true, deviceInfo: true, ipAddress: true, userAgent: true, lastActivity: true, createdAt: true, }, orderBy: { lastActivity: 'desc' }, }) } export async function createRefreshToken(userId: string): Promise { const token = generateRefreshToken() const expiresAt = new Date() expiresAt.setDate(expiresAt.getDate() + 7) // 7 days await prisma.refreshToken.create({ data: { token, userId, expiresAt, }, }) return token } export async function validateRefreshToken(token: string) { const refreshToken = await prisma.refreshToken.findFirst({ where: { token, isRevoked: false, expiresAt: { gt: new Date(), }, }, include: { user: { select: { id: true, email: true, username: true, role: true, }, }, }, }) if (!refreshToken) { return null } return refreshToken.user } export async function revokeRefreshToken(token: string) { await prisma.refreshToken.updateMany({ where: { token }, data: { isRevoked: true }, }) } export async function revokeAllRefreshTokens(userId: string) { await prisma.refreshToken.updateMany({ where: { userId }, data: { isRevoked: true }, }) } export function generateEmailToken(): string { return crypto.randomUUID() } export function slugify(text: string): string { return text .toLowerCase() .replace(/[^\w\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .trim() }