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
1 change: 0 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"generate:master-key": "node scripts/generate-master-key.js"
},
"devDependencies": {
"@cloudflare/puppeteer": "^1.0.2",
"@cloudflare/vitest-pool-workers": "^0.8.58",
"@cloudflare/workers-types": "^4.20250726.0",
"@eslint/js": "^9.26.0",
Expand Down
1 change: 0 additions & 1 deletion apps/api/src/cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ async function executeWorkflow(
userId: "cron_trigger",
organizationId: workflowInfo.organizationId,
status: "executing" as ExecutionStatusType,
visibility: "private",
nodeExecutions,
createdAt: new Date(),
updatedAt: new Date(),
Expand Down
128 changes: 0 additions & 128 deletions apps/api/src/db/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ export type SaveExecutionRecord = {
organizationId: string;
status: ExecutionStatusType;
nodeExecutions: NodeExecution[];
visibility: "public" | "private";
error?: string;
createdAt?: Date;
updatedAt?: Date;
Expand Down Expand Up @@ -481,7 +480,6 @@ export async function saveExecution(
status: record.status as WorkflowExecutionStatus,
nodeExecutions,
error: record.error,
visibility: record.visibility,
startedAt: record.startedAt,
endedAt: record.endedAt,
};
Expand Down Expand Up @@ -914,25 +912,6 @@ export async function listExecutions(
return results.map((item) => item.executions);
}

/**
* Get a public execution by ID
*
* @param db Database instance
* @param id Execution ID
* @returns Execution record or undefined if not found or not public
*/
export async function getPublicExecution(
db: ReturnType<typeof createDatabase>,
id: string
): Promise<ExecutionRow | undefined> {
const [execution] = await db
.select()
.from(executions)
.where(and(eq(executions.id, id), eq(executions.visibility, "public")));

return execution;
}

/**
* Get workflow names by their IDs
*
Expand Down Expand Up @@ -977,113 +956,6 @@ export async function getWorkflowName(
return workflow?.name;
}

/**
* Update execution visibility to public
*
* @param db Database instance
* @param executionId Execution ID
* @param organizationId Organization ID
* @returns The updated execution record
*/
export async function updateExecutionToPublic(
db: ReturnType<typeof createDatabase>,
executionId: string,
organizationId: string
): Promise<ExecutionRow | undefined> {
const [execution] = await db
.update(executions)
.set({ visibility: "public", updatedAt: new Date() })
.where(
and(
eq(executions.id, executionId),
eq(executions.organizationId, organizationId)
)
)
.returning();

return execution;
}

/**
* Update execution visibility to private
*
* @param db Database instance
* @param executionId Execution ID
* @param organizationId Organization ID
* @returns The updated execution record
*/
export async function updateExecutionToPrivate(
db: ReturnType<typeof createDatabase>,
executionId: string,
organizationId: string
): Promise<ExecutionRow | undefined> {
const [execution] = await db
.update(executions)
.set({ visibility: "private", updatedAt: new Date() })
.where(
and(
eq(executions.id, executionId),
eq(executions.organizationId, organizationId)
)
)
.returning();

return execution;
}

/**
* Update execution OG image generation status
*
* @param db Database instance
* @param executionId Execution ID
* @returns The updated execution record
*/
export async function updateExecutionOgImageStatus(
db: ReturnType<typeof createDatabase>,
executionId: string
): Promise<ExecutionRow | undefined> {
const [execution] = await db
.update(executions)
.set({ ogImageGenerated: true, updatedAt: new Date() })
.where(eq(executions.id, executionId))
.returning();

return execution;
}

/**
* Get execution with visibility check
*
* @param db Database instance
* @param executionId Execution ID
* @param organizationId Organization ID
* @returns The execution record with visibility status
*/
export async function getExecutionWithVisibility(
db: ReturnType<typeof createDatabase>,
executionId: string,
organizationId: string
): Promise<
| { id: string; organizationId: string; ogImageGenerated: boolean | null }
| undefined
> {
const [execution] = await db
.select({
id: executions.id,
organizationId: executions.organizationId,
ogImageGenerated: executions.ogImageGenerated,
})
.from(executions)
.where(
and(
eq(executions.id, executionId),
eq(executions.organizationId, organizationId)
)
);

return execution;
}

/**
* Get a cron trigger.
*
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/db/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,16 +284,10 @@ export const executions = sqliteTable(
.$type<WorkflowExecutionType>()
.notNull(),
error: text("error"),
visibility: text("visibility", { enum: ["public", "private"] })
.notNull()
.default("private"),
startedAt: integer("started_at", { mode: "timestamp" }),
endedAt: integer("ended_at", { mode: "timestamp" }),
createdAt: createCreatedAt(),
updatedAt: createUpdatedAt(),
ogImageGenerated: integer("og_image_generated", {
mode: "boolean",
}).default(false),
},
(table) => [
index("executions_workflow_id_idx").on(table.workflowId),
Expand All @@ -303,7 +297,6 @@ export const executions = sqliteTable(
index("executions_created_at_idx").on(table.createdAt),
index("executions_started_at_idx").on(table.startedAt),
index("executions_ended_at_idx").on(table.endedAt),
index("executions_visibility_idx").on(table.visibility),
// Composite indexes for common query patterns
index("executions_organization_id_status_idx").on(
table.organizationId,
Expand Down
1 change: 0 additions & 1 deletion apps/api/src/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ export async function handleIncomingEmail(
userId: "email",
organizationId: workflow.organizationId,
status: ExecutionStatus.EXECUTING,
visibility: "private",
nodeExecutions,
createdAt: new Date(),
updatedAt: new Date(),
Expand Down
2 changes: 0 additions & 2 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import llmsRoutes from "./routes/llms";
import objectRoutes from "./routes/objects";
import organizationRoutes from "./routes/organizations";
import profileRoutes from "./routes/profile";
import publicRoutes from "./routes/public";
import robotsRoutes from "./routes/robots";
import secretRoutes from "./routes/secrets";
import typeRoutes from "./routes/types";
Expand Down Expand Up @@ -55,7 +54,6 @@ app.route("/robots.txt", robotsRoutes);
app.route("/llms.txt", llmsRoutes);

// Public routes
app.route("/public", publicRoutes);
app.route("/types", typeRoutes);

app.route("/:organizationIdOrHandle/api-keys", apiKeyRoutes);
Expand Down
1 change: 0 additions & 1 deletion apps/api/src/routes/deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,6 @@ deploymentRoutes.post(
userId,
organizationId,
status: executingStatus,
visibility: "private",
nodeExecutions,
createdAt: new Date(),
updatedAt: new Date(),
Expand Down
86 changes: 0 additions & 86 deletions apps/api/src/routes/executions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
GetExecutionResponse,
ListExecutionsRequest,
ListExecutionsResponse,
UpdateExecutionVisibilityResponse,
WorkflowExecution,
WorkflowExecutionStatus,
} from "@dafthunk/types";
Expand All @@ -13,15 +12,10 @@ import { ApiContext } from "../context";
import {
createDatabase,
getExecution,
getExecutionWithVisibility,
getWorkflowName,
getWorkflowNames,
listExecutions,
updateExecutionOgImageStatus,
updateExecutionToPrivate,
updateExecutionToPublic,
} from "../db";
import { generateExecutionOgImage } from "../utils/og-image-generator";

const executionRoutes = new Hono<ApiContext>();

Expand Down Expand Up @@ -53,7 +47,6 @@ executionRoutes.get("/:id", apiKeyOrJwtMiddleware, async (c) => {
status: execution.status as WorkflowExecutionStatus,
nodeExecutions: executionData.nodeExecutions || [],
error: execution.error || undefined,
visibility: execution.visibility,
startedAt: execution.startedAt ?? executionData.startedAt,
endedAt: execution.endedAt ?? executionData.endedAt,
};
Expand Down Expand Up @@ -102,7 +95,6 @@ executionRoutes.get("/", jwtMiddleware, async (c) => {
status: execution.status as WorkflowExecutionStatus,
nodeExecutions: executionData.nodeExecutions || [],
error: execution.error || undefined,
visibility: execution.visibility,
startedAt: execution.startedAt ?? executionData.startedAt,
endedAt: execution.endedAt ?? executionData.endedAt,
};
Expand All @@ -112,82 +104,4 @@ executionRoutes.get("/", jwtMiddleware, async (c) => {
return c.json(response);
});

executionRoutes.patch("/:id/share/public", jwtMiddleware, async (c) => {
const executionId = c.req.param("id");
const db = createDatabase(c.env.DB);
const organizationId = c.get("organizationId")!;

try {
const execution = await getExecutionWithVisibility(
db,
executionId,
organizationId
);

if (!execution) {
return c.json({ error: "Execution not found" }, 404);
}

await updateExecutionToPublic(db, executionId, organizationId);

if (!execution.ogImageGenerated && c.env.BROWSER) {
try {
await generateExecutionOgImage({
env: c.env,
executionId: executionId,
organizationId: organizationId,
});

await updateExecutionOgImageStatus(db, executionId);
console.log(
`Execution ${executionId} updated with OG image generation status.`
);
} catch (error) {
console.error(
`OG image generation step failed for execution ${executionId}, but execution is now public. Error: ${error}`
);
}
}

const response: UpdateExecutionVisibilityResponse = {
success: true,
message: "Execution set to public",
};
return c.json(response);
} catch (error) {
console.error("Error setting execution to public:", error);
return c.json({ error: "Failed to set execution to public" }, 500);
}
});

executionRoutes.patch("/:id/share/private", jwtMiddleware, async (c) => {
const id = c.req.param("id");
const db = createDatabase(c.env.DB);

const organizationId = c.get("organizationId")!;

try {
const execution = await getExecution(db, id, organizationId);

if (!execution) {
return c.json({ error: "Execution not found" }, 404);
}

// The check execution.organizationId !== orgId is implicitly handled by getExecutionById
// if it correctly filters by organizationId. If not, this check might be needed here.
// However, getExecutionById already takes orgId, so it should be fine.

await updateExecutionToPrivate(db, id, organizationId);

const response: UpdateExecutionVisibilityResponse = {
success: true,
message: "Execution set to private",
};
return c.json(response);
} catch (error) {
console.error("Error setting execution to private:", error);
return c.json({ error: "Failed to set execution to private" }, 500);
}
});

export default executionRoutes;
17 changes: 6 additions & 11 deletions apps/api/src/routes/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ objectRoutes.get("/", apiKeyOrJwtMiddleware, async (c) => {
const db = createDatabase(c.env.DB);
const [execution] = await db
.select({
visibility: executionsTable.visibility,
organizationId: executionsTable.organizationId,
})
.from(executionsTable)
Expand All @@ -58,17 +57,13 @@ objectRoutes.get("/", apiKeyOrJwtMiddleware, async (c) => {
return c.text("Object not found or linked to invalid execution", 404);
}

if (execution.visibility === "private") {
// For private executions, the execution's organization must match the requesting organization
if (execution.organizationId !== requestingOrganizationId) {
return c.text(
"Forbidden: You do not have access to this object via its execution",
403
);
}
// For private executions, the execution's organization must match the requesting organization
if (execution.organizationId !== requestingOrganizationId) {
return c.text(
"Forbidden: You do not have access to this object via its execution",
403
);
}
// If execution is public, access is allowed for any authenticated entity (JWT user or API key).
// No further check against requestingOrganizationId is needed here for public executions.
} else {
// Object not linked to an execution, check its own organizationId
if (metadata?.organizationId !== requestingOrganizationId) {
Expand Down
Loading