mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
Adds isolated Convex-backed Vercel PR previews with shared local/preview seeding, preview-safe routing, and production guards.
85 lines
2.7 KiB
TypeScript
85 lines
2.7 KiB
TypeScript
import {
|
|
defineEventHandler,
|
|
getRequestURL,
|
|
proxyRequest,
|
|
type H3Event,
|
|
type HTTPResponse,
|
|
} from "h3";
|
|
import { convexDeploymentName, resolveConvexSiteUrl } from "../src/lib/convexDeploymentUrl";
|
|
|
|
type ProxyEnv = {
|
|
CONVEX_URL?: string;
|
|
VERCEL_ENV?: string;
|
|
VITE_CLAWHUB_DEPLOY_ENV?: string;
|
|
VITE_CONVEX_SITE_URL?: string;
|
|
VITE_CONVEX_URL?: string;
|
|
};
|
|
|
|
const BUNDLED_PROXY_ENV: ProxyEnv = {
|
|
VITE_CLAWHUB_DEPLOY_ENV: import.meta.env.VITE_CLAWHUB_DEPLOY_ENV,
|
|
VITE_CONVEX_SITE_URL: import.meta.env.VITE_CONVEX_SITE_URL,
|
|
VITE_CONVEX_URL: import.meta.env.VITE_CONVEX_URL,
|
|
};
|
|
|
|
export function resolveConvexProxyEnv(
|
|
runtimeEnv: ProxyEnv,
|
|
bundledEnv: ProxyEnv = BUNDLED_PROXY_ENV,
|
|
): ProxyEnv {
|
|
return {
|
|
...runtimeEnv,
|
|
...(bundledEnv.VITE_CLAWHUB_DEPLOY_ENV
|
|
? { VITE_CLAWHUB_DEPLOY_ENV: bundledEnv.VITE_CLAWHUB_DEPLOY_ENV }
|
|
: {}),
|
|
...(bundledEnv.VITE_CONVEX_SITE_URL
|
|
? { VITE_CONVEX_SITE_URL: bundledEnv.VITE_CONVEX_SITE_URL }
|
|
: {}),
|
|
...(bundledEnv.VITE_CONVEX_URL ? { VITE_CONVEX_URL: bundledEnv.VITE_CONVEX_URL } : {}),
|
|
};
|
|
}
|
|
|
|
function isPreviewFrontend(env: ProxyEnv) {
|
|
return env.VERCEL_ENV === "preview" || env.VITE_CLAWHUB_DEPLOY_ENV === "preview";
|
|
}
|
|
|
|
export function isConvexProxyMethodAllowed(method: string, env: ProxyEnv) {
|
|
if (!isPreviewFrontend(env)) return true;
|
|
return method === "GET" || method === "HEAD";
|
|
}
|
|
|
|
export function buildConvexProxyTarget(pathAndQuery: string, env: ProxyEnv) {
|
|
const requestUrl = new URL(pathAndQuery, "https://clawhub.invalid");
|
|
const targetPath = requestUrl.pathname.startsWith("/v1/feeds/")
|
|
? `/api${requestUrl.pathname}`
|
|
: requestUrl.pathname;
|
|
const targetUrl = new URL(targetPath, resolveConvexSiteUrl(env));
|
|
targetUrl.search = requestUrl.search;
|
|
return targetUrl.toString();
|
|
}
|
|
|
|
export async function proxyConvexRequest(
|
|
event: H3Event,
|
|
env: ProxyEnv = resolveConvexProxyEnv(process.env),
|
|
): Promise<HTTPResponse | Response> {
|
|
if (!isConvexProxyMethodAllowed(event.req.method, env)) {
|
|
return new Response("Disposable previews are read-only.", {
|
|
status: 405,
|
|
headers: {
|
|
Allow: "GET, HEAD",
|
|
"Cache-Control": "no-store",
|
|
"Content-Type": "text/plain; charset=utf-8",
|
|
},
|
|
});
|
|
}
|
|
|
|
const requestUrl = getRequestURL(event);
|
|
const target = buildConvexProxyTarget(`${requestUrl.pathname}${requestUrl.search}`, env);
|
|
const response = await proxyRequest(event, target);
|
|
if (isPreviewFrontend(env)) {
|
|
const deployment = convexDeploymentName(target);
|
|
if (deployment) response.headers.set("X-ClawHub-Preview-Backend", deployment);
|
|
}
|
|
return response;
|
|
}
|
|
|
|
export default defineEventHandler((event) => proxyConvexRequest(event));
|