diff --git a/apps/sim/app/api/copilot/checkpoints/revert/route.test.ts b/apps/sim/app/api/copilot/checkpoints/revert/route.test.ts index 3cd75ccd0b..d1fdb6d526 100644 --- a/apps/sim/app/api/copilot/checkpoints/revert/route.test.ts +++ b/apps/sim/app/api/copilot/checkpoints/revert/route.test.ts @@ -237,7 +237,6 @@ describe('Copilot Checkpoints Revert API Route', () => { parallels: {}, isDeployed: true, deploymentStatuses: { production: 'deployed' }, - hasActiveWebhook: false, }, } @@ -287,7 +286,6 @@ describe('Copilot Checkpoints Revert API Route', () => { parallels: {}, isDeployed: true, deploymentStatuses: { production: 'deployed' }, - hasActiveWebhook: false, lastSaved: 1640995200000, }, }, @@ -309,7 +307,6 @@ describe('Copilot Checkpoints Revert API Route', () => { parallels: {}, isDeployed: true, deploymentStatuses: { production: 'deployed' }, - hasActiveWebhook: false, lastSaved: 1640995200000, }), } @@ -445,7 +442,6 @@ describe('Copilot Checkpoints Revert API Route', () => { parallels: {}, isDeployed: false, deploymentStatuses: {}, - hasActiveWebhook: false, lastSaved: 1640995200000, }) }) @@ -722,7 +718,6 @@ describe('Copilot Checkpoints Revert API Route', () => { production: 'deployed', staging: 'pending', }, - hasActiveWebhook: true, deployedAt: '2024-01-01T10:00:00.000Z', }, } @@ -769,7 +764,6 @@ describe('Copilot Checkpoints Revert API Route', () => { production: 'deployed', staging: 'pending', }, - hasActiveWebhook: true, deployedAt: '2024-01-01T10:00:00.000Z', lastSaved: 1640995200000, }) diff --git a/apps/sim/app/api/copilot/checkpoints/revert/route.ts b/apps/sim/app/api/copilot/checkpoints/revert/route.ts index a6c70de3fd..c3e5f0fb70 100644 --- a/apps/sim/app/api/copilot/checkpoints/revert/route.ts +++ b/apps/sim/app/api/copilot/checkpoints/revert/route.ts @@ -73,7 +73,6 @@ export async function POST(request: NextRequest) { parallels: checkpointState?.parallels || {}, isDeployed: checkpointState?.isDeployed || false, deploymentStatuses: checkpointState?.deploymentStatuses || {}, - hasActiveWebhook: checkpointState?.hasActiveWebhook || false, lastSaved: Date.now(), // Only include deployedAt if it's a valid date string that can be converted ...(checkpointState?.deployedAt && diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts index 41434061a7..01c0e0705b 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts @@ -76,7 +76,6 @@ export async function POST( isDeployed: true, deployedAt: new Date(), deploymentStatuses: deployedState.deploymentStatuses || {}, - hasActiveWebhook: deployedState.hasActiveWebhook || false, }) if (!saveResult.success) { diff --git a/apps/sim/app/api/workflows/[id]/route.ts b/apps/sim/app/api/workflows/[id]/route.ts index 26e92cef98..aa7b31d9ba 100644 --- a/apps/sim/app/api/workflows/[id]/route.ts +++ b/apps/sim/app/api/workflows/[id]/route.ts @@ -133,7 +133,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ state: { // Default values for expected properties deploymentStatuses: {}, - hasActiveWebhook: false, // Data from normalized tables blocks: normalizedData.blocks, edges: normalizedData.edges, diff --git a/apps/sim/app/api/workflows/[id]/state/route.ts b/apps/sim/app/api/workflows/[id]/state/route.ts index 3d593339d9..c8509bb900 100644 --- a/apps/sim/app/api/workflows/[id]/state/route.ts +++ b/apps/sim/app/api/workflows/[id]/state/route.ts @@ -89,13 +89,6 @@ const ParallelSchema = z.object({ parallelType: z.enum(['count', 'collection']).optional(), }) -const DeploymentStatusSchema = z.object({ - id: z.string(), - status: z.enum(['deploying', 'deployed', 'failed', 'stopping', 'stopped']), - deployedAt: z.date().optional(), - error: z.string().optional(), -}) - const WorkflowStateSchema = z.object({ blocks: z.record(BlockStateSchema), edges: z.array(EdgeSchema), @@ -103,9 +96,7 @@ const WorkflowStateSchema = z.object({ parallels: z.record(ParallelSchema).optional(), lastSaved: z.number().optional(), isDeployed: z.boolean().optional(), - deployedAt: z.date().optional(), - deploymentStatuses: z.record(DeploymentStatusSchema).optional(), - hasActiveWebhook: z.boolean().optional(), + deployedAt: z.coerce.date().optional(), }) /** @@ -204,8 +195,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ lastSaved: state.lastSaved || Date.now(), isDeployed: state.isDeployed || false, deployedAt: state.deployedAt, - deploymentStatuses: state.deploymentStatuses || {}, - hasActiveWebhook: state.hasActiveWebhook || false, } const saveResult = await saveWorkflowToNormalizedTables(workflowId, workflowState as any) diff --git a/apps/sim/app/api/workflows/yaml/export/route.ts b/apps/sim/app/api/workflows/yaml/export/route.ts index 198b8203d4..284da1bf6e 100644 --- a/apps/sim/app/api/workflows/yaml/export/route.ts +++ b/apps/sim/app/api/workflows/yaml/export/route.ts @@ -89,7 +89,6 @@ export async function GET(request: NextRequest) { // Use normalized table data - construct state from normalized tables workflowState = { deploymentStatuses: {}, - hasActiveWebhook: false, blocks: normalizedData.blocks, edges: normalizedData.edges, loops: normalizedData.loops, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls.tsx index b7b8a55e59..db334379c3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls.tsx @@ -86,7 +86,6 @@ export function DiffControls() { lastSaved: rawState.lastSaved || Date.now(), isDeployed: rawState.isDeployed || false, deploymentStatuses: rawState.deploymentStatuses || {}, - hasActiveWebhook: rawState.hasActiveWebhook || false, // Only include deployedAt if it's a valid date, never include null/undefined ...(rawState.deployedAt && rawState.deployedAt instanceof Date ? { deployedAt: rawState.deployedAt } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts index ed91e1ba58..2190aca88d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts @@ -19,7 +19,6 @@ export interface CurrentWorkflow { deployedAt?: Date deploymentStatuses?: Record needsRedeployment?: boolean - hasActiveWebhook?: boolean // Mode information isDiffMode: boolean @@ -66,7 +65,6 @@ export function useCurrentWorkflow(): CurrentWorkflow { deployedAt: activeWorkflow.deployedAt, deploymentStatuses: activeWorkflow.deploymentStatuses, needsRedeployment: activeWorkflow.needsRedeployment, - hasActiveWebhook: activeWorkflow.hasActiveWebhook, // Mode information - update to reflect ready state isDiffMode: shouldUseDiff, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout.ts index 49ff7b0f33..2a5775ac3d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout.ts @@ -204,16 +204,19 @@ export async function applyAutoLayoutAndUpdateStore( useWorkflowStore.getState().updateLastSaved() // Clean up the workflow state for API validation + // Destructure out UI-only fields that shouldn't be persisted + const { deploymentStatuses, needsRedeployment, dragStartPosition, ...stateToSave } = + newWorkflowState + const cleanedWorkflowState = { - ...newWorkflowState, + ...stateToSave, // Convert null dates to undefined (since they're optional) - deployedAt: newWorkflowState.deployedAt ? new Date(newWorkflowState.deployedAt) : undefined, + deployedAt: stateToSave.deployedAt ? new Date(stateToSave.deployedAt) : undefined, // Ensure other optional fields are properly handled - loops: newWorkflowState.loops || {}, - parallels: newWorkflowState.parallels || {}, - deploymentStatuses: newWorkflowState.deploymentStatuses || {}, + loops: stateToSave.loops || {}, + parallels: stateToSave.parallels || {}, // Sanitize edges: remove null/empty handle fields to satisfy schema (optional strings) - edges: (newWorkflowState.edges || []).map((edge: any) => { + edges: (stateToSave.edges || []).map((edge: any) => { const { sourceHandle, targetHandle, ...rest } = edge || {} const sanitized: any = { ...rest } if (typeof sourceHandle === 'string' && sourceHandle.length > 0) { diff --git a/apps/sim/contexts/socket-context.tsx b/apps/sim/contexts/socket-context.tsx index 991f264912..0029d24721 100644 --- a/apps/sim/contexts/socket-context.tsx +++ b/apps/sim/contexts/socket-context.tsx @@ -382,7 +382,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) { isDeployed: workflowState.isDeployed ?? false, deployedAt: workflowState.deployedAt, deploymentStatuses: workflowState.deploymentStatuses || {}, - hasActiveWebhook: workflowState.hasActiveWebhook ?? false, }) // Replace subblock store values for this workflow diff --git a/apps/sim/hooks/use-collaborative-workflow.ts b/apps/sim/hooks/use-collaborative-workflow.ts index 5400d102a3..e0a67bfea0 100644 --- a/apps/sim/hooks/use-collaborative-workflow.ts +++ b/apps/sim/hooks/use-collaborative-workflow.ts @@ -479,7 +479,6 @@ export function useCollaborativeWorkflow() { isDeployed: workflowData.state.isDeployed || false, deployedAt: workflowData.state.deployedAt, lastSaved: workflowData.state.lastSaved || Date.now(), - hasActiveWebhook: workflowData.state.hasActiveWebhook || false, deploymentStatuses: workflowData.state.deploymentStatuses || {}, }) diff --git a/apps/sim/lib/workflows/db-helpers.test.ts b/apps/sim/lib/workflows/db-helpers.test.ts index 24d1e2bf27..88e712703c 100644 --- a/apps/sim/lib/workflows/db-helpers.test.ts +++ b/apps/sim/lib/workflows/db-helpers.test.ts @@ -214,7 +214,6 @@ const mockWorkflowState: WorkflowState = { lastSaved: Date.now(), isDeployed: false, deploymentStatuses: {}, - hasActiveWebhook: false, } describe('Database Helpers', () => { @@ -452,11 +451,6 @@ describe('Database Helpers', () => { ) expect(result.success).toBe(true) - expect(result.jsonBlob).toBeDefined() - expect(result.jsonBlob.blocks).toEqual(mockWorkflowState.blocks) - expect(result.jsonBlob.edges).toEqual(mockWorkflowState.edges) - expect(result.jsonBlob.loops).toEqual(mockWorkflowState.loops) - expect(result.jsonBlob.parallels).toEqual(mockWorkflowState.parallels) // Verify transaction was called expect(mockTransaction).toHaveBeenCalledTimes(1) @@ -471,7 +465,6 @@ describe('Database Helpers', () => { lastSaved: Date.now(), isDeployed: false, deploymentStatuses: {}, - hasActiveWebhook: false, } const mockTransaction = vi.fn().mockImplementation(async (callback) => { @@ -494,10 +487,6 @@ describe('Database Helpers', () => { ) expect(result.success).toBe(true) - expect(result.jsonBlob.blocks).toEqual({}) - expect(result.jsonBlob.edges).toEqual([]) - expect(result.jsonBlob.loops).toEqual({}) - expect(result.jsonBlob.parallels).toEqual({}) }) it('should return error when transaction fails', async () => { @@ -650,7 +639,6 @@ describe('Database Helpers', () => { lastSaved: Date.now(), isDeployed: false, deploymentStatuses: {}, - hasActiveWebhook: false, } it('should successfully migrate workflow from JSON to normalized tables', async () => { @@ -737,7 +725,6 @@ describe('Database Helpers', () => { lastSaved: Date.now(), isDeployed: false, deploymentStatuses: {}, - hasActiveWebhook: false, } // Create 1000 blocks @@ -782,8 +769,6 @@ describe('Database Helpers', () => { ) expect(result.success).toBe(true) - expect(Object.keys(result.jsonBlob.blocks)).toHaveLength(1000) - expect(result.jsonBlob.edges).toHaveLength(999) }) }) @@ -1020,7 +1005,6 @@ describe('Database Helpers', () => { loops: {}, parallels: {}, deploymentStatuses: {}, - hasActiveWebhook: false, } // Mock the transaction for save operation @@ -1058,10 +1042,6 @@ describe('Database Helpers', () => { ) expect(saveResult.success).toBe(true) - // Step 6: Verify the JSON blob also preserves advancedMode - expect(saveResult.jsonBlob?.blocks['agent-original'].advancedMode).toBe(true) - expect(saveResult.jsonBlob?.blocks['agent-duplicate'].advancedMode).toBe(true) - // Verify the database insert was called with the correct values expect(mockTransaction).toHaveBeenCalled() }) @@ -1161,7 +1141,6 @@ describe('Database Helpers', () => { loops: {}, parallels: {}, deploymentStatuses: {}, - hasActiveWebhook: false, } // Mock successful save diff --git a/apps/sim/lib/workflows/db-helpers.ts b/apps/sim/lib/workflows/db-helpers.ts index b4f6f75897..27b48f7d87 100644 --- a/apps/sim/lib/workflows/db-helpers.ts +++ b/apps/sim/lib/workflows/db-helpers.ts @@ -221,12 +221,11 @@ export async function loadWorkflowFromNormalizedTables( /** * Save workflow state to normalized tables - * Also returns the JSON blob for backward compatibility */ export async function saveWorkflowToNormalizedTables( workflowId: string, state: WorkflowState -): Promise<{ success: boolean; jsonBlob?: any; error?: string }> { +): Promise<{ success: boolean; error?: string }> { try { // Start a transaction await db.transaction(async (tx) => { @@ -302,27 +301,9 @@ export async function saveWorkflowToNormalizedTables( if (subflowInserts.length > 0) { await tx.insert(workflowSubflows).values(subflowInserts) } - - return { success: true } }) - // Create JSON blob for backward compatibility - const jsonBlob = { - blocks: state.blocks, - edges: state.edges, - loops: state.loops || {}, - parallels: state.parallels || {}, - lastSaved: Date.now(), - isDeployed: state.isDeployed, - deployedAt: state.deployedAt, - deploymentStatuses: state.deploymentStatuses, - hasActiveWebhook: state.hasActiveWebhook, - } - - return { - success: true, - jsonBlob, - } + return { success: true } } catch (error) { logger.error(`Error saving workflow ${workflowId} to normalized tables:`, error) return { @@ -359,6 +340,7 @@ export async function migrateWorkflowToNormalizedTables( ): Promise<{ success: boolean; error?: string }> { try { // Convert JSON state to WorkflowState format + // Only include fields that are actually persisted to normalized tables const workflowState: WorkflowState = { blocks: jsonState.blocks || {}, edges: jsonState.edges || [], @@ -367,16 +349,9 @@ export async function migrateWorkflowToNormalizedTables( lastSaved: jsonState.lastSaved, isDeployed: jsonState.isDeployed, deployedAt: jsonState.deployedAt, - deploymentStatuses: jsonState.deploymentStatuses || {}, - hasActiveWebhook: jsonState.hasActiveWebhook, } - const result = await saveWorkflowToNormalizedTables(workflowId, workflowState) - - if (result.success) { - return { success: true } - } - return { success: false, error: result.error } + return await saveWorkflowToNormalizedTables(workflowId, workflowState) } catch (error) { logger.error(`Error migrating workflow ${workflowId} to normalized tables:`, error) return { diff --git a/apps/sim/lib/workflows/diff/use-workflow-diff.ts b/apps/sim/lib/workflows/diff/use-workflow-diff.ts index c9d82a2b2a..c8ed9fdff5 100644 --- a/apps/sim/lib/workflows/diff/use-workflow-diff.ts +++ b/apps/sim/lib/workflows/diff/use-workflow-diff.ts @@ -68,7 +68,6 @@ export function useWorkflowDiff(): UseWorkflowDiffReturn { isDeployed: currentState.isDeployed, deployedAt: currentState.deployedAt, deploymentStatuses: { ...currentState.deploymentStatuses }, - hasActiveWebhook: currentState.hasActiveWebhook, }, subblockValues: JSON.parse(JSON.stringify(currentSubblockValues)), // Deep copy timestamp: Date.now(), @@ -107,7 +106,6 @@ export function useWorkflowDiff(): UseWorkflowDiffReturn { isDeployed: backup.workflowState.isDeployed, deployedAt: backup.workflowState.deployedAt, deploymentStatuses: backup.workflowState.deploymentStatuses, - hasActiveWebhook: backup.workflowState.hasActiveWebhook, }) // Restore subblock values diff --git a/apps/sim/lib/workflows/json-sanitizer.ts b/apps/sim/lib/workflows/json-sanitizer.ts index 69acb5b14a..3a974a3c59 100644 --- a/apps/sim/lib/workflows/json-sanitizer.ts +++ b/apps/sim/lib/workflows/json-sanitizer.ts @@ -365,6 +365,5 @@ export function mergeWithUIState( isDeployed: fullState.isDeployed, deployedAt: fullState.deployedAt, deploymentStatuses: fullState.deploymentStatuses, - hasActiveWebhook: fullState.hasActiveWebhook, } } diff --git a/apps/sim/socket-server/database/operations.ts b/apps/sim/socket-server/database/operations.ts index 388d6610a3..9ea9c7be06 100644 --- a/apps/sim/socket-server/database/operations.ts +++ b/apps/sim/socket-server/database/operations.ts @@ -132,7 +132,6 @@ export async function getWorkflowState(workflowId: string) { const finalState = { // Default values for expected properties deploymentStatuses: {}, - hasActiveWebhook: false, // Data from normalized tables blocks: normalizedData.blocks, edges: normalizedData.edges, diff --git a/apps/sim/stores/copilot/store.ts b/apps/sim/stores/copilot/store.ts index 2b4f6f34e4..155ad1d383 100644 --- a/apps/sim/stores/copilot/store.ts +++ b/apps/sim/stores/copilot/store.ts @@ -1953,7 +1953,6 @@ export const useCopilotStore = create()( isDeployed: !!reverted.isDeployed, ...(reverted.deployedAt ? { deployedAt: new Date(reverted.deployedAt) } : {}), deploymentStatuses: reverted.deploymentStatuses || {}, - hasActiveWebhook: !!reverted.hasActiveWebhook, }) // Extract and apply subblock values diff --git a/apps/sim/stores/workflows/workflow/store.ts b/apps/sim/stores/workflows/workflow/store.ts index 8d8a4663da..22104ff6f7 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -38,7 +38,6 @@ const initialState = { // New field for per-workflow deployment tracking deploymentStatuses: {}, needsRedeployment: false, - hasActiveWebhook: false, history: { past: [], present: { @@ -475,7 +474,6 @@ export const useWorkflowStore = create()( lastSaved: Date.now(), isDeployed: false, isPublished: false, - hasActiveWebhook: false, } set(newState) // Note: Socket.IO handles real-time sync automatically @@ -500,7 +498,6 @@ export const useWorkflowStore = create()( deployedAt: state.deployedAt, deploymentStatuses: state.deploymentStatuses, needsRedeployment: state.needsRedeployment, - hasActiveWebhook: state.hasActiveWebhook, } }, @@ -902,15 +899,6 @@ export const useWorkflowStore = create()( })) }, - setWebhookStatus: (hasActiveWebhook: boolean) => { - // Only update if the status has changed to avoid unnecessary rerenders - if (get().hasActiveWebhook !== hasActiveWebhook) { - set({ hasActiveWebhook }) - get().updateLastSaved() - // Note: Socket.IO handles real-time sync automatically - } - }, - revertToDeployedState: async (deployedState: WorkflowState) => { const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId @@ -931,7 +919,6 @@ export const useWorkflowStore = create()( parallels: deployedState.parallels || {}, isDeployed: true, needsRedeployment: false, - hasActiveWebhook: false, // Reset webhook status // Keep existing deployment statuses and update for the active workflow if needed deploymentStatuses: { ...get().deploymentStatuses, @@ -966,14 +953,6 @@ export const useWorkflowStore = create()( }, }) - // Check if there's an active webhook in the deployed state - const starterBlock = Object.values(deployedState.blocks).find( - (block) => block.type === 'starter' - ) - if (starterBlock && starterBlock.subBlocks?.startWorkflow?.value === 'webhook') { - set({ hasActiveWebhook: true }) - } - pushHistory(set, get, newState, 'Reverted to deployed state') get().updateLastSaved() diff --git a/apps/sim/stores/workflows/workflow/types.ts b/apps/sim/stores/workflows/workflow/types.ts index a9681d1301..3a1c6a0b5b 100644 --- a/apps/sim/stores/workflows/workflow/types.ts +++ b/apps/sim/stores/workflows/workflow/types.ts @@ -154,7 +154,6 @@ export interface WorkflowState { // New field for per-workflow deployment status deploymentStatuses?: Record needsRedeployment?: boolean - hasActiveWebhook?: boolean // Drag state for undo/redo dragStartPosition?: DragStartPosition | null } @@ -214,7 +213,6 @@ export interface WorkflowActions { generateLoopBlocks: () => Record generateParallelBlocks: () => Record setNeedsRedeploymentFlag: (needsRedeployment: boolean) => void - setWebhookStatus: (hasActiveWebhook: boolean) => void revertToDeployedState: (deployedState: WorkflowState) => void toggleBlockAdvancedMode: (id: string) => void toggleBlockTriggerMode: (id: string) => void