diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx
index 5944c2ad02..224a2534e8 100644
--- a/app/w/[id]/workflow.tsx
+++ b/app/w/[id]/workflow.tsx
@@ -19,7 +19,6 @@ import { initializeStateLogger } from '@/stores/workflow/logger'
import { useWorkflowRegistry } from '@/stores/workflow/registry/store'
import { useWorkflowStore } from '@/stores/workflow/store'
import { NotificationList } from '@/app/w/components/notifications/notifications'
-import { WorkflowSyncWrapper } from '@/app/w/components/workflows/sync-wrapper'
import { getBlock } from '../../../blocks'
import { ErrorBoundary } from '../components/error-boundary/error-boundary'
import { CustomEdge } from './components/custom-edge/custom-edge'
@@ -379,9 +378,7 @@ export default function Workflow() {
return (
-
-
-
+
)
diff --git a/app/w/components/workflows/sync-wrapper.tsx b/app/w/components/workflows/sync-wrapper.tsx
deleted file mode 100644
index 0428922e52..0000000000
--- a/app/w/components/workflows/sync-wrapper.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { ReactNode } from 'react'
-import {
- useDebouncedWorkflowSync,
- usePeriodicWorkflowSync,
- useSyncOnUnload,
-} from '@/stores/workflow/sync/hooks'
-
-interface WorkflowSyncWrapperProps {
- children: ReactNode
-}
-
-export function WorkflowSyncWrapper({ children }: WorkflowSyncWrapperProps) {
- useDebouncedWorkflowSync()
- usePeriodicWorkflowSync()
- useSyncOnUnload()
-
- return <>{children}>
-}
diff --git a/drizzle.config.ts b/drizzle.config.ts
index c7ddc326a7..e3fb4bda9b 100644
--- a/drizzle.config.ts
+++ b/drizzle.config.ts
@@ -5,6 +5,6 @@ export default {
out: './db/migrations',
dialect: 'postgresql',
dbCredentials: {
- url: process.env.DATABASE_URL!,
+ url: 'postgresql://postgres:%23WaldoEmmy1@db.jchdgebatsqopodtyast.supabase.co:5432/postgres',
},
} satisfies Config
diff --git a/middleware.ts b/middleware.ts
index b1ef96d4d8..211337f155 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -11,5 +11,5 @@ export async function middleware(request: NextRequest) {
// TODO: Add protected routes
export const config = {
- matcher: ['/dashboard/:path*', '/w/:path*'],
+ matcher: ['/w/:path*'],
}
diff --git a/stores/index.ts b/stores/index.ts
index 3b6a135590..085a707604 100644
--- a/stores/index.ts
+++ b/stores/index.ts
@@ -4,9 +4,15 @@ import { useExecutionStore } from './execution/store'
import { useNotificationStore } from './notifications/store'
import { useEnvironmentStore } from './settings/environment/store'
import { useGeneralStore } from './settings/general/store'
+import { initializeSyncManager } from './sync-manager'
import { useWorkflowRegistry } from './workflow/registry/store'
import { useWorkflowStore } from './workflow/store'
+// Initialize sync manager when the store is first imported
+if (typeof window !== 'undefined') {
+ initializeSyncManager()
+}
+
// Reset all application stores to their initial state
export const resetAllStores = () => {
// Selectively clear localStorage items
diff --git a/stores/sync-manager.ts b/stores/sync-manager.ts
new file mode 100644
index 0000000000..7bc8d4b02e
--- /dev/null
+++ b/stores/sync-manager.ts
@@ -0,0 +1,69 @@
+import { useWorkflowRegistry } from './workflow/registry/store'
+import { useWorkflowStore } from './workflow/store'
+
+interface SyncPayload {
+ id: string
+ name: string
+ description?: string
+ state: string
+}
+
+async function syncWorkflowToServer(payload: SyncPayload): Promise {
+ try {
+ const response = await fetch('/api/workflows/sync', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ keepalive: true, // Ensures request completes even during page unload
+ })
+
+ if (!response.ok) {
+ if (response.status === 401) {
+ window.location.href = '/login'
+ return false
+ }
+ throw new Error(`Sync failed: ${response.statusText}`)
+ }
+
+ return true
+ } catch (error) {
+ console.error('Error syncing workflow:', error)
+ return false
+ }
+}
+
+export function initializeSyncManager() {
+ if (typeof window === 'undefined') return
+
+ const handleBeforeUnload = async (event: BeforeUnloadEvent) => {
+ const { activeWorkflowId, workflows } = useWorkflowRegistry.getState()
+ const workflowState = useWorkflowStore.getState()
+
+ if (!activeWorkflowId || !workflows[activeWorkflowId]) {
+ return
+ }
+
+ const activeWorkflow = workflows[activeWorkflowId]
+ const payload: SyncPayload = {
+ id: activeWorkflowId,
+ name: activeWorkflow.name,
+ description: activeWorkflow.description,
+ state: JSON.stringify({
+ blocks: workflowState.blocks,
+ edges: workflowState.edges,
+ loops: workflowState.loops,
+ lastSaved: workflowState.lastSaved,
+ }),
+ }
+
+ // Show confirmation dialog
+ event.preventDefault()
+ event.returnValue = ''
+
+ // Attempt to sync
+ await syncWorkflowToServer(payload)
+ }
+
+ window.addEventListener('beforeunload', handleBeforeUnload)
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload)
+}
diff --git a/stores/workflow/sync/hooks.ts b/stores/workflow/sync/hooks.ts
deleted file mode 100644
index 197ea12f72..0000000000
--- a/stores/workflow/sync/hooks.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { useEffect, useRef } from 'react'
-import { useRouter } from 'next/navigation'
-import debounce from 'lodash.debounce'
-import { useNotificationStore } from '@/stores/notifications/store'
-import { useWorkflowRegistry } from '../registry/store'
-import { useWorkflowStore } from '../store'
-
-const SYNC_DEBOUNCE_MS = 2000 // 2 seconds
-const PERIODIC_SYNC_MS = 30000 // 30 seconds
-
-interface SyncPayload {
- id: string
- name: string
- description?: string
- state: string
-}
-
-async function syncWorkflowToServer(payload: SyncPayload): Promise {
- try {
- const response = await fetch('/api/workflows/sync', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- })
-
- if (!response.ok) {
- if (response.status === 401) {
- // Auth error - will be handled by the middleware
- window.location.href = '/login'
- return false
- }
- throw new Error(`Sync failed: ${response.statusText}`)
- }
-
- return true
- } catch (error) {
- console.error('Error syncing workflow:', error)
- return false
- }
-}
-
-export function useDebouncedWorkflowSync() {
- const router = useRouter()
- const { addNotification } = useNotificationStore()
- const workflowState = useWorkflowStore((state) => ({
- blocks: state.blocks,
- edges: state.edges,
- loops: state.loops,
- lastSaved: state.lastSaved,
- }))
- const { activeWorkflowId, workflows } = useWorkflowRegistry()
-
- const debouncedSyncRef = useRef | null>(null)
-
- useEffect(() => {
- if (!activeWorkflowId || !workflows[activeWorkflowId]) return
-
- const syncWorkflow = async () => {
- const activeWorkflow = workflows[activeWorkflowId]
- const payload: SyncPayload = {
- id: activeWorkflowId,
- name: activeWorkflow.name,
- description: activeWorkflow.description,
- state: JSON.stringify(workflowState),
- }
-
- const success = await syncWorkflowToServer(payload)
- if (!success) {
- addNotification(
- 'error',
- 'Failed to save workflow changes. Please try again.',
- activeWorkflowId
- )
- }
- }
-
- // Create a debounced version of syncWorkflow
- if (!debouncedSyncRef.current) {
- debouncedSyncRef.current = debounce(syncWorkflow, SYNC_DEBOUNCE_MS)
- }
-
- // Call the debounced sync
- debouncedSyncRef.current()
-
- // Cleanup
- return () => {
- debouncedSyncRef.current?.cancel()
- }
- }, [activeWorkflowId, workflows, workflowState, addNotification])
-}
-
-export function usePeriodicWorkflowSync() {
- const { addNotification } = useNotificationStore()
- const workflowState = useWorkflowStore((state) => ({
- blocks: state.blocks,
- edges: state.edges,
- loops: state.loops,
- lastSaved: state.lastSaved,
- }))
- const { activeWorkflowId, workflows } = useWorkflowRegistry()
-
- useEffect(() => {
- if (!activeWorkflowId || !workflows[activeWorkflowId]) return
-
- const syncWorkflow = async () => {
- const activeWorkflow = workflows[activeWorkflowId]
- const payload: SyncPayload = {
- id: activeWorkflowId,
- name: activeWorkflow.name,
- description: activeWorkflow.description,
- state: JSON.stringify(workflowState),
- }
-
- const success = await syncWorkflowToServer(payload)
- if (!success) {
- addNotification(
- 'error',
- 'Failed to auto-save workflow changes. Please save manually.',
- activeWorkflowId
- )
- }
- }
-
- const intervalId = setInterval(syncWorkflow, PERIODIC_SYNC_MS)
-
- return () => clearInterval(intervalId)
- }, [activeWorkflowId, workflows, workflowState, addNotification])
-}
-
-export function useSyncOnUnload() {
- const { addNotification } = useNotificationStore()
- const workflowState = useWorkflowStore((state) => ({
- blocks: state.blocks,
- edges: state.edges,
- loops: state.loops,
- lastSaved: state.lastSaved,
- }))
- const { activeWorkflowId, workflows } = useWorkflowRegistry()
-
- useEffect(() => {
- if (!activeWorkflowId || !workflows[activeWorkflowId]) return
-
- const handleBeforeUnload = async (event: BeforeUnloadEvent) => {
- const activeWorkflow = workflows[activeWorkflowId]
- const payload: SyncPayload = {
- id: activeWorkflowId,
- name: activeWorkflow.name,
- description: activeWorkflow.description,
- state: JSON.stringify(workflowState),
- }
-
- // Use the keepalive option to try to complete the request even during unload
- const response = await fetch('/api/workflows/sync', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- keepalive: true,
- })
-
- if (!response.ok) {
- addNotification(
- 'error',
- 'Failed to save workflow changes before closing.',
- activeWorkflowId
- )
- }
-
- // Show a confirmation dialog
- event.preventDefault()
- event.returnValue = ''
- }
-
- window.addEventListener('beforeunload', handleBeforeUnload)
- return () => window.removeEventListener('beforeunload', handleBeforeUnload)
- }, [activeWorkflowId, workflows, workflowState, addNotification])
-}