feat(01-03): add validation schemas and server actions
- Add Zod validation schemas for auth operations - Add server actions for register, login, reset, update password - Add clsx and tailwind-merge for class utilities - Password validation: 8+ chars, 1 number, 1 uppercase - Error messages in Italian per user requirement - Specific error messages (not generic 'invalid credentials') Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
164
src/app/actions/auth.ts
Normal file
164
src/app/actions/auth.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
'use server'
|
||||
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { registerSchema, loginSchema, resetPasswordSchema, updatePasswordSchema } from '@/lib/schemas/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export type ActionState = {
|
||||
error?: string
|
||||
fieldErrors?: Record<string, string[]>
|
||||
success?: boolean
|
||||
message?: string
|
||||
}
|
||||
|
||||
export async function registerUser(
|
||||
prevState: ActionState,
|
||||
formData: FormData
|
||||
): Promise<ActionState> {
|
||||
const supabase = await createClient()
|
||||
|
||||
const parsed = registerSchema.safeParse({
|
||||
email: formData.get('email'),
|
||||
password: formData.get('password'),
|
||||
confirmPassword: formData.get('confirmPassword'),
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: parsed.data.email,
|
||||
password: parsed.data.password,
|
||||
options: {
|
||||
emailRedirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`,
|
||||
}
|
||||
})
|
||||
|
||||
if (error) {
|
||||
// SPECIFIC error messages per user requirement
|
||||
if (error.message.includes('already registered')) {
|
||||
return { error: 'Questa email e gia registrata' }
|
||||
}
|
||||
if (error.message.includes('invalid')) {
|
||||
return { error: 'Email non valida' }
|
||||
}
|
||||
return { error: error.message }
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Registrazione completata! Controlla la tua email per confermare l\'account.'
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginUser(
|
||||
prevState: ActionState,
|
||||
formData: FormData
|
||||
): Promise<ActionState> {
|
||||
const supabase = await createClient()
|
||||
|
||||
const parsed = loginSchema.safeParse({
|
||||
email: formData.get('email'),
|
||||
password: formData.get('password'),
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email: parsed.data.email,
|
||||
password: parsed.data.password,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
// SPECIFIC error messages per user requirement
|
||||
if (error.message.includes('Invalid login credentials')) {
|
||||
return { error: 'Email o password errata' }
|
||||
}
|
||||
if (error.message.includes('Email not confirmed')) {
|
||||
return { error: 'Devi confermare la tua email prima di accedere' }
|
||||
}
|
||||
return { error: error.message }
|
||||
}
|
||||
|
||||
revalidatePath('/', 'layout')
|
||||
redirect('/dashboard')
|
||||
}
|
||||
|
||||
export async function resetPassword(
|
||||
prevState: ActionState,
|
||||
formData: FormData
|
||||
): Promise<ActionState> {
|
||||
const supabase = await createClient()
|
||||
|
||||
const parsed = resetPasswordSchema.safeParse({
|
||||
email: formData.get('email'),
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { error } = await supabase.auth.resetPasswordForEmail(
|
||||
parsed.data.email,
|
||||
{
|
||||
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback?next=/update-password`,
|
||||
}
|
||||
)
|
||||
|
||||
if (error) {
|
||||
return { error: error.message }
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Se l\'email esiste, riceverai un link per reimpostare la password.'
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePassword(
|
||||
prevState: ActionState,
|
||||
formData: FormData
|
||||
): Promise<ActionState> {
|
||||
const supabase = await createClient()
|
||||
|
||||
const parsed = updatePasswordSchema.safeParse({
|
||||
password: formData.get('password'),
|
||||
confirmPassword: formData.get('confirmPassword'),
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
||||
}
|
||||
}
|
||||
|
||||
const { error } = await supabase.auth.updateUser({
|
||||
password: parsed.data.password,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
return { error: error.message }
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Password aggiornata con successo!'
|
||||
}
|
||||
}
|
||||
|
||||
export async function signOut() {
|
||||
const supabase = await createClient()
|
||||
await supabase.auth.signOut()
|
||||
revalidatePath('/', 'layout')
|
||||
redirect('/login')
|
||||
}
|
||||
Reference in New Issue
Block a user