103 lines
2.5 KiB
TypeScript
103 lines
2.5 KiB
TypeScript
import { headers } from "next/headers";
|
|
|
|
import { getEnv } from "@/lib/env";
|
|
|
|
function getFirstHeaderValue(value: string | null) {
|
|
return value?.split(",")[0]?.trim() || null;
|
|
}
|
|
|
|
function sanitizeProto(value: string | null) {
|
|
const candidate = getFirstHeaderValue(value)?.toLowerCase();
|
|
if (candidate === "http" || candidate === "https") {
|
|
return candidate;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function sanitizeHost(value: string | null) {
|
|
const candidate = getFirstHeaderValue(value)?.replace(/^"|"$/g, "");
|
|
if (!candidate) {
|
|
return null;
|
|
}
|
|
|
|
if (candidate.includes("/") || /\s/.test(candidate)) {
|
|
return null;
|
|
}
|
|
|
|
return candidate;
|
|
}
|
|
|
|
function parseForwardedHeader(value: string | null) {
|
|
const firstEntry = getFirstHeaderValue(value);
|
|
if (!firstEntry) {
|
|
return {
|
|
host: null,
|
|
proto: null,
|
|
};
|
|
}
|
|
|
|
let host: string | null = null;
|
|
let proto: "http" | "https" | null = null;
|
|
|
|
for (const segment of firstEntry.split(";")) {
|
|
const [rawKey, ...rest] = segment.split("=");
|
|
const key = rawKey?.trim().toLowerCase();
|
|
const rawValue = rest.join("=").trim();
|
|
|
|
if (!key || !rawValue) {
|
|
continue;
|
|
}
|
|
|
|
if (key === "host") {
|
|
host = sanitizeHost(rawValue) || host;
|
|
} else if (key === "proto") {
|
|
proto = sanitizeProto(rawValue) || proto;
|
|
}
|
|
}
|
|
|
|
return { host, proto };
|
|
}
|
|
|
|
function buildOriginFromParts(
|
|
host: string | null,
|
|
proto: "http" | "https" | null,
|
|
fallback: string,
|
|
) {
|
|
if (!host) {
|
|
return fallback;
|
|
}
|
|
|
|
return `${proto || "http"}://${host}`;
|
|
}
|
|
|
|
export async function getRequestOrigin() {
|
|
const headerList = await headers();
|
|
const envOrigin = getEnv().APP_URL;
|
|
const forwarded = parseForwardedHeader(headerList.get("forwarded"));
|
|
const host =
|
|
sanitizeHost(headerList.get("x-forwarded-host")) ||
|
|
forwarded.host ||
|
|
sanitizeHost(headerList.get("host"));
|
|
const proto =
|
|
sanitizeProto(headerList.get("x-forwarded-proto")) || forwarded.proto;
|
|
|
|
return buildOriginFromParts(host, proto, envOrigin);
|
|
}
|
|
|
|
export function getOriginFromRequest(request: Request) {
|
|
const url = new URL(request.url);
|
|
const forwarded = parseForwardedHeader(request.headers.get("forwarded"));
|
|
const host =
|
|
sanitizeHost(request.headers.get("x-forwarded-host")) ||
|
|
forwarded.host ||
|
|
sanitizeHost(request.headers.get("host")) ||
|
|
url.host;
|
|
const proto =
|
|
sanitizeProto(request.headers.get("x-forwarded-proto")) ||
|
|
forwarded.proto ||
|
|
sanitizeProto(url.protocol.replace(/:$/, ""));
|
|
|
|
return buildOriginFromParts(host, proto, getEnv().APP_URL);
|
|
}
|