Skip to content

Commit 244c086

Browse files
Vikhyath MondretiVikhyath Mondreti
authored andcommitted
fix sidebar issue
1 parent f8cfdba commit 244c086

File tree

5 files changed

+24
-113
lines changed

5 files changed

+24
-113
lines changed

apps/sim/app/api/workflows/sync/route.ts

Lines changed: 8 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,93 +4,29 @@ import { NextResponse } from 'next/server'
44
import { getSession } from '@/lib/auth'
55
import { createLogger } from '@/lib/logs/console-logger'
66
import { db } from '@/db'
7-
import { workflow, workspace, workspaceMember } from '@/db/schema'
7+
import { workflow, workspace } from '@/db/schema'
8+
import { getUserEntityPermissions } from '@/lib/permissions/utils'
89

910
const logger = createLogger('WorkflowAPI')
1011

11-
// Cache for workspace membership to reduce DB queries
12-
const workspaceMembershipCache = new Map<string, { role: string; expires: number }>()
13-
const CACHE_TTL = 60000 // 1 minute cache expiration
14-
const MAX_CACHE_SIZE = 1000 // Maximum number of entries to prevent unbounded growth
15-
16-
/**
17-
* Cleans up expired entries from the workspace membership cache
18-
*/
19-
function cleanupExpiredCacheEntries(): void {
20-
const now = Date.now()
21-
let expiredCount = 0
22-
23-
// Remove expired entries
24-
for (const [key, value] of workspaceMembershipCache.entries()) {
25-
if (value.expires <= now) {
26-
workspaceMembershipCache.delete(key)
27-
expiredCount++
28-
}
29-
}
30-
31-
// If we're still over the limit after removing expired entries,
32-
// remove the oldest entries (those that will expire soonest)
33-
if (workspaceMembershipCache.size > MAX_CACHE_SIZE) {
34-
const entries = Array.from(workspaceMembershipCache.entries()).sort(
35-
(a, b) => a[1].expires - b[1].expires
36-
)
37-
38-
const toRemove = entries.slice(0, workspaceMembershipCache.size - MAX_CACHE_SIZE)
39-
toRemove.forEach(([key]) => workspaceMembershipCache.delete(key))
40-
41-
logger.debug(
42-
`Cache cleanup: removed ${expiredCount} expired entries and ${toRemove.length} additional entries due to size limit`
43-
)
44-
} else if (expiredCount > 0) {
45-
logger.debug(`Cache cleanup: removed ${expiredCount} expired entries`)
46-
}
47-
}
48-
4912
/**
50-
* Efficiently verifies user's membership and role in a workspace with caching
13+
* Verifies user's workspace permissions using the permissions table
5114
* @param userId User ID to check
5215
* @param workspaceId Workspace ID to check
53-
* @returns Role if user is a member, null otherwise
16+
* @returns Permission type if user has access, null otherwise
5417
*/
5518
async function verifyWorkspaceMembership(
5619
userId: string,
5720
workspaceId: string
5821
): Promise<string | null> {
59-
// Opportunistic cleanup of expired cache entries
60-
if (workspaceMembershipCache.size > MAX_CACHE_SIZE / 2) {
61-
cleanupExpiredCacheEntries()
62-
}
63-
64-
// Create cache key from userId and workspaceId
65-
const cacheKey = `${userId}:${workspaceId}`
66-
67-
// Check cache first
68-
const cached = workspaceMembershipCache.get(cacheKey)
69-
if (cached && cached.expires > Date.now()) {
70-
return cached.role
71-
}
72-
73-
// If not in cache or expired, query the database
7422
try {
75-
const membership = await db
76-
.select({ role: workspaceMember.role })
77-
.from(workspaceMember)
78-
.where(and(eq(workspaceMember.workspaceId, workspaceId), eq(workspaceMember.userId, userId)))
79-
.then((rows) => rows[0])
80-
81-
if (!membership) {
82-
return null
83-
}
23+
const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId)
24+
8425

85-
// Cache the result
86-
workspaceMembershipCache.set(cacheKey, {
87-
role: membership.role,
88-
expires: Date.now() + CACHE_TTL,
89-
})
9026

91-
return membership.role
27+
return permission
9228
} catch (error) {
93-
logger.error(`Error verifying workspace membership for ${userId} in ${workspaceId}:`, error)
29+
logger.error(`Error verifying workspace permissions for ${userId} in ${workspaceId}:`, error)
9430
return null
9531
}
9632
}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const IS_DEV = process.env.NODE_ENV === 'development'
3232
export function Sidebar() {
3333
useGlobalShortcuts()
3434

35-
const { workflows, createWorkflow, isLoading: workflowsLoading } = useWorkflowRegistry()
35+
const { workflows, createWorkflow, isLoading: workflowsLoading, loadWorkflows } = useWorkflowRegistry()
3636
const { isPending: sessionLoading } = useSession()
3737
const userPermissions = useUserPermissionsContext()
3838
const isLoading = workflowsLoading || sessionLoading
@@ -41,6 +41,14 @@ export function Sidebar() {
4141
const workspaceId = params.workspaceId as string
4242
const pathname = usePathname()
4343

44+
// Load workflows for the current workspace when workspaceId changes
45+
// This is the single source of truth for workflow loading
46+
useEffect(() => {
47+
if (workspaceId) {
48+
loadWorkflows(workspaceId)
49+
}
50+
}, [workspaceId, loadWorkflows])
51+
4452
const [showSettings, setShowSettings] = useState(false)
4553
const [showHelp, setShowHelp] = useState(false)
4654
const [showInviteMembers, setShowInviteMembers] = useState(false)

apps/sim/socket-server/middleware/permissions.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { and, eq } from 'drizzle-orm'
22
import { db } from '../../db'
3-
import { workflow, workspaceMember } from '../../db/schema'
3+
import { workflow } from '../../db/schema'
44
import { createLogger } from '../../lib/logs/console-logger'
5+
import { getUserEntityPermissions } from '../../lib/permissions/utils'
56

67
const logger = createLogger('SocketPermissions')
78

@@ -10,15 +11,10 @@ export async function verifyWorkspaceMembership(
1011
workspaceId: string
1112
): Promise<string | null> {
1213
try {
13-
const membership = await db
14-
.select({ role: workspaceMember.role })
15-
.from(workspaceMember)
16-
.where(and(eq(workspaceMember.workspaceId, workspaceId), eq(workspaceMember.userId, userId)))
17-
.limit(1)
18-
19-
return membership.length > 0 ? membership[0].role : null
14+
const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId)
15+
return permission
2016
} catch (error) {
21-
logger.error(`Error verifying workspace membership for ${userId} in ${workspaceId}:`, error)
17+
logger.error(`Error verifying workspace permissions for ${userId} in ${workspaceId}:`, error)
2218
return null
2319
}
2420
}

apps/sim/stores/index.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ async function initializeApplication(): Promise<void> {
4040
// Load custom tools from server
4141
await useCustomToolsStore.getState().loadCustomTools()
4242

43-
// Load workflows from database (replaced sync system)
44-
await useWorkflowRegistry.getState().loadWorkflows()
45-
4643
// Mark data as initialized only after sync managers have loaded data from DB
4744
dataInitialized = true
4845

@@ -66,30 +63,7 @@ async function initializeApplication(): Promise<void> {
6663
}
6764
}
6865

69-
/**
70-
* Extract workflow ID from current URL
71-
* @returns workflow ID if found in URL, null otherwise
72-
*/
73-
function extractWorkflowIdFromUrl(): string | null {
74-
if (typeof window === 'undefined') return null
7566

76-
try {
77-
const pathSegments = window.location.pathname.split('/')
78-
// Check if URL matches pattern /w/{workflowId}
79-
if (pathSegments.length >= 3 && pathSegments[1] === 'w') {
80-
const workflowId = pathSegments[2]
81-
// Basic UUID validation (36 characters, contains hyphens)
82-
if (workflowId && workflowId.length === 36 && workflowId.includes('-')) {
83-
logger.info(`Extracted workflow ID from URL: ${workflowId}`)
84-
return workflowId
85-
}
86-
}
87-
return null
88-
} catch (error) {
89-
logger.warn('Failed to extract workflow ID from URL:', error)
90-
return null
91-
}
92-
}
9367

9468
/**
9569
* Checks if application is fully initialized

apps/sim/stores/workflows/registry/store.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
267267
await fetchWorkflowsFromDB(workspaceId)
268268
},
269269

270-
// Switch to workspace with comprehensive error handling and loading states
270+
// Switch to workspace - just clear state, let sidebar handle workflow loading
271271
switchToWorkspace: async (workspaceId: string) => {
272272
// Prevent multiple simultaneous transitions
273273
if (isWorkspaceTransitioning) {
@@ -286,17 +286,14 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
286286
// Clear current workspace state
287287
resetWorkflowStores()
288288

289-
// Update state
289+
// Update state - sidebar will load workflows when URL changes
290290
set({
291291
activeWorkflowId: null,
292292
workflows: {},
293293
isLoading: true,
294294
error: null,
295295
})
296296

297-
// Fetch workflows for the new workspace
298-
await fetchWorkflowsFromDB(workspaceId)
299-
300297
logger.info(`Successfully switched to workspace: ${workspaceId}`)
301298
} catch (error) {
302299
logger.error(`Error switching to workspace ${workspaceId}:`, { error })

0 commit comments

Comments
 (0)