From cbab38b283c98d4caed56f75d9b82a2acb5a0db4 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 27 Mar 2026 13:04:13 -0700 Subject: [PATCH] fix(import): dedup workflow name --- apps/sim/app/api/workflows/route.ts | 76 ++++++++++--------- .../app/workspace/[workspaceId]/home/home.tsx | 1 + .../w/hooks/use-import-workflow.ts | 1 + .../w/hooks/use-import-workspace.ts | 1 + apps/sim/hooks/queries/workflows.ts | 5 +- 5 files changed, 49 insertions(+), 35 deletions(-) diff --git a/apps/sim/app/api/workflows/route.ts b/apps/sim/app/api/workflows/route.ts index 3181185b75e..4dc1d85a9c0 100644 --- a/apps/sim/app/api/workflows/route.ts +++ b/apps/sim/app/api/workflows/route.ts @@ -8,7 +8,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { getNextWorkflowColor } from '@/lib/workflows/colors' -import { listWorkflows, type WorkflowScope } from '@/lib/workflows/utils' +import { deduplicateWorkflowName, listWorkflows, type WorkflowScope } from '@/lib/workflows/utils' import { getUserEntityPermissions, workspaceExists } from '@/lib/workspaces/permissions/utils' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' @@ -25,6 +25,7 @@ const CreateWorkflowSchema = z.object({ workspaceId: z.string().optional(), folderId: z.string().nullable().optional(), sortOrder: z.number().int().optional(), + deduplicate: z.boolean().optional(), }) // GET /api/workflows - Get workflows for user (optionally filtered by workspaceId) @@ -126,12 +127,13 @@ export async function POST(req: NextRequest) { const body = await req.json() const { id: clientId, - name, + name: requestedName, description, color, workspaceId, folderId, sortOrder: providedSortOrder, + deduplicate, } = CreateWorkflowSchema.parse(body) if (!workspaceId) { @@ -162,19 +164,6 @@ export async function POST(req: NextRequest) { logger.info(`[${requestId}] Creating workflow ${workflowId} for user ${userId}`) - import('@/lib/core/telemetry') - .then(({ PlatformEvents }) => { - PlatformEvents.workflowCreated({ - workflowId, - name, - workspaceId: workspaceId || undefined, - folderId: folderId || undefined, - }) - }) - .catch(() => { - // Silently fail - }) - let sortOrder: number if (providedSortOrder !== undefined) { sortOrder = providedSortOrder @@ -214,31 +203,50 @@ export async function POST(req: NextRequest) { sortOrder = minSortOrder != null ? minSortOrder - 1 : 0 } - const duplicateConditions = [ - eq(workflow.workspaceId, workspaceId), - isNull(workflow.archivedAt), - eq(workflow.name, name), - ] + let name = requestedName - if (folderId) { - duplicateConditions.push(eq(workflow.folderId, folderId)) + if (deduplicate) { + name = await deduplicateWorkflowName(requestedName, workspaceId, folderId) } else { - duplicateConditions.push(isNull(workflow.folderId)) - } + const duplicateConditions = [ + eq(workflow.workspaceId, workspaceId), + isNull(workflow.archivedAt), + eq(workflow.name, requestedName), + ] + + if (folderId) { + duplicateConditions.push(eq(workflow.folderId, folderId)) + } else { + duplicateConditions.push(isNull(workflow.folderId)) + } - const [duplicateWorkflow] = await db - .select({ id: workflow.id }) - .from(workflow) - .where(and(...duplicateConditions)) - .limit(1) + const [duplicateWorkflow] = await db + .select({ id: workflow.id }) + .from(workflow) + .where(and(...duplicateConditions)) + .limit(1) - if (duplicateWorkflow) { - return NextResponse.json( - { error: `A workflow named "${name}" already exists in this folder` }, - { status: 409 } - ) + if (duplicateWorkflow) { + return NextResponse.json( + { error: `A workflow named "${requestedName}" already exists in this folder` }, + { status: 409 } + ) + } } + import('@/lib/core/telemetry') + .then(({ PlatformEvents }) => { + PlatformEvents.workflowCreated({ + workflowId, + name, + workspaceId: workspaceId || undefined, + folderId: folderId || undefined, + }) + }) + .catch(() => { + // Silently fail + }) + await db.insert(workflow).values({ id: workflowId, userId, diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index 001489783ae..87e8f126b80 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -54,6 +54,7 @@ export function Home({ chatId }: HomeProps = {}) { description, color, workspaceId, + deduplicate: true, }), }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts index 5be5a661268..c7f461b71f6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workflow.ts @@ -56,6 +56,7 @@ export function useImportWorkflow({ workspaceId }: UseImportWorkflowProps) { workspaceId, folderId, sortOrder, + deduplicate: true, }), }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts index 24f8d35c4ae..6b6b5df81ac 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/hooks/use-import-workspace.ts @@ -176,6 +176,7 @@ export function useImportWorkspace({ onSuccess }: UseImportWorkspaceProps = {}) color: workflowColor, workspaceId: newWorkspace.id, folderId: targetFolderId, + deduplicate: true, }), }) diff --git a/apps/sim/hooks/queries/workflows.ts b/apps/sim/hooks/queries/workflows.ts index 16ceb2c9822..c2ae3a40363 100644 --- a/apps/sim/hooks/queries/workflows.ts +++ b/apps/sim/hooks/queries/workflows.ts @@ -164,6 +164,7 @@ interface CreateWorkflowVariables { folderId?: string | null sortOrder?: number id?: string + deduplicate?: boolean } interface CreateWorkflowResult { @@ -300,7 +301,8 @@ export function useCreateWorkflow() { return useMutation({ mutationFn: async (variables: CreateWorkflowVariables): Promise => { - const { workspaceId, name, description, color, folderId, sortOrder, id } = variables + const { workspaceId, name, description, color, folderId, sortOrder, id, deduplicate } = + variables logger.info(`Creating new workflow in workspace: ${workspaceId}`) @@ -315,6 +317,7 @@ export function useCreateWorkflow() { workspaceId, folderId: folderId || null, sortOrder, + deduplicate, }), })