143 lines
5.0 KiB
JavaScript
143 lines
5.0 KiB
JavaScript
import grabRouteParams from "../../utils/grab-route-params";
|
|
import grabConstants from "../../utils/grab-constants";
|
|
import grabRouter from "../../utils/grab-router";
|
|
import isDevelopment from "../../utils/is-development";
|
|
import { log } from "../../utils/log";
|
|
import _ from "lodash";
|
|
import path from "path";
|
|
import grabDirNames from "../../utils/grab-dir-names";
|
|
import grabRelativePath from "../../utils/grab-relative-path";
|
|
import { RequestBodyTooLargeError } from "../../utils/read-request-body";
|
|
import importFresh from "../../utils/import-fresh";
|
|
const { ROOT_DIR } = grabDirNames();
|
|
export default async function ({ req }) {
|
|
try {
|
|
const url = new URL(req.url);
|
|
const is_dev = isDevelopment();
|
|
const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = grabConstants();
|
|
const router = grabRouter();
|
|
const match = router.match(url.pathname);
|
|
if (!match?.filePath) {
|
|
const errMsg = `Route ${url.pathname} not found`;
|
|
return Response.json({
|
|
success: false,
|
|
msg: errMsg,
|
|
}, {
|
|
status: 404,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
}
|
|
let module;
|
|
const relative_match_path = grabRelativePath(match.filePath);
|
|
if (is_dev &&
|
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[relative_match_path]?.path) {
|
|
const target_import = path.join(ROOT_DIR, global.BUNEXT_SSR_BUNDLER_CTX_MAP[relative_match_path].path);
|
|
module = await importFresh(target_import);
|
|
}
|
|
else {
|
|
module = await importFresh(match.filePath);
|
|
}
|
|
const config = module.config;
|
|
const maxBodyBytes = config?.max_request_body_mb
|
|
? config.max_request_body_mb * MBInBytes
|
|
: ServerDefaultRequestBodyLimitBytes;
|
|
const contentLength = req.headers.get("content-length");
|
|
if (contentLength && parseInt(contentLength, 10) > maxBodyBytes) {
|
|
return bodyTooLargeResponse();
|
|
}
|
|
let routeParams;
|
|
try {
|
|
routeParams = await grabRouteParams({
|
|
req,
|
|
query: match.query,
|
|
max_body_bytes: maxBodyBytes,
|
|
});
|
|
}
|
|
catch (error) {
|
|
if (error instanceof RequestBodyTooLargeError) {
|
|
return bodyTooLargeResponse();
|
|
}
|
|
throw error;
|
|
}
|
|
const target_module = (module["default"] ||
|
|
module["handler"]);
|
|
if (typeof target_module !== "function") {
|
|
log.error(`API Route ${url.pathname} has no default or \`handler\` export`);
|
|
return Response.json({
|
|
success: false,
|
|
msg: is_dev
|
|
? `Route ${url.pathname} has no default or \`handler\` export`
|
|
: "Internal Server Error",
|
|
}, { status: 500 });
|
|
}
|
|
let res;
|
|
try {
|
|
res = await target_module({
|
|
...routeParams,
|
|
});
|
|
}
|
|
catch (error) {
|
|
log.error(`API Route Error (${url.pathname}): ${error?.message}\n${error?.stack || ""}`);
|
|
return Response.json({
|
|
success: false,
|
|
msg: routeErrorMessage(error),
|
|
}, {
|
|
status: 500,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
}
|
|
if (res instanceof Response) {
|
|
if (is_dev) {
|
|
res.headers.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
}
|
|
return res;
|
|
}
|
|
if (res) {
|
|
let final_res = Response.json(_.omit(res, [
|
|
"bunext_api_route_res_options",
|
|
"bunext_api_route_res_transform_fn",
|
|
]), {
|
|
...(res.bunext_api_route_res_options || undefined),
|
|
});
|
|
if (res.bunext_api_route_res_transform_fn) {
|
|
final_res = await res.bunext_api_route_res_transform_fn(final_res);
|
|
}
|
|
return final_res;
|
|
}
|
|
return Response.json({ err: `Route handler error` });
|
|
}
|
|
catch (error) {
|
|
const url = new URL(req.url);
|
|
log.error(`API Route Error (${url.pathname}): ${error?.message}\n${error?.stack || ""}`);
|
|
return Response.json({
|
|
success: false,
|
|
msg: routeErrorMessage(error),
|
|
}, {
|
|
status: 500,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
}
|
|
}
|
|
function bodyTooLargeResponse() {
|
|
return Response.json({
|
|
success: false,
|
|
msg: "Request Body Too Large!",
|
|
}, { status: 413 });
|
|
}
|
|
/**
|
|
* Error details can leak internals, so they are only exposed in
|
|
* development. They are always logged server-side.
|
|
*/
|
|
function routeErrorMessage(error) {
|
|
if (!isDevelopment()) {
|
|
return "Internal Server Error";
|
|
}
|
|
return `Route handler error: ${error?.message || "Internal Server Error"}`;
|
|
}
|