import { Handlers, PageProps } from 'fresh/server.ts'; import { generateHash, validateEmail } from '/lib/utils/misc.ts'; import { PASSWORD_SALT } from '/lib/auth.ts'; import { FormField, generateFieldHtml, getFormDataField } from '/lib/form-utils.tsx'; import { UserModel, VerificationCodeModel } from '/lib/models/user.ts'; import { sendVerifyEmailEmail } from '/lib/providers/brevo.ts'; import { AppConfig } from '/lib/config.ts'; import { FreshContextState } from '/lib/types.ts'; interface Data { error?: string; notice?: string; email?: string; formData?: FormData; helpEmail: string; } export const handler: Handlers = { async GET(request, context) { if (context.state.user) { return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); } const helpEmail = (await AppConfig.getConfig()).visuals.helpEmail; const searchParams = new URL(request.url).searchParams; let notice = ''; if (searchParams.get('success') === 'delete') { notice = `Your account and all its data has been deleted.`; } return await context.render({ notice, helpEmail }); }, async POST(request, context) { if (context.state.user) { return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); } const helpEmail = (await AppConfig.getConfig()).visuals.helpEmail; const formData = await request.clone().formData(); const email = getFormDataField(formData, 'email'); try { if (!(await AppConfig.isSignupAllowed())) { throw new Error(`Signups are not allowed.`); } if (!validateEmail(email)) { throw new Error(`Invalid email.`); } const password = getFormDataField(formData, 'password'); if (password.length < 6) { throw new Error(`Password is too short.`); } const existingUser = await UserModel.getByEmail(email); if (existingUser) { throw new Error('Email is already in use. Perhaps you want to login instead?'); } const hashedPassword = await generateHash(`${password}:${PASSWORD_SALT}`, 'SHA-256'); const user = await UserModel.create(email, hashedPassword); if (await AppConfig.isEmailVerificationEnabled()) { const verificationCode = await VerificationCodeModel.create(user, user.email, 'email'); await sendVerifyEmailEmail(user.email, verificationCode); } return new Response('Signup successful', { status: 303, headers: { 'location': `/login?success=signup&email=${encodeURIComponent(user.email)}` }, }); } catch (error) { console.error(error); return await context.render({ error: (error as Error).toString(), email, formData, helpEmail }); } }, }; function formFields(email?: string) { const fields: FormField[] = [ { name: 'email', label: 'Email', description: `The email that will be used to login. A code will be sent to it.`, type: 'email', placeholder: 'jane.doe@example.com', value: email || '', required: true, }, { name: 'password', label: 'Password', description: `The password that will be used to login.`, type: 'password', placeholder: 'super-SECRET-passphrase', required: true, }, ]; return fields; } export default function Signup({ data }: PageProps) { return (

Signup

{data?.error ? (

Failed to signup!

{data?.error}

) : null} {data?.notice ? (

Success!

{data?.notice}

) : null}
{formFields(data?.email).map((field) => generateFieldHtml(field, data?.formData || new FormData()))}

Already have an account?

If you already have an account,{' '} login .

{data?.helpEmail !== '' ? ( <>

Need help?

If you're having any issues or have any questions,{' '} please reach out .

) : null}
); }