Files
manyangles/apps/web/src/server/public-app-url.ts
T

61 lines
1.6 KiB
TypeScript

const WILDCARD_HOSTNAMES = new Set(["0.0.0.0", "::", "[::]"]);
const LOCAL_HOSTNAMES = new Set([...WILDCARD_HOSTNAMES, "localhost"]);
function isWildcardHostname(hostname: string) {
return WILDCARD_HOSTNAMES.has(hostname);
}
function isLocalHostname(hostname: string) {
return (
LOCAL_HOSTNAMES.has(hostname) ||
hostname === "127.0.0.1" ||
hostname.startsWith("127.")
);
}
export function resolvePublicAppOrigin(input: {
configuredUrl?: string;
authUrl?: string;
requestUrl?: string;
production: boolean;
}) {
const configured = input.configuredUrl?.trim() || input.authUrl?.trim();
if (!configured) {
if (input.production) {
throw new Error("NEXT_PUBLIC_APP_URL is required in production");
}
if (input.requestUrl) {
const requestOrigin = new URL(input.requestUrl);
if (!isWildcardHostname(requestOrigin.hostname)) {
return requestOrigin.origin;
}
}
return "http://localhost:3000";
}
const url = new URL(configured);
if (isWildcardHostname(url.hostname)) {
throw new Error(
`NEXT_PUBLIC_APP_URL resolved to a wildcard bind address (${url.hostname})`,
);
}
if (
input.production &&
(url.protocol !== "https:" || isLocalHostname(url.hostname))
) {
throw new Error(
"NEXT_PUBLIC_APP_URL must be a public HTTPS origin in production",
);
}
return url.origin;
}
export function publicAppOrigin(requestUrl?: string) {
return resolvePublicAppOrigin({
configuredUrl: process.env.NEXT_PUBLIC_APP_URL,
authUrl: process.env.BETTER_AUTH_URL,
requestUrl,
production: process.env.NODE_ENV === "production",
});
}