1-
2- import { APIGatewayEvent , APIGatewayProxyEvent , APIGatewayProxyEventV2 } from 'aws-lambda' ;
3- import { CustomError } from './custom-error' ;
4- import { RouteConfig , ConfigRouteEntry , RouteArguments , RouteModule } from './types-and-interfaces' ;
5- import { authorizeRoute } from './authorization-helper' ;
6-
7- const getRouteConfigEntry = ( config : RouteConfig , method : string , path : string ) =>
8- config . routes . find ( r => r . path . toLowerCase ( ) === path . toLowerCase ( ) && r . method . toLowerCase ( ) === method . toLowerCase ( ) ) as ConfigRouteEntry ;
9-
10- const shouldAuthorizeRoute = ( routesConfig : RouteConfig , routeConfigEntry : ConfigRouteEntry ) =>
11- ( routesConfig . authorizeAllRoutes && routeConfigEntry . authorizeRoute !== false )
12- ||
1+ import {
2+ APIGatewayEvent ,
3+ APIGatewayProxyEvent ,
4+ APIGatewayProxyEventV2 ,
5+ } from "aws-lambda" ;
6+ import { CustomError } from "./custom-error" ;
7+ import {
8+ RouteConfig ,
9+ ConfigRouteEntry ,
10+ RouteArguments ,
11+ RouteModule ,
12+ } from "./types-and-interfaces" ;
13+ import { authorizeRoute } from "./authorization-helper" ;
14+
15+ const getRouteConfigEntry = (
16+ config : RouteConfig ,
17+ method : string ,
18+ path : string
19+ ) =>
20+ config . routes . find (
21+ ( r ) =>
22+ r . path . toLowerCase ( ) === path . toLowerCase ( ) &&
23+ r . method . toLowerCase ( ) === method . toLowerCase ( )
24+ ) as ConfigRouteEntry ;
25+
26+ const shouldAuthorizeRoute = (
27+ routesConfig : RouteConfig ,
28+ routeConfigEntry : ConfigRouteEntry
29+ ) =>
30+ ( routesConfig . authorizeAllRoutes &&
31+ routeConfigEntry . authorizeRoute !== false ) ||
1332 routeConfigEntry . authorizeRoute === true ;
1433
15-
16- export const getRouteModule = ( config : RouteConfig , method : string , path : string , availableRouteModules : { [ key : string ] : any } ) : RouteModule => {
34+ export const getRouteModule = (
35+ config : RouteConfig ,
36+ method : string ,
37+ path : string ,
38+ availableRouteModules : { [ key : string ] : any }
39+ ) : RouteModule => {
1740 const routeEntry = getRouteConfigEntry ( config , method , path ) ;
1841 let routeModule = null ;
1942 console . log ( `route entry: ${ JSON . stringify ( routeEntry ) } ` ) ;
2043 if ( routeEntry ) {
21- const matchingRouteModuleMapKey = Object . keys ( availableRouteModules ) . find ( ( k : string ) => routeEntry . handlerPath . endsWith ( k ) ) ;
44+ const matchingRouteModuleMapKey = Object . keys ( availableRouteModules ) . find (
45+ ( k : string ) => routeEntry . handlerPath . endsWith ( k )
46+ ) ;
2247 // routeModule = availableRouteModules[routeEntry.handlerPath.split('/').reverse()[0]];
2348 routeModule = availableRouteModules [ matchingRouteModuleMapKey ! ] ;
2449 }
@@ -33,7 +58,10 @@ interface RouteEvent {
3358 isBase64Encoded : boolean ;
3459}
3560
36- export const getRouteModuleResult = async ( { routeChain } : RouteModule , incoming : RouteArguments ) : Promise < any > => {
61+ export const getRouteModuleResult = async (
62+ { routeChain } : RouteModule ,
63+ incoming : RouteArguments
64+ ) : Promise < any > => {
3765 let returnValue = incoming ;
3866 for ( const chainFn of routeChain ) {
3967 returnValue = await chainFn ( returnValue ) ;
@@ -44,38 +72,47 @@ export const getRouteModuleResult = async ({ routeChain }: RouteModule, incoming
4472function pathToRegex ( path : string ) : string {
4573 // Convert route path to regex pattern
4674 return path
47- . replace ( / \/ / g, ' \\/' ) // Escape forward slashes
48- . replace ( / { ( [ ^ } ] + ) } / g, ' (?<$1>[^/]+)' ) ; // Convert {param} to named capture groups
75+ . replace ( / \/ / g, " \\/" ) // Escape forward slashes
76+ . replace ( / { ( [ ^ } ] + ) } / g, " (?<$1>[^/]+)" ) ; // Convert {param} to named capture groups
4977}
5078
5179const v2ApiGatewayEvent = ( event : APIGatewayProxyEventV2 ) : RouteEvent => {
5280 return {
5381 routeKey : event . routeKey ,
54- queryStringParameters : event . queryStringParameters ?? ( { } as RouteEvent [ 'queryStringParameters' ] ) ,
82+ queryStringParameters :
83+ event . queryStringParameters ??
84+ ( { } as RouteEvent [ "queryStringParameters" ] ) ,
5585 pathParameters : event . pathParameters ?? { } ,
5686 body : event . body ,
5787 isBase64Encoded : event . isBase64Encoded ,
5888 } ;
59- }
89+ } ;
6090
61- const v1ApiGatewayEvent = ( event : APIGatewayProxyEvent , config : RouteConfig ) : RouteEvent => {
62- const routeConfig = getRouteConfigByPath ( event . path , event . httpMethod , config . routes ) ;
91+ const v1ApiGatewayEvent = (
92+ event : APIGatewayProxyEvent ,
93+ config : RouteConfig
94+ ) : RouteEvent => {
95+ const routeConfig = getRouteConfigByPath (
96+ event . path ,
97+ event . httpMethod ,
98+ config . routes
99+ ) ;
63100 return {
64101 routeKey : `${ event . httpMethod } ${ routeConfig . path } ` ,
65102 queryStringParameters : event . queryStringParameters ?? { } ,
66103 pathParameters : routeConfig . params ?? { } ,
67104 body : event . body ,
68105 isBase64Encoded : event . isBase64Encoded ,
69106 } ;
70- }
107+ } ;
71108
72109export function getRouteConfigByPath (
73110 eventPath : string ,
74111 method : string ,
75- configs : ConfigRouteEntry [ ] ,
112+ configs : ConfigRouteEntry [ ]
76113) : ConfigRouteEntry & { params ?: { [ key : string ] : string } } {
77- eventPath = eventPath . replace ( / \? .* $ / , '' ) ; // Remove query string
78- const normalizedPath = eventPath . replace ( / ^ \/ / , '' ) ; // Remove leading slash
114+ eventPath = eventPath . replace ( / \? .* $ / , "" ) ; // Remove query string
115+ const normalizedPath = eventPath . replace ( / ^ \/ / , "" ) ; // Remove leading slash
79116 for ( const config of configs ) {
80117 const pattern = pathToRegex ( config . path ) ;
81118 const regex = new RegExp ( `^${ pattern } $` ) ;
@@ -91,17 +128,22 @@ export function getRouteConfigByPath(
91128 }
92129 }
93130
94- throw new CustomError ( JSON . stringify ( { message : ' path no found' } ) , 400 ) ;
131+ throw new CustomError ( JSON . stringify ( { message : " path no found" } ) , 400 ) ;
95132}
96133
97- export const lambdaRouteProxyEntryHandler = ( config : RouteConfig , availableRouteModules : { [ key : string ] : any } ) =>
98- async ( event : APIGatewayProxyEventV2 | APIGatewayProxyEvent | APIGatewayEvent ) => {
134+ export const lambdaRouteProxyEntryHandler =
135+ ( config : RouteConfig , availableRouteModules : { [ key : string ] : any } ) =>
136+ async (
137+ event : APIGatewayProxyEventV2 | APIGatewayProxyEvent | APIGatewayEvent
138+ ) => {
99139 console . log ( `Event Data: ${ JSON . stringify ( event ) } ` ) ;
100- const isV2 = ( event as APIGatewayProxyEventV2 ) . version === ' 2.0' ;
140+ const isV2 = ( event as APIGatewayProxyEventV2 ) . version === " 2.0" ;
101141
102- const isProxied = ! isV2 && event . hasOwnProperty ( ' requestContext' ) ;
142+ const isProxied = ! isV2 && event . hasOwnProperty ( " requestContext" ) ;
103143
104- const newEvent = isV2 ? v2ApiGatewayEvent ( event as APIGatewayProxyEventV2 ) : v1ApiGatewayEvent ( event as APIGatewayProxyEvent , config ) ;
144+ const newEvent = isV2
145+ ? v2ApiGatewayEvent ( event as APIGatewayProxyEventV2 )
146+ : v1ApiGatewayEvent ( event as APIGatewayProxyEvent , config ) ;
105147
106148 const {
107149 routeKey,
@@ -110,64 +152,98 @@ export const lambdaRouteProxyEntryHandler = (config: RouteConfig, availableRoute
110152 body,
111153 isBase64Encoded,
112154 } = newEvent ;
113-
155+
114156 let retVal : any = { } ;
115157 try {
116- const [ method = '' , path = '' ] = routeKey . split ( ' ' ) ;
117- if ( shouldAuthorizeRoute ( config , getRouteConfigEntry ( config , method , path ) ) ) {
158+ const [ method = "" , path = "" ] = routeKey . split ( " " ) ;
159+ if (
160+ shouldAuthorizeRoute ( config , getRouteConfigEntry ( config , method , path ) )
161+ ) {
118162 await authorizeRoute ( event ) ;
119163 }
120164
121- const routeModule = getRouteModule ( config , method , path , availableRouteModules ) ;
165+ const routeModule = getRouteModule (
166+ config ,
167+ method ,
168+ path ,
169+ availableRouteModules
170+ ) ;
122171
123172 console . log ( `isBase64Encoded: ${ isBase64Encoded } ` ) ;
124173 console . log ( `body: ${ body } ` ) ;
125- const decodedBody = isBase64Encoded ? Buffer . from ( body ! , 'base64' ) . toString ( 'utf-8' ) : undefined ;
174+ const decodedBody = isBase64Encoded
175+ ? Buffer . from ( body ! , "base64" ) . toString ( "utf-8" )
176+ : undefined ;
126177 console . log ( `decodedBody:
127178 ${ decodedBody } ` ) ;
128179
129-
130180 retVal = await getRouteModuleResult ( routeModule , {
131181 query : queryStringParameters ,
132182 params : pathParameters ,
133183 body : body ? decodedBody || JSON . parse ( body ) : undefined ,
134184 rawEvent : event ,
135185 } ) ;
136186
137- if ( isProxied ) {
138- if ( retVal . statusCode && ! retVal . body ) {
139- console . log ( ' body must be included when status code is set' , retVal ) ;
140- throw new CustomError ( ' No body found' , 500 ) ;
141- } else if ( retVal . statusCode && retVal . body ) {
187+ if ( isProxied ) {
188+ if ( retVal . statusCode && ! retVal . body ) {
189+ console . log ( " body must be included when status code is set" , retVal ) ;
190+ throw new CustomError ( " No body found" , 500 ) ;
191+ } else if ( retVal . statusCode && retVal . body ) {
142192 retVal = {
143193 ...retVal ,
144194 isBase64Encoded : false ,
145195 headers : {
146- ' Content-Type' : ' application/json' ,
147- ...retVal . headers ?? { }
196+ " Content-Type" : " application/json" ,
197+ ...( retVal . headers ?? { } ) ,
148198 } ,
149- body : typeof retVal . body === 'object' ? JSON . stringify ( retVal . body ) : retVal . body
199+ body :
200+ typeof retVal . body === "object"
201+ ? JSON . stringify ( retVal . body )
202+ : retVal . body ,
150203 } ;
151204 } else {
152205 retVal = {
153206 statusCode : 200 ,
154207 body : JSON . stringify ( retVal ) ,
155- ' Content-Type' : ' application/json' ,
156- }
208+ " Content-Type" : " application/json" ,
209+ } ;
157210 }
158211 }
159212 } catch ( error : any ) {
160213 console . error ( JSON . stringify ( { error, stack : error . stack } ) ) ;
214+ let headers = {
215+ "Content-Type" : "application/json" ,
216+ } as Record < string , string > ;
217+
218+ let statusCode = 500 ;
219+
220+ if ( isProxied ) {
221+ const isOptions = ( event . requestContext as any ) . httpMethod === "OPTIONS" ;
222+ if ( isOptions ) {
223+ statusCode = 200 ;
224+ } else {
225+ statusCode = error . httpStatusCode || 500 ;
226+ }
227+ headers = {
228+ ...headers ,
229+ "Access-Control-Allow-Origin" : "*" ,
230+ "Access-Control-Allow-Methods" :
231+ "GET, POST, PUT, DELETE, PATCH, OPTIONS" ,
232+ "Access-Control-Allow-Headers" :
233+ "Content-Type, Authorization, X-Amz-Date, X-Api-Key, X-Amz-Security-Token" ,
234+ "Access-Control-Allow-Credentials" : "true" ,
235+ } ;
236+ }
161237 if ( error instanceof CustomError ) {
162238 retVal = {
163- statusCode : error . httpStatusCode ,
164- headers : { 'Content-Type' : 'application/json' } ,
239+ statusCode,
240+ headers,
165241 body : error . message ,
166242 } ;
167243 } else {
168244 retVal = {
169- statusCode : 500 ,
170- headers : { 'Content-Type' : 'application/json' } ,
245+ statusCode,
246+ headers,
171247 body : error . message || JSON . stringify ( error ) ,
172248 } ;
173249 }
0 commit comments