-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
124 lines (111 loc) · 3.78 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { Middleware } from '../core';
import { SanitizedHeadersEvent } from '../sanitizeHeaders';
import { APIGatewayProxyResult } from '../types/APIGatewayProxyResult';
export interface CorsOptions {
/**
* When true, creates response on OPTIONS request with 'Access-Control-Allow-*'
* headers
* @default true
*/
preflight?: boolean;
/**
* When true, uses 'Origin' header from request as 'Access-Control-Allow-Origin'
* in response.
* @default '*'
*/
origin?: true | string | string[];
/**
* Specifies value for 'Access-Control-Allow-Credentials' header
* @default true
*/
credentials?: boolean;
/**
* Specifies value for 'Access-Control-Allow-Headers' header
* When set to true, headers from 'Access-Control-Request-Headers' will be
* used
* @default ['Content-Type']
*/
headers?: string[] | true;
/**
* Specifies values for 'Access-Control-Allow-Methods' header.
* @default ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE']
*/
methods?: string[];
/**
* Specifies values for 'Access-Control-Allow-Max-Age' header.
* @default false
*/
maxAge?: number | false;
}
type EvaluatedCorsOptions = Required<CorsOptions>;
export const defaultCorsOptions: EvaluatedCorsOptions = {
preflight: true,
origin: '*',
credentials: true,
headers: ['Content-Type'],
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
maxAge: false,
};
interface CorsRequiredEvent extends SanitizedHeadersEvent {
httpMethod: string;
}
export const handleCors =
<T extends CorsRequiredEvent, R extends APIGatewayProxyResult>(
options: CorsOptions = {},
): Middleware<T, T, Promise<R>, Promise<R>> =>
(handler) =>
async (event, ...rest) => {
const evaluatedOptions = { ...defaultCorsOptions, ...options };
if (evaluatedOptions.preflight && event.httpMethod === 'OPTIONS') {
return {
statusCode: 200,
headers: {
...accessControlAllowOrigin(evaluatedOptions, event),
...accessControlAllowCredentials(evaluatedOptions),
...accessControlAllowHeaders(evaluatedOptions, event),
...accessControlAllowMethods(evaluatedOptions),
...accessControlAllowMaxAge(evaluatedOptions),
},
} satisfies APIGatewayProxyResult as unknown as R;
}
let response = await handler(event, ...rest);
response = {
...response,
headers: {
...accessControlAllowOrigin(evaluatedOptions, event),
...accessControlAllowCredentials(evaluatedOptions),
...response?.headers,
},
};
return response;
};
const accessControlAllowOrigin = ({ origin }: EvaluatedCorsOptions, event: CorsRequiredEvent) => {
const requestOrigin = event.sanitizedHeaders['origin'];
let _origin: string | null = null;
if (origin === true) {
_origin = requestOrigin;
} else if (Array.isArray(origin)) {
if (origin.includes(requestOrigin)) {
_origin = requestOrigin;
}
} else {
_origin = origin;
}
return {
'Access-Control-Allow-Origin': String(_origin),
};
};
const accessControlAllowCredentials = ({ credentials }: EvaluatedCorsOptions) => ({
'Access-Control-Allow-Credentials': String(credentials),
});
const accessControlAllowHeaders = ({ headers }: EvaluatedCorsOptions, event: CorsRequiredEvent) => {
const headerStr =
headers === true
? event.sanitizedHeaders['access-control-request-headers'] ?? ''
: headers.join(',');
return headerStr ? { 'Access-Control-Allow-Headers': headerStr } : undefined;
};
const accessControlAllowMethods = ({ methods }: EvaluatedCorsOptions) =>
methods.length ? { 'Access-Control-Allow-Methods': methods.join(',') } : undefined;
const accessControlAllowMaxAge = ({ maxAge }: EvaluatedCorsOptions) =>
maxAge !== false ? { 'Access-Control-Allow-Max-Age': String(maxAge) } : undefined;