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
2 changes: 2 additions & 0 deletions apps/cloud/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@executor/plugin-mcp": "workspace:*",
"@executor/plugin-openapi": "workspace:*",
"@executor/react": "workspace:*",
"@executor/runtime-dynamic-worker": "workspace:*",
"@executor/sdk": "workspace:*",
"@executor/storage-postgres": "workspace:*",
"@tanstack/react-router": "catalog:",
Expand All @@ -33,6 +34,7 @@
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/plugin-react": "catalog:",
"@cloudflare/workers-types": "^4.20250620.0",
"portless": "^0.10.1",
"typescript": "catalog:",
"vite": "catalog:",
Expand Down
12 changes: 9 additions & 3 deletions apps/cloud/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { setCookie } from "@tanstack/react-start/server";
import { addGroup } from "@executor/api";
import { CoreHandlers, ExecutorService, ExecutionEngineService } from "@executor/api/server";
import { createExecutionEngine } from "@executor/execution";
import { makeDynamicWorkerExecutor, type CodeExecutor } from "@executor/runtime-dynamic-worker";
import { OpenApiGroup, OpenApiExtensionService, OpenApiHandlers } from "@executor/plugin-openapi/api";
import { McpGroup, McpExtensionService, McpHandlers } from "@executor/plugin-mcp/api";
import {
Expand All @@ -31,7 +32,7 @@ import { CloudAuthHandlers, CloudAuthPublicHandlers } from "./auth/handlers";
import { WorkOSAuth } from "./auth/workos";
import { DbService } from "./services/db";
import { createTeamExecutor } from "./services/executor";
import { server } from "./env";
import { cf, server } from "./env";

const ProtectedCloudApi = addGroup(OpenApiGroup)
.add(McpGroup)
Expand Down Expand Up @@ -93,6 +94,7 @@ const parseCookie = (cookieHeader: string | null, name: string): string | null =
return match.slice(name.length + 1) || null;
};


const isPublicPath = (pathname: string): boolean =>
pathname === "/auth/login" || pathname === "/auth/callback";

Expand Down Expand Up @@ -167,8 +169,9 @@ const createProtectedHandler = (
auth: ResolvedAuth,
teamId: string,
executor: TeamExecutor,
codeExecutor: CodeExecutor,
) => {
const engine = createExecutionEngine({ executor });
const engine = createExecutionEngine({ executor, codeExecutor });

const requestServices = Layer.mergeAll(
Layer.succeed(AuthContext, {
Expand Down Expand Up @@ -230,7 +233,10 @@ export const handleApiRequest = async (request: Request): Promise<Response> => {
);

const handler = yield* Effect.acquireRelease(
Effect.sync(() => createProtectedHandler(auth, teamId, executor)),
Effect.sync(() => {
const codeExecutor = makeDynamicWorkerExecutor({ loader: cf.loader });
return createProtectedHandler(auth, teamId, executor, codeExecutor);
}),
disposeProtectedHandler,
);

Expand Down
11 changes: 11 additions & 0 deletions apps/cloud/src/env.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { env as cfEnv } from "cloudflare:workers";
import { createEnv, Env } from "@executor/env";

const sharedShape = {
Expand Down Expand Up @@ -51,3 +52,13 @@ export const server = createEnv(serverShape, {
runtimeEnv: process.env,
emptyStringAsUndefined: true,
}) as ServerEnv;

// ---------------------------------------------------------------------------
// Cloudflare bindings — single boundary for all platform-specific access
// ---------------------------------------------------------------------------

export const cf = {
hyperdrive: cfEnv.HYPERDRIVE,
loader: cfEnv.LOADER,
marketing: cfEnv.MARKETING,
} as const;
13 changes: 4 additions & 9 deletions apps/cloud/src/services/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Context, Effect, Layer } from "effect";
import * as sharedSchema from "@executor/storage-postgres/schema";
import * as cloudSchema from "./schema";
import type { DrizzleDb } from "@executor/storage-postgres";
import { server } from "../env";
import { cf, server } from "../env";

const schema = { ...sharedSchema, ...cloudSchema };

Expand All @@ -19,14 +19,9 @@ export type { DrizzleDb };
// Connection string resolution
// ---------------------------------------------------------------------------

const resolveHyperdriveUrl = Effect.tryPromise({
try: async () => {
const { env } = await import("cloudflare:workers");
const hyperdrive = (env as any).HYPERDRIVE;
return (hyperdrive?.connectionString as string) ?? null;
},
catch: () => null,
}).pipe(Effect.map((v) => v ?? undefined));
const resolveHyperdriveUrl = Effect.succeed(
cf.hyperdrive?.connectionString ?? undefined,
);

const resolveConnectionString = resolveHyperdriveUrl.pipe(
Effect.map((url) => url ?? (server.DATABASE_URL || undefined)),
Expand Down
12 changes: 3 additions & 9 deletions apps/cloud/src/start.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMiddleware, createStart } from "@tanstack/react-start";
import { handleApiRequest } from "./api";
import { cf } from "./env";

// ---------------------------------------------------------------------------
// Marketing routes — proxied to the marketing worker via service binding
Expand All @@ -10,14 +11,7 @@ const MARKETING_PATHS = ["/home", "/setup", "/api/detect", "/_astro", "/favicon.
const isMarketingPath = (pathname: string) =>
MARKETING_PATHS.some((p) => pathname === p || pathname.startsWith(`${p}/`));

const getMarketingWorker = async () => {
try {
const { env } = await import("cloudflare:workers");
return (env as any).MARKETING as { fetch: typeof fetch } | undefined;
} catch {
return undefined;
}
};
const getMarketingWorker = () => cf.marketing as { fetch: typeof fetch } | undefined;

const marketingMiddleware = createMiddleware({ type: "request" }).server(
async ({ pathname, request, next }) => {
Expand All @@ -27,7 +21,7 @@ const marketingMiddleware = createMiddleware({ type: "request" }).server(

if (!shouldProxyToMarketing) return next();

const marketing = await getMarketingWorker();
const marketing = getMarketingWorker();
if (!marketing) return next();

const url = new URL(request.url);
Expand Down
3 changes: 2 additions & 1 deletion apps/cloud/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["@cloudflare/workers-types"],
"outDir": "dist",
"rootDir": ".",
"declaration": false,
Expand All @@ -19,5 +20,5 @@
}
]
},
"include": ["src", "vite.config.ts"]
"include": ["src", "vite.config.ts", "worker-configuration.d.ts"]
}
1 change: 1 addition & 0 deletions apps/cloud/worker-configuration.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare namespace Cloudflare {
interface Env {
HYPERDRIVE: Hyperdrive;
LOADER: WorkerLoader;
MARKETING: Fetcher;
WORKOS_API_KEY: string;
WORKOS_CLIENT_ID: string;
Expand Down
Loading