feat: implement user registration functionality
- Created registerUser server action with email/password validation - Implemented password hashing with bcrypt (10 rounds) - Added email uniqueness check to prevent duplicate registrations - Created session after successful registration using NextAuth - Added comprehensive validation for email format and password length (min 8 chars) - Added email and password validation utilities to lib/validation.ts - Removed PrismaAdapter from auth.ts (using JWT sessions only) - Added vitest testing framework and configuration - Created comprehensive unit tests for registration functionality - All tests passing (6/6) Requirements covered: 1.1, 1.2, 1.3, 1.4, 1.5
This commit is contained in:
83
lib/auth-actions.ts
Normal file
83
lib/auth-actions.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
'use server'
|
||||
|
||||
import { prisma } from './db'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { z } from 'zod'
|
||||
import { signIn } from '@/auth'
|
||||
|
||||
// Validation schemas
|
||||
const registerSchema = z.object({
|
||||
email: z.string().email({ message: 'Invalid email address' }),
|
||||
password: z.string().min(8, { message: 'Password must be at least 8 characters' }),
|
||||
name: z.string().optional(),
|
||||
})
|
||||
|
||||
export type RegisterResult =
|
||||
| { success: true; userId: string }
|
||||
| { success: false; error: string }
|
||||
|
||||
/**
|
||||
* Register a new user with email and password
|
||||
* Requirements: 1.1, 1.2, 1.3, 1.4, 1.5
|
||||
*/
|
||||
export async function registerUser(
|
||||
email: string,
|
||||
password: string,
|
||||
name?: string
|
||||
): Promise<RegisterResult> {
|
||||
try {
|
||||
// Validate input (Requirement 1.5 - password length validation)
|
||||
const validation = registerSchema.safeParse({ email, password, name })
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues.map((issue) => issue.message).join(', ')
|
||||
return { success: false, error: errors }
|
||||
}
|
||||
|
||||
const { email: validEmail, password: validPassword, name: validName } = validation.data
|
||||
|
||||
// Check if email already exists (Requirement 1.2 - duplicate email rejection)
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: { email: validEmail },
|
||||
})
|
||||
|
||||
if (existingUser) {
|
||||
return { success: false, error: 'Email already registered' }
|
||||
}
|
||||
|
||||
// Hash password (Requirement 1.3 - password hashing)
|
||||
const passwordHash = await bcrypt.hash(validPassword, 10)
|
||||
|
||||
// Create user (Requirement 1.1 - create user with unique identifier)
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: validEmail,
|
||||
passwordHash,
|
||||
name: validName || null,
|
||||
},
|
||||
})
|
||||
|
||||
// Create session after successful registration (Requirement 1.4)
|
||||
try {
|
||||
await signIn('credentials', {
|
||||
email: validEmail,
|
||||
password: validPassword,
|
||||
redirect: false,
|
||||
})
|
||||
} catch (error) {
|
||||
// If session creation fails, still return success since user was created
|
||||
console.error('Session creation failed after registration:', error)
|
||||
}
|
||||
|
||||
return { success: true, userId: user.id }
|
||||
} catch (error) {
|
||||
console.error('Registration error:', error)
|
||||
|
||||
// Handle unique constraint violation (shouldn't happen due to check above, but just in case)
|
||||
if (error instanceof Error && error.message.includes('Unique constraint')) {
|
||||
return { success: false, error: 'Email already registered' }
|
||||
}
|
||||
|
||||
return { success: false, error: 'Registration failed. Please try again.' }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user