@@ -3,7 +3,7 @@ import { createLogger } from '@/lib/logs/console/logger'
33import { persistWorkflowOperation } from '@/socket-server/database/operations'
44import type { HandlerDependencies } from '@/socket-server/handlers/workflow'
55import type { AuthenticatedSocket } from '@/socket-server/middleware/auth'
6- import { verifyOperationPermission } from '@/socket-server/middleware/permissions'
6+ import { checkRolePermission } from '@/socket-server/middleware/permissions'
77import type { RoomManager } from '@/socket-server/rooms/manager'
88import { WorkflowOperationSchema } from '@/socket-server/validation/schemas'
99
@@ -43,36 +43,52 @@ export function setupOperationsHandlers(
4343 operationId = validatedOperation . operationId
4444 const { operation, target, payload, timestamp } = validatedOperation
4545
46- // Check operation permissions
47- const permissionCheck = await verifyOperationPermission (
48- session . userId ,
49- workflowId ,
50- operation ,
51- target
52- )
53- if ( ! permissionCheck . allowed ) {
54- logger . warn (
55- `User ${ session . userId } forbidden from ${ operation } on ${ target } : ${ permissionCheck . reason } `
56- )
57- socket . emit ( 'operation-forbidden' , {
58- type : 'INSUFFICIENT_PERMISSIONS' ,
59- message : permissionCheck . reason || 'Insufficient permissions for this operation' ,
60- operation,
61- target,
62- } )
63- return
64- }
65-
66- const userPresence = room . users . get ( socket . id )
67- if ( userPresence ) {
68- userPresence . lastActivity = Date . now ( )
69- }
70-
7146 // For position updates, preserve client timestamp to maintain ordering
7247 // For other operations, use server timestamp for consistency
7348 const isPositionUpdate = operation === 'update-position' && target === 'block'
49+ const commitPositionUpdate =
50+ isPositionUpdate && 'commit' in payload ? payload . commit === true : false
7451 const operationTimestamp = isPositionUpdate ? timestamp : Date . now ( )
7552
53+ // Skip permission checks for non-committed position updates (broadcasts only, no persistence)
54+ if ( isPositionUpdate && ! commitPositionUpdate ) {
55+ // Update last activity
56+ const userPresence = room . users . get ( socket . id )
57+ if ( userPresence ) {
58+ userPresence . lastActivity = Date . now ( )
59+ }
60+ } else {
61+ // Check permissions from cached role for all other operations
62+ const userPresence = room . users . get ( socket . id )
63+ if ( ! userPresence ) {
64+ logger . warn ( `User presence not found for socket ${ socket . id } ` )
65+ socket . emit ( 'operation-forbidden' , {
66+ type : 'SESSION_ERROR' ,
67+ message : 'User session not found' ,
68+ operation,
69+ target,
70+ } )
71+ return
72+ }
73+
74+ userPresence . lastActivity = Date . now ( )
75+
76+ // Check permissions using cached role (no DB query)
77+ const permissionCheck = checkRolePermission ( userPresence . role , operation )
78+ if ( ! permissionCheck . allowed ) {
79+ logger . warn (
80+ `User ${ session . userId } (role: ${ userPresence . role } ) forbidden from ${ operation } on ${ target } `
81+ )
82+ socket . emit ( 'operation-forbidden' , {
83+ type : 'INSUFFICIENT_PERMISSIONS' ,
84+ message : `${ permissionCheck . reason } on '${ target } '` ,
85+ operation,
86+ target,
87+ } )
88+ return
89+ }
90+ }
91+
7692 // Broadcast first for position updates to minimize latency, then persist
7793 // For other operations, persist first for consistency
7894 if ( isPositionUpdate ) {
@@ -94,36 +110,39 @@ export function setupOperationsHandlers(
94110
95111 socket . to ( workflowId ) . emit ( 'workflow-operation' , broadcastData )
96112
97- // Persist position update asynchronously to avoid blocking real-time updates
98- persistWorkflowOperation ( workflowId , {
99- operation,
100- target,
101- payload,
102- timestamp : operationTimestamp ,
103- userId : session . userId ,
104- } ) . catch ( ( error ) => {
113+ if ( ! commitPositionUpdate ) {
114+ return
115+ }
116+
117+ try {
118+ await persistWorkflowOperation ( workflowId , {
119+ operation,
120+ target,
121+ payload,
122+ timestamp : operationTimestamp ,
123+ userId : session . userId ,
124+ } )
125+ room . lastModified = Date . now ( )
126+
127+ if ( operationId ) {
128+ socket . emit ( 'operation-confirmed' , {
129+ operationId,
130+ serverTimestamp : Date . now ( ) ,
131+ } )
132+ }
133+ } catch ( error ) {
105134 logger . error ( 'Failed to persist position update:' , error )
106- // Emit failure for position updates if operationId is provided
135+
107136 if ( operationId ) {
108137 socket . emit ( 'operation-failed' , {
109138 operationId,
110139 error : error instanceof Error ? error . message : 'Database persistence failed' ,
111140 retryable : true ,
112141 } )
113142 }
114- } )
115-
116- room . lastModified = Date . now ( )
117-
118- // Emit confirmation if operationId is provided
119- if ( operationId ) {
120- socket . emit ( 'operation-confirmed' , {
121- operationId,
122- serverTimestamp : Date . now ( ) ,
123- } )
124143 }
125144
126- return // Early return for position updates
145+ return
127146 }
128147
129148 if ( target === 'variable' && [ 'add' , 'remove' , 'duplicate' ] . includes ( operation ) ) {
0 commit comments