Register auth flow

This commit is contained in:
Jorge Vargas 2025-02-11 11:32:11 -06:00
parent 2db1dd640d
commit 161c44924e
6 changed files with 293 additions and 81 deletions

View file

@ -5,6 +5,7 @@ import { username } from 'better-auth/plugins'
import prismaClient from './utils/prisma-client'
import { sendEmail } from 'utils/email'
import forgorTemplate from 'utils/forgorTemplate'
import verifyTemplate from 'utils/verifyTemplate'
export const auth = betterAuth({
database: prismaAdapter(prismaClient, { provider: 'mysql' }),
@ -12,7 +13,10 @@ export const auth = betterAuth({
plugins: [username()],
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: true
autoSignInAfterVerification: true,
async sendVerificationEmail({ user, url }) {
await sendEmail(user.email, 'Verify your email address', verifyTemplate.replaceAll('{{verify_link}}', url))
}
},
emailAndPassword: {
enabled: true,

View file

@ -0,0 +1,15 @@
import type { ComponentProps, PropsWithChildren } from 'react'
import clsx from 'clsx'
export default function Input(props: PropsWithChildren<ComponentProps<'input'>>) {
const { name, className, children, ...attrs } = props
return (
<div className='flex flex-col'>
<label htmlFor={name} className='font-medium text-black'>
{children}:
</label>
<input {...attrs} name={name} className={clsx('bg-zinc-200 rounded p-2 mt-2 mb-3 text-black', className)} />
</div>
)
}

View file

@ -38,10 +38,13 @@ function LoginForm(props: { setForm: SetState<FormOptions>; setModalOpen: SetSta
const variables = Object.fromEntries(formData)
setLoading(true)
const { error } = await signIn.username({
username: variables.username as string,
password: variables.password as string
})
const { error } = await signIn.username(
{
username: variables.username as string,
password: variables.password as string
},
{}
)
setLoading(false)
if (error) {

View file

@ -1,26 +1,39 @@
import { useState } from 'react'
// import toast from 'react-hot-toast'
import { useState, type FormEvent } from 'react'
import toast from 'react-hot-toast'
import * as m from 'paraglide/messages.js'
import Button from 'components/Button'
import Modal from 'components/Modal'
import Input from 'components/form/Input'
import { signUp } from 'utils/auth-client'
export default function RegisterBtn() {
const [modalOpen, setModalOpen] = useState(false)
const [loading, setLoading] = useState(false)
const handleSubmit = (ev) => {
async function handleSubmit(ev: FormEvent<HTMLFormElement>) {
ev.preventDefault()
const formData = new FormData(ev.target)
const variables = Object.fromEntries(formData)
const formData = new FormData(ev.currentTarget)
const variables = Object.fromEntries(formData) as {
username: string
name: string
password: string
email: string
}
/* mutate({ variables })
.then(() => {
toast.success(m.emailSuccess())
setLoading(true)
await signUp.email(variables, {
onSuccess: () => {
toast.success(m.emailSuccess(), { duration: Infinity })
setLoading(false)
setModalOpen(false)
})
.catch((err) => {
toast.error(err.message)
}) */
},
onError: (ctx) => {
console.log(ctx.error)
toast.error(ctx.error.message)
setLoading(false)
}
})
}
return (
@ -31,39 +44,27 @@ export default function RegisterBtn() {
{modalOpen ? (
<Modal setOpen={setModalOpen}>
<form onSubmit={handleSubmit}>
<div className='bg-white px-4 pt-5 pb-4 gap-x-4 flex flex-col'>
<div className='px-4 pt-5 pb-4 gap-x-4 gap-y-1 flex flex-col'>
<div className='flex gap-x-4'>
<div className='flex flex-col'>
<label htmlFor='username' className='font-medium text-black'>
{m.username()}:
</label>
<input type='text' name='username' className='bg-zinc-200 rounded p-2 mt-2 mb-3 text-black' />
</div>
<div className='flex flex-col'>
<label htmlFor='email' className='font-medium text-black'>
{m.email()}:
</label>
<input type='text' name='email' className='bg-zinc-200 rounded p-2 mt-2 mb-3 text-black' />
</div>
<Input name='username' required>
{m.username()}
</Input>
<Input name='name' required>
{m.displayName()}
</Input>
</div>
<div className='flex flex-col'>
<label htmlFor='pfp' className='font-medium text-black'>
{m.profilePic()}:
</label>
<input
type='file'
name='pfp'
className='bg-zinc-200 rounded p-2 mt-2 mb-3 text-black'
accept='image/*'
/>
<Input name='email' type='email' required>
{m.email()}
</Input>
<Input name='password' type='password' required>
{m.password()}
</Input>
<div className='mx-auto'>
<Button type='submit' loading={loading} disabled={loading}>
{m.register()}
</Button>
</div>
</div>
<div className='bg-zinc-200 px-4 py-3 text-right gap-x-2 flex justify-end'>
<Button className='bg-zinc-500 hover:bg-zinc-600'>{m.close()}</Button>
{/* <Button loading={loading} disabled={loading}>
{m.register()}
</Button> */}
</div>
</form>
</Modal>
) : null}

188
src/utils/verifyTemplate.ts Normal file
View file

@ -0,0 +1,188 @@
const verifyTemplate = `<!DOCTYPE html>
<html lang='en' xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:v='urn:schemas-microsoft-com:vml'>
<head>
<title></title>
<meta charset='utf-8' />
<meta content='width=device-width, initial-scale=1.0' name='viewport' />
<!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch><o:AllowPNG/></o:OfficeDocumentSettings></xml><![endif]-->
<!--[if !mso]><!-->
<link href='https://fonts.googleapis.com/css?family=Abril+Fatface' rel='stylesheet' type='text/css' />
<link href='https://fonts.googleapis.com/css?family=Alegreya' rel='stylesheet' type='text/css' />
<link href='https://fonts.googleapis.com/css?family=Arvo' rel='stylesheet' type='text/css' />
<link href='https://fonts.googleapis.com/css?family=Bitter' rel='stylesheet' type='text/css' />
<link href='https://fonts.googleapis.com/css?family=Cabin' rel='stylesheet' type='text/css' />
<link href='https://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet' type='text/css' />
<!--<![endif]-->
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
background-color: #f5f5f5;
}
body {
margin: 0;
padding: 0;
}
th.column {
padding: 0
}
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: inherit !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
}
p {
line-height: inherit
}
@media (max-width:520px) {
.icons-inner {
text-align: center;
}
.icons-inner td {
margin: 0 auto;
}
.row-content {
width: 100% !important;
}
.image_block img.big {
width: auto !important;
}
.stack .column {
width: 100%;
display: block;
}
}
</style>
</head>
<body style='background-color: #FFFFFF; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;'>
<table border='0' cellpadding='0' cellspacing='0' class='nl-container' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #FFFFFF;' width='100%'>
<tbody>
<tr>
<td>
<table align='center' border='0' cellpadding='0' cellspacing='0' class='row row-3'
role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f5f5f5;' width='100%'>
<tbody>
<tr>
<td>
<table align='center' border='0' cellpadding='0' cellspacing='0'
class='row-content stack' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #ffffff; color: #000000;'
width='500'>
<tbody>
<tr>
<th class='column'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 5px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;'
width='100%'>
<table border='0' cellpadding='0' cellspacing='0'
class='image_block' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt;'
width='100%'>
<tr>
<td
style='padding-bottom:5px;padding-left:5px;padding-right:5px;width:100%;'>
<div align='center' style='line-height:10px'><img
alt='soc-logo' src='https://sittingonclouds.net/img/assets/clouds.png'
style='display: block; height: auto; border: 0; width: 175px; max-width: 100%;'
title='soc-logo' width='175' /></div>
</td>
</tr>
</table>
<table border='0' cellpadding='15' cellspacing='0'
class='button_block' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt;'
width='100%'>
<tr>
<td>
<div align='center'>
<!--[if mso]><v:roundrect xmlns:v='urn:schemas-microsoft-com:vml' xmlns:w='urn:schemas-microsoft-com:office:word' href='{{verify_link}}' style='height:58px;width:272px;v-text-anchor:middle;' arcsize='35%' strokeweight='0.75pt' strokecolor='#FFC727' fillcolor='#e38e36'><w:anchorlock/><v:textbox inset='0px,0px,0px,0px'><center style='color:#393d47; font-family:Tahoma, Verdana, sans-serif; font-size:18px'><![endif]--><a
href='{{verify_link}}'
style='text-decoration:none;display:inline-block;color:#393d47;background-color:#ff7b24;border-radius:20px;width:auto;padding-top:10px;padding-bottom:10px;font-family:Tahoma, Verdana, Segoe, sans-serif;text-align:center;mso-border-alt:none;word-break:keep-all;'
target='_blank'><span
style='padding-left:50px;padding-right:50px;font-size:18px;display:inline-block;letter-spacing:normal;'><span
style='font-size: 16px; line-height: 2; word-break: break-word; mso-line-height-alt: 32px;'><span
data-mce-style='font-size: 18px; line-height: 36px;'
style='font-size: 18px; line-height: 36px;'><strong>Verify your email address</strong></span></span></span></a>
<!--[if mso]></center></v:textbox></v:roundrect><![endif]-->
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table align='center' border='0' cellpadding='0' cellspacing='0' class='row row-5'
role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f5f5f5;' width='100%'>
<tbody>
<tr>
<td>
<table align='center' border='0' cellpadding='0' cellspacing='0'
class='row-content stack' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #000000;'
width='500'>
<tbody>
<tr>
<th class='column'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 5px; padding-bottom: 5px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;'
width='100%'>
<table border='0' cellpadding='15' cellspacing='0'
class='text_block' role='presentation'
style='mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;'
width='100%'>
<tr>
<td>
<div style='font-family: Tahoma, Verdana, sans-serif'>
<div
style='font-size: 12px; font-family: Tahoma, Verdana, Segoe, sans-serif; mso-line-height-alt: 14.399999999999999px; color: #393d47; line-height: 1.2;'>
<p
style='margin: 0; font-size: 14px; text-align: center;'>
<span style='font-size:10px;'>This link will
expire in 24 hours</span></p>
</div>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table><!-- End -->
</body>
</html>`
export default verifyTemplate