Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/url.server.ts
T

107 lines
4.6 KiB
TypeScript

import { getDomainRoutingConfig, loadRequiredWebConfig, type DomainRoutingEnv, type EnvRecord } from "@toolctrl/config";
import { getCanonicalHostRedirectUrl, getHostRoutingContextFromRequest, getRequestRoutingHost } from "./app/HostRouting.server";
import { isAbsoluteWebHostPath, pathMatchesRoutePrefix, protectedWebRouteExactPaths, protectedWebRoutePrefixes } from "./app/PublicRouteSegments";
type UrlServerOptions = {
https?: boolean;
env?: EnvRecord & DomainRoutingEnv;
};
export const getBaseURL = (request: Request, options?: UrlServerOptions) => {
const config = loadRequiredWebConfig(options?.env);
const url = new URL(request.url);
const host = getRequestRoutingHost(request, url);
const forwardedProtocol = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
if (config.nodeEnv === "development") {
return `http://${host}`;
}
if (options?.https || config.nodeEnv === "production") {
return `https://${host}`;
} else if (forwardedProtocol) {
return `${forwardedProtocol}://${host}`;
} else {
return `${url.protocol}//${host}`;
}
};
export const getDomainName = (request: Request) => {
const url = new URL(request.url);
return getRequestRoutingHost(request, url);
};
export const getAbsoluteUrl = (request: Request, path: string, options?: UrlServerOptions) => {
const config = getDomainRoutingConfig(options?.env);
const context = getHostRoutingContextFromRequest(request, options?.env);
const targetPath = normalizeAbsoluteUrlInputToPath(path, config);
const host = getTargetHostForPath(targetPath, context, config);
const targetUrl = new URL(targetPath, `${config.protocol}://${host}`);
const canonicalRedirectUrl = getCanonicalHostRedirectUrl(targetUrl, config);
if (canonicalRedirectUrl) {
return canonicalRedirectUrl;
}
return targetUrl.toString();
};
function normalizeAbsoluteUrlInputToPath(path: string, config: ReturnType<typeof getDomainRoutingConfig>) {
const parsedUrl = new URL(path, `${config.protocol}://${config.appBaseDomain}`);
return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;
}
function getTargetHostForPath(path: string, context: ReturnType<typeof getHostRoutingContextFromRequest>, config: ReturnType<typeof getDomainRoutingConfig>) {
const targetPath = new URL(path, `${config.protocol}://${config.appBaseDomain}`).pathname;
if (targetPath === "/v1" || targetPath.startsWith("/v1/")) {
return config.apiHost;
}
if (isAbsoluteWebHostPath(targetPath) || isProtectedWebHostPath(targetPath)) {
return config.webAppHost;
}
return context === "app" ? config.webAppHost : context === "admin" ? config.adminAppHost : context === "api" ? config.apiHost : config.appBaseDomain;
}
function isProtectedWebHostPath(pathname: string) {
return (
(protectedWebRouteExactPaths as readonly string[]).includes(pathname) ||
protectedWebRoutePrefixes.some((prefix) => pathMatchesRoutePrefix(pathname, prefix))
);
}
export const getAbsoluteCurrentUrl = (request: Request, options?: UrlServerOptions) => {
const config = getDomainRoutingConfig(options?.env);
const url = new URL(request.url);
const canonicalRedirectUrl = getCanonicalHostRedirectUrl(request, config);
if (canonicalRedirectUrl) {
return canonicalRedirectUrl;
}
const context = getHostRoutingContextFromRequest(request, options?.env);
if (context === "app") {
return `${config.protocol}://${config.webAppHost}${url.pathname}${url.search}${url.hash}`;
}
if (context === "admin") {
return `${config.protocol}://${config.adminAppHost}${url.pathname}${url.search}${url.hash}`;
}
if (context === "api") {
return `${config.protocol}://${config.apiHost}${url.pathname}${url.search}${url.hash}`;
}
return getAbsoluteUrl(request, `${url.pathname}${url.search}${url.hash}`, options);
};
export const getAbsoluteApiUrl = (request: Request, path: string, options?: UrlServerOptions) => {
const config = getDomainRoutingConfig(options?.env);
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
const targetUrl = new URL(normalizedPath, `${config.protocol}://${config.apiHost}`);
const strippedPath = targetUrl.pathname.replace(/^\/api\/v1(?=\/|$)/, "").replace(/^\/v1(?=\/|$)/, "");
const versionedPath = `/v1${strippedPath === "/" ? "" : strippedPath}`;
return new URL(`${versionedPath}${targetUrl.search}${targetUrl.hash}`, `${config.protocol}://${config.apiHost}`).toString();
};
export const getWebhooksUrl = (request: Request, options?: UrlServerOptions) => {
const config = loadRequiredWebConfig(options?.env);
return config.webhooksUrl ?? getBaseURL(request, options);
};