Files
bunext/dist/functions/server/handle-routes.js
T
2026-10-13 15:52:34 +01:00

146 lines
5.1 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";
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 now = Date.now();
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 import(`${target_import}?t=${now}`);
}
else {
const import_path = is_dev
? `${match.filePath}?t=${now}`
: match.filePath;
module = await import(import_path);
}
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"}`;
}