Skip to content

Commit 8e8bca7

Browse files
committed
do not cascade delete logs/snapshots
1 parent fec6074 commit 8e8bca7

File tree

21 files changed

+10519
-94
lines changed

21 files changed

+10519
-94
lines changed

apps/sim/app/api/logs/[id]/route.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
5656
deploymentVersionName: workflowDeploymentVersion.name,
5757
})
5858
.from(workflowExecutionLogs)
59-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
59+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
6060
.leftJoin(
6161
workflowDeploymentVersion,
6262
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
@@ -65,7 +65,7 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
6565
permissions,
6666
and(
6767
eq(permissions.entityType, 'workspace'),
68-
eq(permissions.entityId, workflow.workspaceId),
68+
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
6969
eq(permissions.userId, userId)
7070
)
7171
)
@@ -77,17 +77,19 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
7777
return NextResponse.json({ error: 'Not found' }, { status: 404 })
7878
}
7979

80-
const workflowSummary = {
81-
id: log.workflowId,
82-
name: log.workflowName,
83-
description: log.workflowDescription,
84-
color: log.workflowColor,
85-
folderId: log.workflowFolderId,
86-
userId: log.workflowUserId,
87-
workspaceId: log.workflowWorkspaceId,
88-
createdAt: log.workflowCreatedAt,
89-
updatedAt: log.workflowUpdatedAt,
90-
}
80+
const workflowSummary = log.workflowId
81+
? {
82+
id: log.workflowId,
83+
name: log.workflowName,
84+
description: log.workflowDescription,
85+
color: log.workflowColor,
86+
folderId: log.workflowFolderId,
87+
userId: log.workflowUserId,
88+
workspaceId: log.workflowWorkspaceId,
89+
createdAt: log.workflowCreatedAt,
90+
updatedAt: log.workflowUpdatedAt,
91+
}
92+
: null
9193

9294
const response = {
9395
id: log.id,

apps/sim/app/api/logs/cleanup/route.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { db } from '@sim/db'
2-
import { subscription, user, workflow, workflowExecutionLogs } from '@sim/db/schema'
2+
import { subscription, user, workflowExecutionLogs, workspace } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { and, eq, inArray, lt, sql } from 'drizzle-orm'
55
import { type NextRequest, NextResponse } from 'next/server'
@@ -40,17 +40,17 @@ export async function GET(request: NextRequest) {
4040

4141
const freeUserIds = freeUsers.map((u) => u.userId)
4242

43-
const workflowsQuery = await db
44-
.select({ id: workflow.id })
45-
.from(workflow)
46-
.where(inArray(workflow.userId, freeUserIds))
43+
const workspacesQuery = await db
44+
.select({ id: workspace.id })
45+
.from(workspace)
46+
.where(inArray(workspace.billedAccountUserId, freeUserIds))
4747

48-
if (workflowsQuery.length === 0) {
49-
logger.info('No workflows found for free users')
50-
return NextResponse.json({ message: 'No workflows found for cleanup' })
48+
if (workspacesQuery.length === 0) {
49+
logger.info('No workspaces found for free users')
50+
return NextResponse.json({ message: 'No workspaces found for cleanup' })
5151
}
5252

53-
const workflowIds = workflowsQuery.map((w) => w.id)
53+
const workspaceIds = workspacesQuery.map((w) => w.id)
5454

5555
const results = {
5656
enhancedLogs: {
@@ -77,7 +77,7 @@ export async function GET(request: NextRequest) {
7777
let batchesProcessed = 0
7878
let hasMoreLogs = true
7979

80-
logger.info(`Starting enhanced logs cleanup for ${workflowIds.length} workflows`)
80+
logger.info(`Starting enhanced logs cleanup for ${workspaceIds.length} workspaces`)
8181

8282
while (hasMoreLogs && batchesProcessed < MAX_BATCHES) {
8383
const oldEnhancedLogs = await db
@@ -99,7 +99,7 @@ export async function GET(request: NextRequest) {
9999
.from(workflowExecutionLogs)
100100
.where(
101101
and(
102-
inArray(workflowExecutionLogs.workflowId, workflowIds),
102+
inArray(workflowExecutionLogs.workspaceId, workspaceIds),
103103
lt(workflowExecutionLogs.createdAt, retentionDate)
104104
)
105105
)
@@ -127,7 +127,7 @@ export async function GET(request: NextRequest) {
127127
customKey: enhancedLogKey,
128128
metadata: {
129129
logId: String(log.id),
130-
workflowId: String(log.workflowId),
130+
workflowId: String(log.workflowId ?? ''),
131131
executionId: String(log.executionId),
132132
logType: 'enhanced',
133133
archivedAt: new Date().toISOString(),

apps/sim/app/api/logs/execution/[executionId]/route.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ export async function GET(
5252
executionData: workflowExecutionLogs.executionData,
5353
})
5454
.from(workflowExecutionLogs)
55-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
55+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
5656
.innerJoin(
5757
permissions,
5858
and(
5959
eq(permissions.entityType, 'workspace'),
60-
eq(permissions.entityId, workflow.workspaceId),
60+
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
6161
eq(permissions.userId, authenticatedUserId)
6262
)
6363
)
@@ -89,9 +89,8 @@ export async function GET(
8989
if (typeof snapshotId === 'string') {
9090
childSnapshotIds.add(snapshotId)
9191
}
92-
const children = span.children
93-
if (Array.isArray(children)) {
94-
collectSnapshotIds(children)
92+
if (span.children?.length) {
93+
collectSnapshotIds(span.children)
9594
}
9695
})
9796
}

apps/sim/app/api/logs/export/route.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '@sim/db'
22
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
4-
import { and, desc, eq } from 'drizzle-orm'
4+
import { and, desc, eq, sql } from 'drizzle-orm'
55
import { type NextRequest, NextResponse } from 'next/server'
66
import { getSession } from '@/lib/auth'
77
import { buildFilterConditions, LogFilterParamsSchema } from '@/lib/logs/filters'
@@ -41,7 +41,7 @@ export async function GET(request: NextRequest) {
4141
totalDurationMs: workflowExecutionLogs.totalDurationMs,
4242
cost: workflowExecutionLogs.cost,
4343
executionData: workflowExecutionLogs.executionData,
44-
workflowName: workflow.name,
44+
workflowName: sql<string>`COALESCE(${workflow.name}, 'Deleted Workflow')`,
4545
}
4646

4747
const workspaceCondition = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
@@ -74,7 +74,7 @@ export async function GET(request: NextRequest) {
7474
const rows = await db
7575
.select(selectColumns)
7676
.from(workflowExecutionLogs)
77-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
77+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
7878
.innerJoin(
7979
permissions,
8080
and(

apps/sim/app/api/logs/route.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export async function GET(request: NextRequest) {
116116
workflowDeploymentVersion,
117117
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
118118
)
119-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
119+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
120120
.innerJoin(
121121
permissions,
122122
and(
@@ -190,7 +190,7 @@ export async function GET(request: NextRequest) {
190190
pausedExecutions,
191191
eq(pausedExecutions.executionId, workflowExecutionLogs.executionId)
192192
)
193-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
193+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
194194
.innerJoin(
195195
permissions,
196196
and(
@@ -314,17 +314,19 @@ export async function GET(request: NextRequest) {
314314
} catch {}
315315
}
316316

317-
const workflowSummary = {
318-
id: log.workflowId,
319-
name: log.workflowName,
320-
description: log.workflowDescription,
321-
color: log.workflowColor,
322-
folderId: log.workflowFolderId,
323-
userId: log.workflowUserId,
324-
workspaceId: log.workflowWorkspaceId,
325-
createdAt: log.workflowCreatedAt,
326-
updatedAt: log.workflowUpdatedAt,
327-
}
317+
const workflowSummary = log.workflowId
318+
? {
319+
id: log.workflowId,
320+
name: log.workflowName,
321+
description: log.workflowDescription,
322+
color: log.workflowColor,
323+
folderId: log.workflowFolderId,
324+
userId: log.workflowUserId,
325+
workspaceId: log.workflowWorkspaceId,
326+
createdAt: log.workflowCreatedAt,
327+
updatedAt: log.workflowUpdatedAt,
328+
}
329+
: null
328330

329331
return {
330332
id: log.id,

apps/sim/app/api/logs/stats/route.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export async function GET(request: NextRequest) {
7272
maxTime: sql<string>`MAX(${workflowExecutionLogs.startedAt})`,
7373
})
7474
.from(workflowExecutionLogs)
75-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
75+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
7676
.innerJoin(
7777
permissions,
7878
and(
@@ -103,8 +103,8 @@ export async function GET(request: NextRequest) {
103103

104104
const statsQuery = await db
105105
.select({
106-
workflowId: workflowExecutionLogs.workflowId,
107-
workflowName: workflow.name,
106+
workflowId: sql<string>`COALESCE(${workflowExecutionLogs.workflowId}, 'deleted')`,
107+
workflowName: sql<string>`COALESCE(${workflow.name}, 'Deleted Workflow')`,
108108
segmentIndex:
109109
sql<number>`FLOOR(EXTRACT(EPOCH FROM (${workflowExecutionLogs.startedAt} - ${startTimeIso}::timestamp)) * 1000 / ${segmentMs})`.as(
110110
'segment_index'
@@ -120,7 +120,7 @@ export async function GET(request: NextRequest) {
120120
),
121121
})
122122
.from(workflowExecutionLogs)
123-
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
123+
.leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
124124
.innerJoin(
125125
permissions,
126126
and(
@@ -130,7 +130,11 @@ export async function GET(request: NextRequest) {
130130
)
131131
)
132132
.where(whereCondition)
133-
.groupBy(workflowExecutionLogs.workflowId, workflow.name, sql`segment_index`)
133+
.groupBy(
134+
sql`COALESCE(${workflowExecutionLogs.workflowId}, 'deleted')`,
135+
sql`COALESCE(${workflow.name}, 'Deleted Workflow')`,
136+
sql`segment_index`
137+
)
134138

135139
const workflowMap = new Map<
136140
string,

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/workflows-list/workflows-list.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { memo } from 'react'
22
import { cn } from '@/lib/core/utils/cn'
3+
import {
4+
DELETED_WORKFLOW_COLOR,
5+
DELETED_WORKFLOW_LABEL,
6+
} from '@/app/workspace/[workspaceId]/logs/utils'
37
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
48
import { StatusBar, type StatusBarSegment } from '..'
59

@@ -61,22 +65,32 @@ export function WorkflowsList({
6165
<div>
6266
{filteredExecutions.map((workflow, idx) => {
6367
const isSelected = expandedWorkflowId === workflow.workflowId
68+
const isDeletedWorkflow = workflow.workflowName === DELETED_WORKFLOW_LABEL
69+
const workflowColor = isDeletedWorkflow
70+
? DELETED_WORKFLOW_COLOR
71+
: workflows[workflow.workflowId]?.color
72+
const canToggle = !isDeletedWorkflow
6473

6574
return (
6675
<div
6776
key={workflow.workflowId}
6877
className={cn(
69-
'flex h-[44px] cursor-pointer items-center gap-[16px] px-[24px] hover:bg-[var(--surface-3)] dark:hover:bg-[var(--surface-4)]',
78+
'flex h-[44px] items-center gap-[16px] px-[24px] hover:bg-[var(--surface-3)] dark:hover:bg-[var(--surface-4)]',
79+
canToggle ? 'cursor-pointer' : 'cursor-default',
7080
isSelected && 'bg-[var(--surface-3)] dark:bg-[var(--surface-4)]'
7181
)}
72-
onClick={() => onToggleWorkflow(workflow.workflowId)}
82+
onClick={() => {
83+
if (canToggle) {
84+
onToggleWorkflow(workflow.workflowId)
85+
}
86+
}}
7387
>
7488
{/* Workflow name with color */}
7589
<div className='flex w-[160px] flex-shrink-0 items-center gap-[8px] pr-[8px]'>
7690
<div
7791
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
7892
style={{
79-
backgroundColor: workflows[workflow.workflowId]?.color || '#64748b',
93+
backgroundColor: workflowColor,
8094
}}
8195
/>
8296
<span className='min-w-0 truncate font-medium text-[12px] text-[var(--text-primary)]'>

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {
2626
} from '@/app/workspace/[workspaceId]/logs/components'
2727
import { useLogDetailsResize } from '@/app/workspace/[workspaceId]/logs/hooks'
2828
import {
29+
DELETED_WORKFLOW_COLOR,
30+
DELETED_WORKFLOW_LABEL,
2931
formatDate,
3032
getDisplayStatus,
3133
StatusBadge,
@@ -386,22 +388,25 @@ export const LogDetails = memo(function LogDetails({
386388
</div>
387389

388390
{/* Workflow Card */}
389-
{log.workflow && (
390-
<div className='flex w-0 min-w-0 flex-1 flex-col gap-[8px]'>
391-
<div className='font-medium text-[12px] text-[var(--text-tertiary)]'>
392-
Workflow
393-
</div>
394-
<div className='flex min-w-0 items-center gap-[8px]'>
395-
<div
396-
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
397-
style={{ backgroundColor: log.workflow?.color }}
398-
/>
399-
<span className='min-w-0 flex-1 truncate font-medium text-[14px] text-[var(--text-secondary)]'>
400-
{log.workflow.name}
401-
</span>
402-
</div>
391+
<div className='flex w-0 min-w-0 flex-1 flex-col gap-[8px]'>
392+
<div className='font-medium text-[12px] text-[var(--text-tertiary)]'>
393+
Workflow
403394
</div>
404-
)}
395+
<div className='flex min-w-0 items-center gap-[8px]'>
396+
<div
397+
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
398+
style={{
399+
backgroundColor:
400+
log.workflow?.color ||
401+
(!log.workflowId ? DELETED_WORKFLOW_COLOR : undefined),
402+
}}
403+
/>
404+
<span className='min-w-0 flex-1 truncate font-medium text-[14px] text-[var(--text-secondary)]'>
405+
{log.workflow?.name ||
406+
(!log.workflowId ? DELETED_WORKFLOW_LABEL : 'Unknown')}
407+
</span>
408+
</div>
409+
</div>
405410
</div>
406411

407412
{/* Execution ID */}

apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { List, type RowComponentProps, useListRef } from 'react-window'
77
import { Badge, buttonVariants } from '@/components/emcn'
88
import { cn } from '@/lib/core/utils/cn'
99
import {
10+
DELETED_WORKFLOW_COLOR,
11+
DELETED_WORKFLOW_LABEL,
1012
formatDate,
1113
formatDuration,
1214
getDisplayStatus,
@@ -33,6 +35,11 @@ interface LogRowProps {
3335
const LogRow = memo(
3436
function LogRow({ log, isSelected, onClick, onContextMenu, selectedRowRef }: LogRowProps) {
3537
const formattedDate = useMemo(() => formatDate(log.createdAt), [log.createdAt])
38+
const isDeletedWorkflow = !log.workflow?.id && !log.workflowId
39+
const workflowName = isDeletedWorkflow
40+
? DELETED_WORKFLOW_LABEL
41+
: log.workflow?.name || 'Unknown'
42+
const workflowColor = isDeletedWorkflow ? DELETED_WORKFLOW_COLOR : log.workflow?.color
3643

3744
const handleClick = useCallback(() => onClick(log), [onClick, log])
3845

@@ -78,10 +85,15 @@ const LogRow = memo(
7885
>
7986
<div
8087
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
81-
style={{ backgroundColor: log.workflow?.color }}
88+
style={{ backgroundColor: workflowColor }}
8289
/>
83-
<span className='min-w-0 truncate font-medium text-[12px] text-[var(--text-primary)]'>
84-
{log.workflow?.name || 'Unknown'}
90+
<span
91+
className={cn(
92+
'min-w-0 truncate font-medium text-[12px]',
93+
isDeletedWorkflow ? 'text-[var(--text-tertiary)]' : 'text-[var(--text-primary)]'
94+
)}
95+
>
96+
{workflowName}
8597
</span>
8698
</div>
8799

apps/sim/app/workspace/[workspaceId]/logs/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export const LOG_COLUMN_ORDER: readonly LogColumnKey[] = [
2727
'duration',
2828
] as const
2929

30+
export const DELETED_WORKFLOW_LABEL = 'Deleted Workflow'
31+
export const DELETED_WORKFLOW_COLOR = 'var(--text-tertiary)'
32+
3033
export type LogStatus = 'error' | 'pending' | 'running' | 'info' | 'cancelled'
3134

3235
/**

0 commit comments

Comments
 (0)