|
import { VercelRequest, VercelResponse } from '@vercel/node' |
|
|
|
export interface Request extends VercelRequest { |
|
params: { [key: string]: string } |
|
} |
|
|
|
export type Handler = (req: Request, res: VercelResponse) => unknown; |
|
|
|
interface Route { |
|
match: string; |
|
handler: Handler; |
|
method: string; |
|
} |
|
|
|
let match = (path: string, route: Route) => { |
|
let match = path.match(route.match); |
|
if (!match) return false; |
|
return match.groups || {}; |
|
}; |
|
|
|
export let router = ({ basePath, allowCors }: { |
|
basePath?: string, |
|
allowCors?: boolean |
|
}) => { |
|
if (!basePath) basePath = ''; |
|
if (basePath.endsWith('/')) basePath = basePath.slice(0, -1); |
|
|
|
let routes: Route[] |
|
|
|
let addRoute = (method: string, route: string, handler: Handler) => { |
|
routes.push({ |
|
match: `^${(basePath + route) |
|
.replace(/(\/?)\*/g, '($1.*)?') |
|
.replace(/\/$/, '') |
|
.replace(/:(\w+)(\?)?(\.)?/g, '$2(?<$1>[^/$3]+)$2$3') |
|
}\/*$`, |
|
handler, |
|
method: method.toUpperCase() |
|
}) |
|
}; |
|
|
|
let run = (baseReq: VercelRequest, res: VercelResponse) => { |
|
if (allowCors) { |
|
res.setHeader('Access-Control-Allow-Credentials', 'true') |
|
res.setHeader('Access-Control-Allow-Origin', '*') |
|
res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT') |
|
res.setHeader( |
|
'Access-Control-Allow-Headers', |
|
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' |
|
) |
|
|
|
if (baseReq.method === 'OPTIONS') { |
|
res.status(200).end(''); |
|
return |
|
} |
|
} |
|
|
|
let path = new URL(baseReq.url, `http://${baseReq.headers.host}`).pathname; |
|
|
|
for (let route of routes) { |
|
if (baseReq.method != route.method && route.method != 'ALL') continue; |
|
let params = match(path, route); |
|
|
|
if (params) { |
|
let req: Request = Object.assign(baseReq, { params }); |
|
route.handler(req, res); |
|
} |
|
} |
|
} |
|
|
|
return { |
|
run, |
|
all: (route: string, handler: Handler) => addRoute('ALL', route, handler), |
|
get: (route: string, handler: Handler) => addRoute('GET', route, handler), |
|
post: (route: string, handler: Handler) => addRoute('POST', route, handler), |
|
put: (route: string, handler: Handler) => addRoute('PUT', route, handler), |
|
patch: (route: string, handler: Handler) => addRoute('PATCH', route, handler), |
|
delete: (route: string, handler: Handler) => addRoute('DELETE', route, handler), |
|
head: (route: string, handler: Handler) => addRoute('HEAD', route, handler), |
|
options: (route: string, handler: Handler) => addRoute('OPTIONS', route, handler), |
|
} |
|
} |