@@ -9,8 +9,12 @@ import {
9
9
checkAuthorization ,
10
10
} from "../authorization.server" ;
11
11
import { logger } from "../logger.server" ;
12
+ import {
13
+ authenticateApiRequestWithPersonalAccessToken ,
14
+ PersonalAccessTokenAuthenticationResult ,
15
+ } from "../personalAccessToken.server" ;
12
16
13
- type RouteBuilderOptions <
17
+ type ApiKeyRouteBuilderOptions <
14
18
TParamsSchema extends z . AnyZodObject | undefined = undefined ,
15
19
TSearchParamsSchema extends z . AnyZodObject | undefined = undefined
16
20
> = {
@@ -30,7 +34,7 @@ type RouteBuilderOptions<
30
34
} ;
31
35
} ;
32
36
33
- type HandlerFunction <
37
+ type ApiKeyHandlerFunction <
34
38
TParamsSchema extends z . AnyZodObject | undefined ,
35
39
TSearchParamsSchema extends z . AnyZodObject | undefined
36
40
> = ( args : {
@@ -46,8 +50,8 @@ export function createLoaderApiRoute<
46
50
TParamsSchema extends z . AnyZodObject | undefined = undefined ,
47
51
TSearchParamsSchema extends z . AnyZodObject | undefined = undefined
48
52
> (
49
- options : RouteBuilderOptions < TParamsSchema , TSearchParamsSchema > ,
50
- handler : HandlerFunction < TParamsSchema , TSearchParamsSchema >
53
+ options : ApiKeyRouteBuilderOptions < TParamsSchema , TSearchParamsSchema > ,
54
+ handler : ApiKeyHandlerFunction < TParamsSchema , TSearchParamsSchema >
51
55
) {
52
56
return async function loader ( { request, params } : LoaderFunctionArgs ) {
53
57
const {
@@ -147,6 +151,110 @@ export function createLoaderApiRoute<
147
151
} ;
148
152
}
149
153
154
+ type PATRouteBuilderOptions <
155
+ TParamsSchema extends z . AnyZodObject | undefined = undefined ,
156
+ TSearchParamsSchema extends z . AnyZodObject | undefined = undefined
157
+ > = {
158
+ params ?: TParamsSchema ;
159
+ searchParams ?: TSearchParamsSchema ;
160
+ corsStrategy ?: "all" | "none" ;
161
+ } ;
162
+
163
+ type PATHandlerFunction <
164
+ TParamsSchema extends z . AnyZodObject | undefined ,
165
+ TSearchParamsSchema extends z . AnyZodObject | undefined
166
+ > = ( args : {
167
+ params : TParamsSchema extends z . AnyZodObject ? z . infer < TParamsSchema > : undefined ;
168
+ searchParams : TSearchParamsSchema extends z . AnyZodObject
169
+ ? z . infer < TSearchParamsSchema >
170
+ : undefined ;
171
+ authentication : PersonalAccessTokenAuthenticationResult ;
172
+ request : Request ;
173
+ } ) => Promise < Response > ;
174
+
175
+ export function createLoaderPATApiRoute <
176
+ TParamsSchema extends z . AnyZodObject | undefined = undefined ,
177
+ TSearchParamsSchema extends z . AnyZodObject | undefined = undefined
178
+ > (
179
+ options : PATRouteBuilderOptions < TParamsSchema , TSearchParamsSchema > ,
180
+ handler : PATHandlerFunction < TParamsSchema , TSearchParamsSchema >
181
+ ) {
182
+ return async function loader ( { request, params } : LoaderFunctionArgs ) {
183
+ const {
184
+ params : paramsSchema ,
185
+ searchParams : searchParamsSchema ,
186
+ corsStrategy = "none" ,
187
+ } = options ;
188
+
189
+ if ( corsStrategy !== "none" && request . method . toUpperCase ( ) === "OPTIONS" ) {
190
+ return apiCors ( request , json ( { } ) ) ;
191
+ }
192
+
193
+ const authenticationResult = await authenticateApiRequestWithPersonalAccessToken ( request ) ;
194
+
195
+ if ( ! authenticationResult ) {
196
+ return wrapResponse (
197
+ request ,
198
+ json ( { error : "Invalid or Missing API key" } , { status : 401 } ) ,
199
+ corsStrategy !== "none"
200
+ ) ;
201
+ }
202
+
203
+ let parsedParams : any = undefined ;
204
+ if ( paramsSchema ) {
205
+ const parsed = paramsSchema . safeParse ( params ) ;
206
+ if ( ! parsed . success ) {
207
+ return wrapResponse (
208
+ request ,
209
+ json (
210
+ { error : "Params Error" , details : fromZodError ( parsed . error ) . details } ,
211
+ { status : 400 }
212
+ ) ,
213
+ corsStrategy !== "none"
214
+ ) ;
215
+ }
216
+ parsedParams = parsed . data ;
217
+ }
218
+
219
+ let parsedSearchParams : any = undefined ;
220
+ if ( searchParamsSchema ) {
221
+ const searchParams = Object . fromEntries ( new URL ( request . url ) . searchParams ) ;
222
+ const parsed = searchParamsSchema . safeParse ( searchParams ) ;
223
+ if ( ! parsed . success ) {
224
+ return wrapResponse (
225
+ request ,
226
+ json (
227
+ { error : "Query Error" , details : fromZodError ( parsed . error ) . details } ,
228
+ { status : 400 }
229
+ ) ,
230
+ corsStrategy !== "none"
231
+ ) ;
232
+ }
233
+ parsedSearchParams = parsed . data ;
234
+ }
235
+
236
+ try {
237
+ const result = await handler ( {
238
+ params : parsedParams ,
239
+ searchParams : parsedSearchParams ,
240
+ authentication : authenticationResult ,
241
+ request,
242
+ } ) ;
243
+ return wrapResponse ( request , result , corsStrategy !== "none" ) ;
244
+ } catch ( error ) {
245
+ console . error ( "Error in API route:" , error ) ;
246
+ if ( error instanceof Response ) {
247
+ return wrapResponse ( request , error , corsStrategy !== "none" ) ;
248
+ }
249
+ return wrapResponse (
250
+ request ,
251
+ json ( { error : "Internal Server Error" } , { status : 500 } ) ,
252
+ corsStrategy !== "none"
253
+ ) ;
254
+ }
255
+ } ;
256
+ }
257
+
150
258
function wrapResponse ( request : Request , response : Response , useCors : boolean ) {
151
259
return useCors ? apiCors ( request , response ) : response ;
152
260
}
0 commit comments