From 74ef139ffea22c550284db34fb62a47ab550aa25 Mon Sep 17 00:00:00 2001 From: Maxwell Calkin Date: Sun, 8 Mar 2026 21:44:01 -0400 Subject: [PATCH] fix: use leftJoin for workflow in V1 log API endpoints The V1 public API log endpoints used innerJoin with the workflow table, which silently dropped logs for deleted workflows. The internal API endpoints already correctly use leftJoin. This aligns the V1 endpoints with that pattern. Changes: - v1/logs/route.ts: innerJoin -> leftJoin, handle null workflow fields - v1/logs/[id]/route.ts: innerJoin -> leftJoin, fix permissions join to use workflowExecutionLogs.workspaceId instead of workflow.workspaceId (which is null for deleted workflows), handle null workflow fields - v1/logs/executions/[executionId]/route.ts: same innerJoin -> leftJoin and permissions join fix Co-Authored-By: Claude Opus 4.6 --- apps/sim/app/api/v1/logs/[id]/route.ts | 38 ++++++++++++------- .../v1/logs/executions/[executionId]/route.ts | 4 +- apps/sim/app/api/v1/logs/route.ts | 6 +-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/apps/sim/app/api/v1/logs/[id]/route.ts b/apps/sim/app/api/v1/logs/[id]/route.ts index b1d8f89ff36..065746f1229 100644 --- a/apps/sim/app/api/v1/logs/[id]/route.ts +++ b/apps/sim/app/api/v1/logs/[id]/route.ts @@ -47,12 +47,12 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ workflowUpdatedAt: workflow.updatedAt, }) .from(workflowExecutionLogs) - .innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) + .leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) .innerJoin( permissions, and( eq(permissions.entityType, 'workspace'), - eq(permissions.entityId, workflow.workspaceId), + eq(permissions.entityId, workflowExecutionLogs.workspaceId), eq(permissions.userId, userId) ) ) @@ -64,17 +64,29 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ return NextResponse.json({ error: 'Log not found' }, { status: 404 }) } - const workflowSummary = { - id: log.workflowId, - name: log.workflowName, - description: log.workflowDescription, - color: log.workflowColor, - folderId: log.workflowFolderId, - userId: log.workflowUserId, - workspaceId: log.workflowWorkspaceId, - createdAt: log.workflowCreatedAt, - updatedAt: log.workflowUpdatedAt, - } + const workflowSummary = log.workflowName + ? { + id: log.workflowId, + name: log.workflowName, + description: log.workflowDescription, + color: log.workflowColor, + folderId: log.workflowFolderId, + userId: log.workflowUserId, + workspaceId: log.workflowWorkspaceId, + createdAt: log.workflowCreatedAt, + updatedAt: log.workflowUpdatedAt, + } + : { + id: log.workflowId, + name: 'Deleted Workflow', + description: null, + color: null, + folderId: null, + userId: null, + workspaceId: null, + createdAt: null, + updatedAt: null, + } const response = { id: log.id, diff --git a/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts b/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts index 5c2967ef735..fe3f339adae 100644 --- a/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts +++ b/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts @@ -34,12 +34,12 @@ export async function GET( workflow: workflow, }) .from(workflowExecutionLogs) - .innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) + .leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) .innerJoin( permissions, and( eq(permissions.entityType, 'workspace'), - eq(permissions.entityId, workflow.workspaceId), + eq(permissions.entityId, workflowExecutionLogs.workspaceId), eq(permissions.userId, userId) ) ) diff --git a/apps/sim/app/api/v1/logs/route.ts b/apps/sim/app/api/v1/logs/route.ts index 83a7b621923..2b1f8bb0353 100644 --- a/apps/sim/app/api/v1/logs/route.ts +++ b/apps/sim/app/api/v1/logs/route.ts @@ -123,7 +123,7 @@ export async function GET(request: NextRequest) { workflowDescription: workflow.description, }) .from(workflowExecutionLogs) - .innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) + .leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) .innerJoin( permissions, and( @@ -168,8 +168,8 @@ export async function GET(request: NextRequest) { if (params.details === 'full') { result.workflow = { id: log.workflowId, - name: log.workflowName, - description: log.workflowDescription, + name: log.workflowName ?? 'Deleted Workflow', + description: log.workflowDescription ?? null, } if (log.cost) {