Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions apps/cloud/src/edge/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// ---------------------------------------------------------------------------
// Edge concerns — the analytics/marketing/docs request middlewares that run at
// the worker edge BEFORE the app's own mcp + api dispatch. None of these touch
// the Effect app layer; they proxy or tunnel to external services (the
// marketing worker, Sentry, PostHog, Mintlify docs).
// Edge concerns — request middleware that runs before the app's own mcp + api
// dispatch. These proxy or tunnel to external services without touching the
// Effect app layer. Marketing is dispatched even earlier, from server.ts, so a
// public page never loads the TanStack Start graph.
// ---------------------------------------------------------------------------

export { marketingMiddleware } from "./marketing";
export { sentryTunnelMiddleware } from "./sentry-tunnel";
export { posthogProxyMiddleware } from "./posthog";
export { docsProxyMiddleware } from "./docs";
Expand Down
53 changes: 52 additions & 1 deletion apps/cloud/src/edge/marketing.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "@effect/vitest";

import { isMarketingPath } from "./marketing";
import { isMarketingPath, marketingProxyRequest } from "./marketing";

// On executor.sh the marketing middleware proxies an allow-list of paths to the
// `executor-marketing` worker; everything else falls through to the auth-gated
Expand Down Expand Up @@ -37,3 +37,54 @@ describe("isMarketingPath", () => {
});
}
});

describe("marketingProxyRequest", () => {
it("routes a signed-out homepage request", () => {
const request = new Request("https://executor.sh/?source=test");

const proxied = marketingProxyRequest(request);

expect(proxied?.url).toBe("https://executor.sh/?source=test");
});

it("leaves the signed-in homepage with the cloud application", () => {
const request = new Request("https://executor.sh/", {
headers: { cookie: "other=value; wos-session=sealed" },
});

expect(marketingProxyRequest(request)).toBeNull();
});

it("routes public content even when a session cookie is present", () => {
const request = new Request("https://executor.sh/blog/post", {
headers: { cookie: "wos-session=sealed" },
});

expect(marketingProxyRequest(request)?.url).toBe("https://executor.sh/blog/post");
});

it("rewrites the public home alias to the marketing root", () => {
const request = new Request("https://executor.sh/home?source=test");

expect(marketingProxyRequest(request)?.url).toBe("https://executor.sh/?source=test");
});

it("preserves the request method, headers, and body", async () => {
const request = new Request("https://executor.sh/_astro/_ph/capture", {
method: "POST",
headers: { "content-type": "application/json", "x-request-id": "request-1" },
body: JSON.stringify({ event: "test" }),
});

const proxied = marketingProxyRequest(request);

expect(proxied?.method).toBe("POST");
expect(proxied?.headers.get("x-request-id")).toBe("request-1");
await expect(proxied?.json()).resolves.toEqual({ event: "test" });
});

it("does not proxy non-production hosts or app-owned paths", () => {
expect(marketingProxyRequest(new Request("http://executor-cloud.localhost/"))).toBeNull();
expect(marketingProxyRequest(new Request("https://executor.sh/login"))).toBeNull();
});
});
56 changes: 22 additions & 34 deletions apps/cloud/src/edge/marketing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
//
// On the production domain (`executor.sh`), marketing paths and the
// unauthenticated landing page are served by the separate `executor-marketing`
// worker (bound as `env.MARKETING`). In local dev that worker isn't running, so
// unauthenticated visits fall through to the cloud app's routes (the sign-in
// page).
// worker. This module deliberately has no TanStack Start or cloud application
// imports: the Worker entry calls it before loading the Start server graph.
// ---------------------------------------------------------------------------

import { env } from "cloudflare:workers";
import { createMiddleware } from "@tanstack/react-start";

import { parseCookie } from "../auth/cookies";

const MARKETING_PATHS = [
Expand All @@ -27,33 +23,25 @@ const MARKETING_PATHS = [
"/pattern-graph-paper.svg",
];

export const isMarketingPath = (pathname: string) =>
MARKETING_PATHS.some((p) => pathname === p || pathname.startsWith(`${p}/`));

const getMarketingWorker = () => env.MARKETING as { fetch: typeof fetch } | undefined;

export const marketingMiddleware = createMiddleware({ type: "request" }).server(
async ({ pathname, request, next }) => {
// Only proxy to the marketing worker on the production domain. In local
// dev we don't run `executor-marketing`, so unauthenticated visits fall
// through to the cloud app's routes (which show the sign-in page).
const host = new URL(request.url).hostname;
if (host !== "executor.sh") return next();
const SESSION_COOKIE = "wos-session";

const shouldProxyToMarketing =
isMarketingPath(pathname) ||
(pathname === "/" && !parseCookie(request.headers.get("cookie"), "wos-session"));

if (!shouldProxyToMarketing) return next();

const marketing = getMarketingWorker();
if (!marketing) return next();
/** Whether an exact pathname belongs to the public marketing worker. */
export const isMarketingPath = (pathname: string): boolean =>
MARKETING_PATHS.some((p) => pathname === p || pathname.startsWith(`${p}/`));

const url = new URL(request.url);
// Rewrite /home to / so marketing worker serves its homepage
if (pathname === "/home") {
url.pathname = "/";
}
return marketing.fetch(new Request(url, request));
},
);
/**
* Project a production request onto the marketing service-binding request.
* Returns `null` when the cloud application owns the request instead.
*/
export const marketingProxyRequest = (request: Request): Request | null => {
const url = new URL(request.url);
if (url.hostname !== "executor.sh") return null;

const shouldProxy =
isMarketingPath(url.pathname) ||
(url.pathname === "/" && !parseCookie(request.headers.get("cookie"), SESSION_COOKIE));
if (!shouldProxy) return null;

if (url.pathname === "/home") url.pathname = "/";
return new Request(url, request);
};
9 changes: 9 additions & 0 deletions apps/cloud/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as Sentry from "@sentry/cloudflare";
import handler from "@tanstack/react-start/server-entry";

import { isAppOwnedPath } from "./app-paths";
import { marketingProxyRequest } from "./edge/marketing";
import { makeCloudMcpAgentHandler } from "./mcp/agent-handler";
import { classifyMcpPath, prepareMcpOrgScope } from "./mcp/mount";
import { parseTraceparent } from "./mcp/traceparent";
Expand Down Expand Up @@ -175,6 +176,14 @@ const mcpAgentHandler = makeCloudMcpAgentHandler({

const cloudflareHandler: ExportedHandler<Env> = {
fetch: async (request, env, ctx) => {
// Public pages must not enter TanStack Start: its first-request dynamic
// import loads the entire React + Effect server graph and can take seconds
// on a cold isolate. Classify and service-bind marketing at the Worker
// entry, before telemetry or fetchHandler touches that graph.
const marketingRequest = marketingProxyRequest(request);
const marketing: Fetcher | undefined = env.MARKETING;
if (marketingRequest && marketing) return marketing.fetch(marketingRequest);

// Browser OTLP ingress — before the server span opens: exporter traffic
// must never trace itself (the browser already excludes /v1/traces from
// its own tracing for the same reason).
Expand Down
22 changes: 10 additions & 12 deletions apps/cloud/src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { loginPath } from "./auth/return-to";
import { prepareMcpOrgScope } from "./mcp/mount";
import {
docsProxyMiddleware,
marketingMiddleware,
openAiAppsChallengeMiddleware,
posthogProxyMiddleware,
sentryTunnelMiddleware,
Expand Down Expand Up @@ -89,20 +88,19 @@ const appRequestMiddleware = createMiddleware({ type: "request" }).server(
},
);

// The edge concerns (marketing proxy, docs proxy, sentry tunnel, posthog proxy)
// live in `./edge`; they run before the app's own dispatch. Ordering is
// load-bearing: marketing first (production landing/page proxy), then the docs
// proxy and analytics tunnels, then the unified app plane (api + mcp), and last
// the SSR auth gate — it only sees document requests nothing above claimed, so
// signed-out visitors are redirected to /login before the SPA (and its
// app-shell skeleton) is served. The docs proxy sits among the edges (not after
// the auth gate) because `/docs` is public and must skip the sign-in redirect;
// its path is disjoint from every other matcher, so its slot is not otherwise
// load-bearing.
// The remaining edge concerns (docs proxy, sentry tunnel, posthog proxy) live
// in `./edge`; they run before the app's own dispatch. Marketing is handled in
// server.ts before this module is loaded. Ordering here is load-bearing: public
// challenges and docs, then analytics tunnels, then the unified app plane (api
// + mcp), and last the SSR auth gate — it only sees document requests nothing
// above claimed, so signed-out visitors are redirected to /login before the SPA
// (and its app-shell skeleton) is served. The docs proxy sits among the edges
// (not after the auth gate) because `/docs` is public and must skip the sign-in
// redirect; its path is disjoint from every other matcher, so its slot is not
// otherwise load-bearing.
export const startInstance = createStart(() => ({
requestMiddleware: [
openAiAppsChallengeMiddleware,
marketingMiddleware,
docsProxyMiddleware,
sentryTunnelMiddleware,
posthogProxyMiddleware,
Expand Down
Loading