2022-10-31 16:07:56 +01:00
|
|
|
import type { MiddlewareHandler } from '../../types.ts'
|
2022-07-02 08:09:45 +02:00
|
|
|
|
|
|
|
type CORSOptions = {
|
2022-09-16 13:59:27 +02:00
|
|
|
origin: string | string[] | ((origin: string) => string | undefined | null)
|
2022-07-02 08:09:45 +02:00
|
|
|
allowMethods?: string[]
|
|
|
|
allowHeaders?: string[]
|
|
|
|
maxAge?: number
|
|
|
|
credentials?: boolean
|
|
|
|
exposeHeaders?: string[]
|
|
|
|
}
|
|
|
|
|
2022-09-14 01:17:20 +02:00
|
|
|
export const cors = (options?: CORSOptions): MiddlewareHandler => {
|
2022-07-02 08:09:45 +02:00
|
|
|
const defaults: CORSOptions = {
|
|
|
|
origin: '*',
|
|
|
|
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
|
|
|
|
allowHeaders: [],
|
|
|
|
exposeHeaders: [],
|
|
|
|
}
|
|
|
|
const opts = {
|
|
|
|
...defaults,
|
|
|
|
...options,
|
|
|
|
}
|
|
|
|
|
2022-09-16 13:59:27 +02:00
|
|
|
const findAllowOrigin = ((optsOrigin) => {
|
|
|
|
if (typeof optsOrigin === 'string') {
|
|
|
|
return () => optsOrigin
|
|
|
|
} else if (typeof optsOrigin === 'function') {
|
|
|
|
return optsOrigin
|
|
|
|
} else {
|
|
|
|
return (origin: string) => (optsOrigin.includes(origin) ? origin : optsOrigin[0])
|
|
|
|
}
|
|
|
|
})(opts.origin)
|
|
|
|
|
2022-09-14 01:17:20 +02:00
|
|
|
return async (c, next) => {
|
2022-07-02 08:09:45 +02:00
|
|
|
function set(key: string, value: string) {
|
2023-05-24 11:01:19 +02:00
|
|
|
c.res.headers.set(key, value)
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-09-05 15:08:43 +02:00
|
|
|
const allowOrigin = findAllowOrigin(c.req.header('origin') || '')
|
2022-09-16 13:59:27 +02:00
|
|
|
if (allowOrigin) {
|
|
|
|
set('Access-Control-Allow-Origin', allowOrigin)
|
2022-09-15 17:19:02 +02:00
|
|
|
}
|
2022-07-02 08:09:45 +02:00
|
|
|
|
|
|
|
// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
|
|
|
if (opts.origin !== '*') {
|
|
|
|
set('Vary', 'Origin')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.credentials) {
|
|
|
|
set('Access-Control-Allow-Credentials', 'true')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.exposeHeaders?.length) {
|
|
|
|
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','))
|
|
|
|
}
|
|
|
|
|
2022-11-30 16:11:06 +01:00
|
|
|
if (c.req.method !== 'OPTIONS') {
|
|
|
|
await next()
|
2023-01-08 16:30:53 +01:00
|
|
|
} else {
|
2022-07-02 08:09:45 +02:00
|
|
|
// Preflight
|
|
|
|
|
|
|
|
if (opts.maxAge != null) {
|
|
|
|
set('Access-Control-Max-Age', opts.maxAge.toString())
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.allowMethods?.length) {
|
|
|
|
set('Access-Control-Allow-Methods', opts.allowMethods.join(','))
|
|
|
|
}
|
|
|
|
|
|
|
|
let headers = opts.allowHeaders
|
|
|
|
if (!headers?.length) {
|
2023-09-05 15:08:43 +02:00
|
|
|
const requestHeaders = c.req.header('Access-Control-Request-Headers')
|
2022-07-02 08:09:45 +02:00
|
|
|
if (requestHeaders) {
|
|
|
|
headers = requestHeaders.split(/\s*,\s*/)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (headers?.length) {
|
|
|
|
set('Access-Control-Allow-Headers', headers.join(','))
|
2023-05-24 11:01:19 +02:00
|
|
|
c.res.headers.append('Vary', 'Access-Control-Request-Headers')
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
c.res.headers.delete('Content-Length')
|
|
|
|
c.res.headers.delete('Content-Type')
|
|
|
|
|
2023-01-08 16:30:53 +01:00
|
|
|
return new Response(null, {
|
2022-07-02 08:09:45 +02:00
|
|
|
headers: c.res.headers,
|
|
|
|
status: 204,
|
|
|
|
statusText: c.res.statusText,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|