86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import { getCanonicalHostRedirectUrl, getHostAwareInternalUrlPath, getHostRoutingContextFromRequest } from "../apps/web/app/utils/app/HostRouting.server";
|
|
|
|
export type HostRoutingRuntimeDecision =
|
|
| { type: "redirect"; status: 308; location: string }
|
|
| { type: "reject"; status: 404 }
|
|
| { type: "dispatch"; target: "api" | "web"; request: Request };
|
|
|
|
const internalWebApiProxyHeader = "x-toolctrl-internal-web-api-proxy";
|
|
|
|
export function getHostRoutingRuntimeDecision(request: Request): HostRoutingRuntimeDecision {
|
|
const context = getHostRoutingContextFromRequest(request);
|
|
if (isInternalWebApiProxyRequest(request, context)) {
|
|
const requestUrl = new URL(request.url);
|
|
const rewrittenRequest = rewriteRequestUrl(request, `${requestUrl.pathname}${requestUrl.search}${requestUrl.hash}`);
|
|
return {
|
|
type: "dispatch",
|
|
target: "web",
|
|
request: rewrittenRequest,
|
|
};
|
|
}
|
|
|
|
if (isRemovedLegacyRoute(request, context)) {
|
|
return {
|
|
type: "reject",
|
|
status: 404,
|
|
};
|
|
}
|
|
|
|
const canonicalRedirectUrl = getCanonicalHostRedirectUrl(request);
|
|
if (canonicalRedirectUrl) {
|
|
return {
|
|
type: "redirect",
|
|
status: 308,
|
|
location: canonicalRedirectUrl,
|
|
};
|
|
}
|
|
|
|
const rewrittenRequest = rewriteRequestUrl(request, getRuntimeInternalUrlPath(request, context));
|
|
return {
|
|
type: "dispatch",
|
|
target: context === "api" ? "api" : "web",
|
|
request: rewrittenRequest,
|
|
};
|
|
}
|
|
|
|
function isInternalWebApiProxyRequest(request: Request, context: ReturnType<typeof getHostRoutingContextFromRequest>) {
|
|
if (context !== "app" || request.headers.get(internalWebApiProxyHeader) !== "1") {
|
|
return false;
|
|
}
|
|
|
|
const pathname = new URL(request.url).pathname;
|
|
return pathname === "/internal/api/usage" || pathname === "/internal/api/stripe.webhooks" || pathname.startsWith("/internal/api/workflows/");
|
|
}
|
|
|
|
function isRemovedLegacyRoute(request: Request, context: ReturnType<typeof getHostRoutingContextFromRequest>) {
|
|
const pathname = new URL(request.url).pathname.replace(/\/+$/g, "") || "/";
|
|
const isLegacyApiV1Route = pathname === "/api/v1" || pathname.startsWith("/api/v1/");
|
|
const isAdminApiSectionRoute = context === "admin" && (pathname === "/api" || pathname.startsWith("/api/")) && !isLegacyApiV1Route;
|
|
return (
|
|
pathname === "/app" ||
|
|
pathname.startsWith("/app/") ||
|
|
pathname === "/admin" ||
|
|
pathname.startsWith("/admin/") ||
|
|
(!isAdminApiSectionRoute && (pathname === "/api" || (pathname.startsWith("/api/") && !isLegacyApiV1Route)))
|
|
);
|
|
}
|
|
|
|
function getRuntimeInternalUrlPath(request: Request, context: ReturnType<typeof getHostRoutingContextFromRequest>) {
|
|
if (context === "app") {
|
|
const url = new URL(request.url);
|
|
return `${url.pathname}${url.search}${url.hash}`;
|
|
}
|
|
|
|
return getHostAwareInternalUrlPath(request);
|
|
}
|
|
|
|
function rewriteRequestUrl(request: Request, internalUrlPath: string) {
|
|
const url = new URL(request.url);
|
|
url.pathname = "/";
|
|
url.search = "";
|
|
url.hash = "";
|
|
|
|
const rewrittenUrl = new URL(internalUrlPath, url);
|
|
return new Request(rewrittenUrl, request);
|
|
}
|