File size: 4,432 Bytes
6111b2b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import {
PUBLIC_API_ROUTE_PREFIXES,
PUBLIC_READONLY_API_ROUTE_PREFIXES,
PUBLIC_READONLY_METHODS,
} from "../../shared/constants/publicApiRoutes";
import type { ClassificationReason, RouteClass, RouteClassification } from "./types";
const CLIENT_API_ALIAS_PREFIXES: ReadonlyArray<{ alias: string; canonical: string }> = [
{ alias: "/chat/completions", canonical: "/api/v1/chat/completions" },
{ alias: "/responses", canonical: "/api/v1/responses" },
{ alias: "/models", canonical: "/api/v1/models" },
];
function normalizePathname(rawPath: string): { path: string; reason?: ClassificationReason } {
let path = rawPath || "/";
if (!path.startsWith("/")) path = "/" + path;
if (path.length > 1 && path.endsWith("/")) path = path.slice(0, -1);
if (path === "/codex" || path.startsWith("/codex/")) {
return { path: "/api/v1/responses", reason: "client_api_codex_alias" };
}
if (path === "/v1/v1" || path.startsWith("/v1/v1/")) {
const tail = path.slice("/v1/v1".length) || "";
return { path: "/api/v1" + tail, reason: "client_api_double_prefix" };
}
if (path === "/v1" || path.startsWith("/v1/")) {
const tail = path.slice("/v1".length) || "";
return { path: "/api/v1" + tail, reason: "client_api_alias" };
}
for (const { alias, canonical } of CLIENT_API_ALIAS_PREFIXES) {
if (path === alias) {
return { path: canonical, reason: "client_api_alias" };
}
if (path.startsWith(alias + "/")) {
return { path: canonical + path.slice(alias.length), reason: "client_api_alias" };
}
}
return { path };
}
export function classifyRoute(rawPath: string, method: string = "GET"): RouteClassification {
const { path: normalizedPath, reason: aliasReason } = normalizePathname(rawPath);
if (normalizedPath === "/" || normalizedPath === "") {
return {
routeClass: "MANAGEMENT",
reason: "root_redirect",
normalizedPath: "/",
};
}
if (normalizedPath === "/dashboard/onboarding") {
return {
routeClass: "PUBLIC",
reason: "setup_wizard",
normalizedPath,
};
}
// Public, ticket-gated device-flow connect pages (e.g. /connect/codex/{token}).
// Anyone with the shared link completes the provider login in their own browser.
if (normalizedPath === "/connect" || normalizedPath.startsWith("/connect/")) {
return {
routeClass: "PUBLIC",
reason: "public_connect_page",
normalizedPath,
};
}
if (normalizedPath.startsWith("/dashboard")) {
return {
routeClass: "MANAGEMENT",
reason: "dashboard_prefix",
normalizedPath,
};
}
if (normalizedPath === "/api/v1" || normalizedPath.startsWith("/api/v1/")) {
return {
routeClass: "CLIENT_API",
reason: aliasReason ?? "client_api_v1",
normalizedPath,
};
}
if (normalizedPath.startsWith("/api/")) {
if (isClassifiedAsPublic(normalizedPath, method)) {
return {
routeClass: "PUBLIC",
reason: matchesReadonlyPublic(normalizedPath, method)
? "public_readonly_prefix"
: "public_prefix",
normalizedPath,
};
}
return {
routeClass: "MANAGEMENT",
reason: "management_api",
normalizedPath,
};
}
return {
routeClass: "MANAGEMENT",
reason: "fallback_management",
normalizedPath,
};
}
function matchesReadonlyPublic(path: string, method: string): boolean {
if (!PUBLIC_READONLY_METHODS.has(String(method).toUpperCase())) return false;
return PUBLIC_READONLY_API_ROUTE_PREFIXES.some((p) => path.startsWith(p));
}
function isClassifiedAsPublic(path: string, method: string): boolean {
const isV1ApiPrefix = (p: string) =>
p === "/api/v1" || p === "/api/v1/" || p.startsWith("/api/v1/");
const filtered = PUBLIC_API_ROUTE_PREFIXES.filter((p) => p !== "/api/v1/");
if (filtered.some((prefix) => path.startsWith(prefix)) && !isV1ApiPrefix(path)) {
return true;
}
return matchesReadonlyPublic(path, method);
}
export function isClientApi(routeClass: RouteClass): boolean {
return routeClass === "CLIENT_API";
}
export function isManagement(routeClass: RouteClass): boolean {
return routeClass === "MANAGEMENT";
}
export function isPublic(routeClass: RouteClass): boolean {
return routeClass === "PUBLIC";
}
|