@@ -3,14 +3,15 @@ import { SmartRouteHandler, SmartRouteHandlerOverloadMetadata, createSmartRouteH
3
3
import { SmartResponse } from "./smart-response" ;
4
4
import { KnownErrors } from "@stackframe/stack-shared" ;
5
5
import { prismaClient } from "@/prisma-client" ;
6
- import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors" ;
6
+ import { StackAssertionError , throwErr } from "@stackframe/stack-shared/dist/utils/errors" ;
7
7
import { validateRedirectUrl } from "@/lib/redirect-urls" ;
8
8
import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto" ;
9
9
import { adaptSchema , yupBoolean , yupNumber , yupObject , yupString } from "@stackframe/stack-shared/dist/schema-fields" ;
10
10
import { VerificationCodeType } from "@prisma/client" ;
11
11
import { SmartRequest } from "./smart-request" ;
12
12
import { DeepPartial } from "@stackframe/stack-shared/dist/utils/objects" ;
13
13
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects" ;
14
+ import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users" ;
14
15
15
16
type Method = {
16
17
email : string ,
@@ -30,11 +31,12 @@ type CodeObject = {
30
31
expiresAt : Date ,
31
32
} ;
32
33
33
- type VerificationCodeHandler < Data , SendCodeExtraOptions extends { } > = {
34
+ type VerificationCodeHandler < Data , SendCodeExtraOptions extends { } , HasDetails extends boolean > = {
34
35
createCode < CallbackUrl extends string | URL > ( options : CreateCodeOptions < Data , CallbackUrl > ) : Promise < CodeObject > ,
35
36
sendCode ( options : CreateCodeOptions < Data > , sendOptions : SendCodeExtraOptions ) : Promise < void > ,
36
37
postHandler : SmartRouteHandler < any , any , any > ,
37
38
checkHandler : SmartRouteHandler < any , any , any > ,
39
+ detailsHandler : HasDetails extends true ? SmartRouteHandler < any , any , any > : undefined ,
38
40
} ;
39
41
40
42
/**
@@ -44,42 +46,65 @@ export function createVerificationCodeHandler<
44
46
Data ,
45
47
RequestBody extends { } & DeepPartial < SmartRequest [ "body" ] > ,
46
48
Response extends SmartResponse ,
49
+ DetailsResponse extends SmartResponse | undefined ,
50
+ UserRequired extends boolean ,
47
51
SendCodeExtraOptions extends { } ,
48
52
> ( options : {
49
53
metadata ?: {
50
54
post ?: SmartRouteHandlerOverloadMetadata ,
51
55
check ?: SmartRouteHandlerOverloadMetadata ,
56
+ details ?: SmartRouteHandlerOverloadMetadata ,
52
57
} ,
53
58
type : VerificationCodeType ,
54
59
data : yup . Schema < Data > ,
55
60
requestBody ?: yup . ObjectSchema < RequestBody > ,
61
+ userRequired ?: UserRequired ,
62
+ detailsResponse ?: yup . Schema < DetailsResponse > ,
56
63
response : yup . Schema < Response > ,
57
- send : (
64
+ send (
58
65
codeObject : CodeObject ,
59
66
createOptions : CreateCodeOptions < Data > ,
60
67
sendOptions : SendCodeExtraOptions ,
61
- ) => Promise < void > ,
62
- handler ( project : ProjectsCrud [ "Admin" ] [ "Read" ] , method : Method , data : Data , body : RequestBody ) : Promise < Response > ,
63
- } ) : VerificationCodeHandler < Data , SendCodeExtraOptions > {
64
- const createHandler = ( verifyOnly : boolean ) => createSmartRouteHandler ( {
65
- metadata : verifyOnly ? options . metadata ?. check : options . metadata ?. post ,
68
+ ) : Promise < void > ,
69
+ handler (
70
+ project : ProjectsCrud [ "Admin" ] [ "Read" ] ,
71
+ method : Method ,
72
+ data : Data ,
73
+ body : RequestBody ,
74
+ user : UserRequired extends true ? UsersCrud [ "Admin" ] [ "Read" ] : undefined
75
+ ) : Promise < Response > ,
76
+ details ?: DetailsResponse extends SmartResponse ? ( (
77
+ project : ProjectsCrud [ "Admin" ] [ "Read" ] ,
78
+ method : Method ,
79
+ data : Data ,
80
+ body : RequestBody ,
81
+ user : UserRequired extends true ? UsersCrud [ "Admin" ] [ "Read" ] : undefined
82
+ ) => Promise < DetailsResponse > ) : undefined ,
83
+ } ) : VerificationCodeHandler < Data , SendCodeExtraOptions , DetailsResponse extends SmartResponse ? true : false > {
84
+ const createHandler = ( type : 'post' | 'check' | 'details' ) => createSmartRouteHandler ( {
85
+ metadata : options . metadata ?. [ type ] ,
66
86
request : yupObject ( {
67
87
auth : yupObject ( {
68
88
project : adaptSchema . required ( ) ,
89
+ user : options . userRequired ? adaptSchema . required ( ) : adaptSchema ,
69
90
} ) . required ( ) ,
70
91
body : yupObject ( {
71
92
code : yupString ( ) . required ( ) ,
72
93
// we cast to undefined as a typehack because the types are a bit icky
73
94
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
74
- } ) . concat ( ( verifyOnly ? undefined : options . requestBody ) as undefined ?? yupObject ( { } ) ) . required ( ) ,
95
+ } ) . concat ( ( type === 'post' ? options . requestBody : undefined ) as undefined ?? yupObject ( { } ) ) . required ( ) ,
75
96
} ) ,
76
- response : verifyOnly ? yupObject ( {
77
- statusCode : yupNumber ( ) . oneOf ( [ 200 ] ) . required ( ) ,
78
- bodyType : yupString ( ) . oneOf ( [ "json" ] ) . required ( ) ,
79
- body : yupObject ( {
80
- "is_code_valid" : yupBoolean ( ) . oneOf ( [ true ] ) . required ( ) ,
81
- } ) . required ( ) ,
82
- } ) . required ( ) as yup . ObjectSchema < any > : options . response ,
97
+ response : type === 'check' ?
98
+ yupObject ( {
99
+ statusCode : yupNumber ( ) . oneOf ( [ 200 ] ) . required ( ) ,
100
+ bodyType : yupString ( ) . oneOf ( [ "json" ] ) . required ( ) ,
101
+ body : yupObject ( {
102
+ "is_code_valid" : yupBoolean ( ) . oneOf ( [ true ] ) . required ( ) ,
103
+ } ) . required ( ) ,
104
+ } ) . required ( ) as yup . ObjectSchema < any > :
105
+ type === 'details' ?
106
+ options . detailsResponse || throwErr ( 'detailsResponse is required' ) :
107
+ options . response ,
83
108
async handler ( { body : { code, ...requestBody } , auth } ) {
84
109
const verificationCode = await prismaClient . verificationCode . findUnique ( {
85
110
where : {
@@ -98,28 +123,34 @@ export function createVerificationCodeHandler<
98
123
strict : true ,
99
124
} ) ;
100
125
101
- if ( verifyOnly ) {
102
- return {
103
- statusCode : 200 ,
104
- bodyType : "json" ,
105
- body : {
106
- is_code_valid : true ,
107
- } ,
108
- } ;
109
- } else {
110
- await prismaClient . verificationCode . update ( {
111
- where : {
112
- projectId_code : {
113
- projectId : auth . project . id ,
114
- code,
126
+ switch ( type ) {
127
+ case 'post' : {
128
+ await prismaClient . verificationCode . update ( {
129
+ where : {
130
+ projectId_code : {
131
+ projectId : auth . project . id ,
132
+ code,
133
+ } ,
115
134
} ,
116
- } ,
117
- data : {
118
- usedAt : new Date ( ) ,
119
- } ,
120
- } ) ;
135
+ data : {
136
+ usedAt : new Date ( ) ,
137
+ } ,
138
+ } ) ;
121
139
122
- return await options . handler ( auth . project , { email : verificationCode . email } , validatedData as any , requestBody as any ) ;
140
+ return await options . handler ( auth . project , { email : verificationCode . email } , validatedData as any , requestBody as any , auth . user as any ) ;
141
+ }
142
+ case 'check' : {
143
+ return {
144
+ statusCode : 200 ,
145
+ bodyType : "json" ,
146
+ body : {
147
+ is_code_valid : true ,
148
+ } ,
149
+ } ;
150
+ }
151
+ case 'details' : {
152
+ return await options . details ?.( auth . project , { email : verificationCode . email } , validatedData as any , requestBody as any , auth . user as any ) as any ;
153
+ }
123
154
}
124
155
} ,
125
156
} ) ;
@@ -166,7 +197,8 @@ export function createVerificationCodeHandler<
166
197
const codeObj = await this . createCode ( createOptions ) ;
167
198
await options . send ( codeObj , createOptions , sendOptions ) ;
168
199
} ,
169
- postHandler : createHandler ( false ) ,
170
- checkHandler : createHandler ( true ) ,
200
+ postHandler : createHandler ( 'post' ) ,
201
+ checkHandler : createHandler ( 'check' ) ,
202
+ detailsHandler : ( options . detailsResponse ? createHandler ( 'details' ) : undefined ) as any ,
171
203
} ;
172
- }
204
+ }
0 commit comments