project-standalo-sonic-cloud/lib/auth.ts

108 lines
2.2 KiB
TypeScript

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 SALT_ROUNDS = 10
export interface JWTPayload {
userId: string
email: string
role: string
}
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS)
}
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash)
}
export function generateToken(payload: JWTPayload): string {
return jwt.sign(payload, JWT_SECRET, { expiresIn: '7d' })
}
export function verifyToken(token: string): JWTPayload | null {
try {
return jwt.verify(token, JWT_SECRET) as JWTPayload
} 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 function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim()
}