44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import { z } from 'zod';
|
|
import { db } from '../../utils/db';
|
|
import { createSession, hashPassword } from '../../utils/auth';
|
|
import { recordAudit } from '../../utils/audit';
|
|
import { sendEmail } from '../../utils/email';
|
|
|
|
const schema = z.object({
|
|
email: z.string().email().max(200),
|
|
password: z.string().min(8).max(200),
|
|
name: z.string().max(120).optional(),
|
|
});
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const parsed = schema.safeParse(await readBody(event).catch(() => ({})));
|
|
if (!parsed.success) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Invalid email or password (min 8 characters).' });
|
|
}
|
|
const email = parsed.data.email.toLowerCase();
|
|
if (await db.user.findUnique({ where: { email } })) {
|
|
throw createError({ statusCode: 409, statusMessage: 'An account with this email already exists.' });
|
|
}
|
|
|
|
const user = await db.user.create({
|
|
data: {
|
|
email,
|
|
name: parsed.data.name || null,
|
|
role: 'user',
|
|
passwordHash: await hashPassword(parsed.data.password),
|
|
subscription: { create: { plan: 'free', status: 'active', provider: 'mock' } },
|
|
},
|
|
});
|
|
|
|
await recordAudit({ actorId: user.id, action: 'user.register', target: user.email });
|
|
await sendEmail(event, {
|
|
to: user.email,
|
|
subject: 'Welcome',
|
|
html: '<p>Your account is ready. Sign in to get started.</p>',
|
|
}).catch((e) => console.error('[email] register', e));
|
|
|
|
await createSession(event, user.id);
|
|
setResponseStatus(event, 201);
|
|
return { user: { id: user.id, email: user.email, name: user.name, role: user.role } };
|
|
});
|