1- import { generateEmailVerificationToken } from '$lib/drizzle/postgres/models/tokens' ;
2- import { createUser } from '$lib/drizzle/postgres/models/users' ;
3- import { sendEmail } from '$lib/emails/send' ;
1+ import { createUser , getUserByEmail } from '$lib/drizzle/postgres/models/users' ;
42import { getFeedbackObjects } from '$lib/utils/utils' ;
5- import { fail , redirect } from '@sveltejs/kit' ;
3+ import { fail } from '@sveltejs/kit' ;
64import { nanoid } from 'nanoid' ;
75import { z } from 'zod' ;
86import type { Actions } from './$types' ;
97import { Argon2id } from 'oslo/password' ;
10- import type { InsertUser , InsertUserKey , InsertUserProfile } from '$lib/drizzle/postgres/db.model' ;
11- import { lucia } from '$lib/lucia/postgres' ;
12-
8+ import type { InsertUser , InsertUserKey , InsertUserProfile , SelectEmployee } from '$lib/drizzle/postgres/db.model' ;
9+ import { AuthUtils } from '$lib/utils/auth' ;
10+ import { dev } from '$app/environment' ;
11+ import { getEmployeeByEmail } from '$lib/drizzle/postgres/models/employees' ;
1312
1413const signupUserSchema = z . object ( {
15- firstName : z . string ( ) . optional ( ) ,
16- lastName : z . string ( ) . optional ( ) ,
17- email : z . string ( ) . email ( ) ,
18- password : z . string ( ) . min ( 1 )
14+ email : z . string ( ) . email ( )
1915} ) ;
2016
2117export const actions : Actions = {
22- signupUser : async ( { locals , request, url, cookies } ) => {
18+ signupUser : async ( { request, url } ) => {
2319 const formData = Object . fromEntries ( await request . formData ( ) ) ;
2420 const signupUser = signupUserSchema . safeParse ( formData ) ;
2521
@@ -40,85 +36,54 @@ export const actions: Actions = {
4036 } ) ;
4137 }
4238
43- const { firstName , lastName , email, password : inputPassword } = signupUser . data ;
39+ const { email } = signupUser . data ;
4440
45- try {
46- const insertUser = {
47- id : nanoid ( ) ,
48- email,
49- } as InsertUser ;
50-
51- const hashedPassword = await new Argon2id ( ) . hash ( inputPassword ) ;
52-
53- const insertUserKey = {
54- id : nanoid ( ) ,
55- userId : insertUser . id ,
56- hashedPassword,
57- } as InsertUserKey ;
41+ try {
42+ // Check if user exists
43+ const existingEmployee = await getEmployeeByEmail ( email ) ;
5844
59- const insertUserProfile = {
60- id : nanoid ( ) ,
61- userId : insertUser . id ,
62- firstName,
63- lastName,
64- clientId : 'default' ,
65- role : 'user' ,
66- } as InsertUserProfile ;
45+ if ( existingEmployee ) {
46+ let userId : string ;
47+ const user = await getUserByEmail ( email ) ;
6748
68- const result = await createUser ( insertUser , insertUserKey , insertUserProfile ) ;
69-
70- if ( ! result . success ) {
71- return fail ( 500 , {
72- feedbacks : [
49+ if ( ! user ) {
50+ userId = await createNewUserFromEmployee ( email , existingEmployee ) ;
51+ } else {
52+ userId = user . id ;
53+ }
54+
55+ // User exists, send password reset
56+ const failure = await AuthUtils . sendPasswordResetLink ( url . origin , email , userId ) ;
57+
58+ if ( failure ) {
59+ throw new Error ( failure . data . feedbacks [ 0 ] . message ) ;
60+ }
61+
62+ return {
63+ feedbacks : getFeedbackObjects ( [
7364 {
74- type : 'error ' ,
75- title : 'Error creating user ' ,
76- message : 'An error occurred while creating your account. Please try again .'
65+ type : 'success ' ,
66+ title : 'Password Reset Link Sent ' ,
67+ message : 'If an account exists with this email, you will receive a password reset link .'
7768 }
78- ]
79- } ) ;
69+ ] )
70+ } ;
8071 }
8172
82- const session = await lucia . createSession ( insertUser . id , { } ) ;
83- const sessionCookie = lucia . createSessionCookie ( session . id ) ;
84-
85- // Set session cookie
86- cookies . set ( sessionCookie . name , sessionCookie . value , {
87- path : '.' ,
88- ...sessionCookie . attributes ,
89- } ) ;
90-
91- // Send verification email
92- const verificationToken = await generateEmailVerificationToken ( insertUser . id ) ;
93-
94- const sender = 'Stacks <drew@verostack.dev>' ;
95- const recipient = firstName ? `${ firstName } ` : email ;
96- const emailHtml = `Hello ${ recipient } ,
97- <br><br>
98- Thank you for signing up to Stacks! Please click the link below to verify your email address:
99- <br><br>
100- <a href="${ url . origin } /app/email-verification/${ verificationToken } ">Verify Email Address</a>
101- <br>
102- You can also copy directly into your browser:
103- <br><br>
104- <code>${ url . origin } /app/email-verification/${ verificationToken } </code>
105- <br><br>
106- Thanks,
107- <br>
108- Drew from Stacks` ;
109-
110- const signupEmail = await sendEmail ( {
111- from : sender ,
112- to : email ,
113- subject : 'Verify Your Email Address' ,
114- html : emailHtml
115- } ) ;
116-
117- if ( signupEmail [ 0 ] . type === 'error' ) {
118- return fail ( 500 , {
119- feedbacks : signupEmail
120- } ) ;
73+ if ( dev ) {
74+ console . log ( 'User does not exist, sending email verification link' ) ;
12175 }
76+
77+ // If user doesn't exist, return same message to avoid email enumeration
78+ return {
79+ feedbacks : getFeedbackObjects ( [
80+ {
81+ type : 'success' ,
82+ title : 'Password Reset Link Sent' ,
83+ message : 'If an account exists with this email, you will receive a password reset link.'
84+ }
85+ ] )
86+ } ;
12287 } catch ( e ) {
12388 const feedbacks = getFeedbackObjects ( [
12489 {
@@ -132,7 +97,34 @@ export const actions: Actions = {
13297 feedbacks
13398 } ) ;
13499 }
135-
136- redirect ( 302 , '/app/email-verification' ) ;
137100 }
138101} ;
102+
103+ const createNewUserFromEmployee = async ( email : string , existingEmployee : SelectEmployee ) => {
104+ // Create user record for existing employee
105+ const userId = nanoid ( ) ;
106+ const newUser : InsertUser = {
107+ id : userId ,
108+ email : email ,
109+ emailVerified : false
110+ } ;
111+
112+ const newUserKey : InsertUserKey = {
113+ id : `email:${ email } ` ,
114+ userId : userId ,
115+ hashedPassword : await new Argon2id ( ) . hash ( nanoid ( ) )
116+ } ;
117+
118+ const newUserProfile : InsertUserProfile = {
119+ id : nanoid ( ) ,
120+ userId : userId ,
121+ clientId : existingEmployee . clientId ,
122+ role : 'user' ,
123+ firstName : existingEmployee . firstName ,
124+ lastName : existingEmployee . lastName
125+ } ;
126+
127+ await createUser ( newUser , newUserKey , newUserProfile ) ;
128+
129+ return userId ;
130+ }
0 commit comments