From 3e541b04deee8f8f1e5de3c0114cb634c377b270 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Nov 2025 11:24:26 +0000 Subject: [PATCH] fix(api): resolve Go compilation errors in API handlers Fixed multiple compilation errors preventing the API build: 1. websocket/handlers.go:238 - Fixed unused 'err' variable by adding proper error handling with default value fallback 2. template_versioning.go & sessiontemplates.go - Resolved duplicate TemplateVersion type declaration by renaming sessiontemplates version to TemplateSnapshot (different purpose/structure) 3. console.go & collaboration.go - Removed duplicate canAccessSession method from console.go, enhanced the collaboration.go version to include shared access checking 4. collaboration.go - Fixed 27 instances of missing DB methods by changing h.DB.QueryRow() to h.DB.DB().QueryRow() and h.DB.Exec() to h.DB.DB().Exec() to properly access the underlying sql.DB instance All files verified with syntax checking. API build should now succeed. --- api/internal/handlers/collaboration.go | 210 +++++++++++----------- api/internal/handlers/console.go | 70 +++----- api/internal/handlers/sessiontemplates.go | 8 +- api/internal/websocket/handlers.go | 29 +-- 4 files changed, 155 insertions(+), 162 deletions(-) diff --git a/api/internal/handlers/collaboration.go b/api/internal/handlers/collaboration.go index e5bc75ef..55d17beb 100644 --- a/api/internal/handlers/collaboration.go +++ b/api/internal/handlers/collaboration.go @@ -273,15 +273,23 @@ func NewCollaborationHandler(database *db.Database) *Handler { } // canAccessSession checks if a user has access to a session. -// This is a placeholder implementation - replace with actual authorization logic. func (h *Handler) canAccessSession(userID, sessionID string) bool { - // TODO: Implement proper session access check - // For now, query the sessions table to see if user owns the session - var exists bool - err := h.DB.QueryRow(` - SELECT EXISTS(SELECT 1 FROM sessions WHERE id = $1 AND user_id = $2) - `, sessionID, userID).Scan(&exists) - return err == nil && exists + // Check if user owns the session + var owner string + err := h.DB.DB().QueryRow("SELECT user_id FROM sessions WHERE id = $1", sessionID).Scan(&owner) + if err == nil && owner == userID { + return true + } + + // Check shared access + var hasAccess bool + err = h.DB.DB().QueryRow(` + SELECT EXISTS( + SELECT 1 FROM session_shares + WHERE session_id = $1 AND shared_with_user_id = $2 + ) + `, sessionID, userID).Scan(&hasAccess) + return err == nil && hasAccess } // toJSONB converts a Go value to JSON string for JSONB storage. @@ -318,54 +326,54 @@ func toJSONB(v interface{}) string { // - Chat history, annotations preserved after session ends // - Cursor positions ephemeral (not stored in database) type CollaborationSession struct { - ID string `json:"id"` - SessionID string `json:"session_id"` - OwnerID string `json:"owner_id"` - Participants []CollaborationUser `json:"participants"` - Settings CollaborationSettings `json:"settings"` - ActiveUsers int `json:"active_users"` - ChatEnabled bool `json:"chat_enabled"` - AnnotationsEnabled bool `json:"annotations_enabled"` - CursorTracking bool `json:"cursor_tracking"` - Status string `json:"status"` // "active", "paused", "ended" - CreatedAt time.Time `json:"created_at"` - EndedAt *time.Time `json:"ended_at,omitempty"` + ID string `json:"id"` + SessionID string `json:"session_id"` + OwnerID string `json:"owner_id"` + Participants []CollaborationUser `json:"participants"` + Settings CollaborationSettings `json:"settings"` + ActiveUsers int `json:"active_users"` + ChatEnabled bool `json:"chat_enabled"` + AnnotationsEnabled bool `json:"annotations_enabled"` + CursorTracking bool `json:"cursor_tracking"` + Status string `json:"status"` // "active", "paused", "ended" + CreatedAt time.Time `json:"created_at"` + EndedAt *time.Time `json:"ended_at,omitempty"` } // CollaborationUser represents a user in a collaborative session type CollaborationUser struct { - UserID string `json:"user_id"` - Username string `json:"username"` - Role string `json:"role"` // "owner", "presenter", "participant", "viewer" + UserID string `json:"user_id"` + Username string `json:"username"` + Role string `json:"role"` // "owner", "presenter", "participant", "viewer" Permissions CollaborationPermissions `json:"permissions"` - CursorPosition *CursorPosition `json:"cursor_position,omitempty"` - IsActive bool `json:"is_active"` - JoinedAt time.Time `json:"joined_at"` - LastSeenAt time.Time `json:"last_seen_at"` - Color string `json:"color"` // User color for cursor/annotations + CursorPosition *CursorPosition `json:"cursor_position,omitempty"` + IsActive bool `json:"is_active"` + JoinedAt time.Time `json:"joined_at"` + LastSeenAt time.Time `json:"last_seen_at"` + Color string `json:"color"` // User color for cursor/annotations } // CollaborationPermissions defines what a user can do type CollaborationPermissions struct { - CanControl bool `json:"can_control"` // Can interact with session - CanAnnotate bool `json:"can_annotate"` // Can create annotations - CanChat bool `json:"can_chat"` // Can send messages - CanInvite bool `json:"can_invite"` // Can invite others - CanManage bool `json:"can_manage"` // Can change settings - CanRecord bool `json:"can_record"` // Can start recording - CanViewOnly bool `json:"can_view_only"` // View-only mode + CanControl bool `json:"can_control"` // Can interact with session + CanAnnotate bool `json:"can_annotate"` // Can create annotations + CanChat bool `json:"can_chat"` // Can send messages + CanInvite bool `json:"can_invite"` // Can invite others + CanManage bool `json:"can_manage"` // Can change settings + CanRecord bool `json:"can_record"` // Can start recording + CanViewOnly bool `json:"can_view_only"` // View-only mode } // CollaborationSettings defines session behavior type CollaborationSettings struct { - FollowMode string `json:"follow_mode"` // "none", "follow_presenter", "follow_owner" - MaxParticipants int `json:"max_participants"` - RequireApproval bool `json:"require_approval"` - AllowAnonymous bool `json:"allow_anonymous"` - LockOnPresenter bool `json:"lock_on_presenter"` - AutoMuteJoiners bool `json:"auto_mute_joiners"` - ShowCursorLabels bool `json:"show_cursor_labels"` - EnableHandRaise bool `json:"enable_hand_raise"` + FollowMode string `json:"follow_mode"` // "none", "follow_presenter", "follow_owner" + MaxParticipants int `json:"max_participants"` + RequireApproval bool `json:"require_approval"` + AllowAnonymous bool `json:"allow_anonymous"` + LockOnPresenter bool `json:"lock_on_presenter"` + AutoMuteJoiners bool `json:"auto_mute_joiners"` + ShowCursorLabels bool `json:"show_cursor_labels"` + EnableHandRaise bool `json:"enable_hand_raise"` } // CursorPosition represents cursor location @@ -389,17 +397,17 @@ type ChatMessage struct { // Annotation represents a drawing/annotation on the session type Annotation struct { - ID string `json:"id"` - SessionID string `json:"session_id"` - UserID string `json:"user_id"` - Type string `json:"type"` // "line", "arrow", "rectangle", "circle", "text", "freehand" - Color string `json:"color"` - Thickness int `json:"thickness"` - Points []Point `json:"points"` - Text string `json:"text,omitempty"` - IsPersistent bool `json:"is_persistent"` - CreatedAt time.Time `json:"created_at"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ID string `json:"id"` + SessionID string `json:"session_id"` + UserID string `json:"user_id"` + Type string `json:"type"` // "line", "arrow", "rectangle", "circle", "text", "freehand" + Color string `json:"color"` + Thickness int `json:"thickness"` + Points []Point `json:"points"` + Text string `json:"text,omitempty"` + IsPersistent bool `json:"is_persistent"` + CreatedAt time.Time `json:"created_at"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` } // Point represents a coordinate point @@ -420,12 +428,12 @@ func (h *Handler) CreateCollaborationSession(c *gin.Context) { if err := c.ShouldBindJSON(&req); err != nil { // Use defaults if not provided req.Settings = CollaborationSettings{ - FollowMode: "none", - MaxParticipants: 10, - RequireApproval: false, - AllowAnonymous: false, - ShowCursorLabels: true, - EnableHandRaise: true, + FollowMode: "none", + MaxParticipants: 10, + RequireApproval: false, + AllowAnonymous: false, + ShowCursorLabels: true, + EnableHandRaise: true, } } @@ -437,7 +445,7 @@ func (h *Handler) CreateCollaborationSession(c *gin.Context) { // Check if collaboration already exists var existingID string - err := h.DB.QueryRow(` + err := h.DB.DB().QueryRow(` SELECT id FROM collaboration_sessions WHERE session_id = $1 AND status = 'active' `, sessionID).Scan(&existingID) @@ -449,7 +457,7 @@ func (h *Handler) CreateCollaborationSession(c *gin.Context) { // Create collaboration session collabID := fmt.Sprintf("collab-%s-%d", sessionID, time.Now().Unix()) - err = h.DB.QueryRow(` + err = h.DB.DB().QueryRow(` INSERT INTO collaboration_sessions ( id, session_id, owner_id, settings, chat_enabled, annotations_enabled, cursor_tracking, status @@ -464,16 +472,16 @@ func (h *Handler) CreateCollaborationSession(c *gin.Context) { // Add owner as first participant ownerPerms := CollaborationPermissions{ - CanControl: true, - CanAnnotate: true, - CanChat: true, - CanInvite: true, - CanManage: true, - CanRecord: true, - CanViewOnly: false, + CanControl: true, + CanAnnotate: true, + CanChat: true, + CanInvite: true, + CanManage: true, + CanRecord: true, + CanViewOnly: false, } - h.DB.Exec(` + h.DB.DB().Exec(` INSERT INTO collaboration_participants ( collaboration_id, user_id, role, permissions, color, is_active ) VALUES ($1, $2, $3, $4, $5, $6) @@ -500,7 +508,7 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { // Get collaboration details var sessionID, ownerID string var settings, status sql.NullString - err := h.DB.QueryRow(` + err := h.DB.DB().QueryRow(` SELECT session_id, owner_id, settings, status FROM collaboration_sessions WHERE id = $1 `, collabID).Scan(&sessionID, &ownerID, &settings, &status) @@ -529,14 +537,14 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { // Check if already a participant var existingRole string - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT role FROM collaboration_participants WHERE collaboration_id = $1 AND user_id = $2 `, collabID, userID).Scan(&existingRole) if existingRole != "" { // Update to active - h.DB.Exec(` + h.DB.DB().Exec(` UPDATE collaboration_participants SET is_active = true, last_seen_at = $1 WHERE collaboration_id = $2 AND user_id = $3 @@ -548,7 +556,7 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { // Check participant limit var participantCount int - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT COUNT(*) FROM collaboration_participants WHERE collaboration_id = $1 AND is_active = true `, collabID).Scan(&participantCount) @@ -560,13 +568,13 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { // Default permissions for participants participantPerms := CollaborationPermissions{ - CanControl: true, - CanAnnotate: true, - CanChat: true, - CanInvite: false, - CanManage: false, - CanRecord: false, - CanViewOnly: false, + CanControl: true, + CanAnnotate: true, + CanChat: true, + CanInvite: false, + CanManage: false, + CanRecord: false, + CanViewOnly: false, } // Assign color @@ -574,7 +582,7 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { userColor := colors[participantCount%len(colors)] // Add participant - _, err = h.DB.Exec(` + _, err = h.DB.DB().Exec(` INSERT INTO collaboration_participants ( collaboration_id, user_id, role, permissions, color, is_active ) VALUES ($1, $2, $3, $4, $5, $6) @@ -586,14 +594,14 @@ func (h *Handler) JoinCollaborationSession(c *gin.Context) { } // Update participant count - h.DB.Exec(` + h.DB.DB().Exec(` UPDATE collaboration_sessions SET active_users = (SELECT COUNT(*) FROM collaboration_participants WHERE collaboration_id = $1 AND is_active = true) WHERE id = $1 `, collabID) // Send system message - h.DB.Exec(` + h.DB.DB().Exec(` INSERT INTO collaboration_chat ( collaboration_id, user_id, message, message_type ) VALUES ($1, $2, $3, $4) @@ -613,7 +621,7 @@ func (h *Handler) LeaveCollaborationSession(c *gin.Context) { userID := c.GetString("user_id") // Update participant status - _, err := h.DB.Exec(` + _, err := h.DB.DB().Exec(` UPDATE collaboration_participants SET is_active = false, last_seen_at = $1 WHERE collaboration_id = $2 AND user_id = $3 @@ -625,14 +633,14 @@ func (h *Handler) LeaveCollaborationSession(c *gin.Context) { } // Update active user count - h.DB.Exec(` + h.DB.DB().Exec(` UPDATE collaboration_sessions SET active_users = (SELECT COUNT(*) FROM collaboration_participants WHERE collaboration_id = $1 AND is_active = true) WHERE id = $1 `, collabID) // Send system message - h.DB.Exec(` + h.DB.DB().Exec(` INSERT INTO collaboration_chat ( collaboration_id, user_id, message, message_type ) VALUES ($1, $2, $3, $4) @@ -716,7 +724,7 @@ func (h *Handler) UpdateParticipantRole(c *gin.Context) { } // Update participant - _, err := h.DB.Exec(` + _, err := h.DB.DB().Exec(` UPDATE collaboration_participants SET role = $1, permissions = $2 WHERE collaboration_id = $3 AND user_id = $4 @@ -760,7 +768,7 @@ func (h *Handler) SendChatMessage(c *gin.Context) { // Insert message var msgID int64 - err := h.DB.QueryRow(` + err := h.DB.DB().QueryRow(` INSERT INTO collaboration_chat ( collaboration_id, user_id, message, message_type, metadata ) VALUES ($1, $2, $3, $4, $5) @@ -867,7 +875,7 @@ func (h *Handler) CreateAnnotation(c *gin.Context) { // Get session ID var sessionID string - h.DB.QueryRow("SELECT session_id FROM collaboration_sessions WHERE id = $1", collabID).Scan(&sessionID) + h.DB.DB().QueryRow("SELECT session_id FROM collaboration_sessions WHERE id = $1", collabID).Scan(&sessionID) annotationID := fmt.Sprintf("annot-%d", time.Now().UnixNano()) req.ID = annotationID @@ -881,7 +889,7 @@ func (h *Handler) CreateAnnotation(c *gin.Context) { expiresAt = &expires } - _, err := h.DB.Exec(` + _, err := h.DB.DB().Exec(` INSERT INTO collaboration_annotations ( id, collaboration_id, session_id, user_id, type, color, thickness, points, text, is_persistent, expires_at @@ -948,14 +956,14 @@ func (h *Handler) DeleteAnnotation(c *gin.Context) { // Verify ownership or manage permission var ownerID string - h.DB.QueryRow("SELECT user_id FROM collaboration_annotations WHERE id = $1", annotationID).Scan(&ownerID) + h.DB.DB().QueryRow("SELECT user_id FROM collaboration_annotations WHERE id = $1", annotationID).Scan(&ownerID) if ownerID != userID && !h.canManageCollaboration(collabID, userID) { c.JSON(http.StatusForbidden, gin.H{"error": "permission denied"}) return } - _, err := h.DB.Exec("DELETE FROM collaboration_annotations WHERE id = $1", annotationID) + _, err := h.DB.DB().Exec("DELETE FROM collaboration_annotations WHERE id = $1", annotationID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete annotation"}) return @@ -974,7 +982,7 @@ func (h *Handler) ClearAllAnnotations(c *gin.Context) { return } - result, err := h.DB.Exec("DELETE FROM collaboration_annotations WHERE collaboration_id = $1", collabID) + result, err := h.DB.DB().Exec("DELETE FROM collaboration_annotations WHERE collaboration_id = $1", collabID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to clear annotations"}) return @@ -988,7 +996,7 @@ func (h *Handler) ClearAllAnnotations(c *gin.Context) { func (h *Handler) isCollaborationParticipant(collabID, userID string) bool { var exists bool - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT EXISTS(SELECT 1 FROM collaboration_participants WHERE collaboration_id = $1 AND user_id = $2) `, collabID, userID).Scan(&exists) @@ -997,7 +1005,7 @@ func (h *Handler) isCollaborationParticipant(collabID, userID string) bool { func (h *Handler) canManageCollaboration(collabID, userID string) bool { var permissions sql.NullString - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT permissions FROM collaboration_participants WHERE collaboration_id = $1 AND user_id = $2 `, collabID, userID).Scan(&permissions) @@ -1013,7 +1021,7 @@ func (h *Handler) canManageCollaboration(collabID, userID string) bool { func (h *Handler) hasCollaborationPermission(collabID, userID, permission string) bool { var permissions sql.NullString - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT permissions FROM collaboration_participants WHERE collaboration_id = $1 AND user_id = $2 AND is_active = true `, collabID, userID).Scan(&permissions) @@ -1053,7 +1061,7 @@ func (h *Handler) GetCollaborationStats(c *gin.Context) { // Participant count var totalParticipants, activeParticipants int - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT COUNT(*), COUNT(*) FILTER (WHERE is_active = true) FROM collaboration_participants WHERE collaboration_id = $1 `, collabID).Scan(&totalParticipants, &activeParticipants) @@ -1062,14 +1070,14 @@ func (h *Handler) GetCollaborationStats(c *gin.Context) { // Message count var messageCount int - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT COUNT(*) FROM collaboration_chat WHERE collaboration_id = $1 `, collabID).Scan(&messageCount) stats["total_messages"] = messageCount // Annotation count var annotationCount int - h.DB.QueryRow(` + h.DB.DB().QueryRow(` SELECT COUNT(*) FROM collaboration_annotations WHERE collaboration_id = $1 AND (expires_at IS NULL OR expires_at > $2) `, collabID, time.Now()).Scan(&annotationCount) @@ -1077,7 +1085,7 @@ func (h *Handler) GetCollaborationStats(c *gin.Context) { // Session duration var startTime time.Time - h.DB.QueryRow("SELECT created_at FROM collaboration_sessions WHERE id = $1", collabID).Scan(&startTime) + h.DB.DB().QueryRow("SELECT created_at FROM collaboration_sessions WHERE id = $1", collabID).Scan(&startTime) duration := time.Since(startTime) stats["duration_seconds"] = int(duration.Seconds()) diff --git a/api/internal/handlers/console.go b/api/internal/handlers/console.go index 94fd1fab..14b7e7c2 100644 --- a/api/internal/handlers/console.go +++ b/api/internal/handlers/console.go @@ -86,13 +86,13 @@ type ConsoleSession struct { ID string `json:"id"` SessionID string `json:"session_id"` UserID string `json:"user_id"` - Type string `json:"type"` // "terminal", "file_manager" + Type string `json:"type"` // "terminal", "file_manager" Status string `json:"status"` // "active", "idle", "disconnected" WebSocketURL string `json:"websocket_url,omitempty"` CurrentPath string `json:"current_path,omitempty"` ShellType string `json:"shell_type,omitempty"` // "bash", "sh", "zsh" - Columns int `json:"columns,omitempty"` // Terminal columns - Rows int `json:"rows,omitempty"` // Terminal rows + Columns int `json:"columns,omitempty"` // Terminal columns + Rows int `json:"rows,omitempty"` // Terminal rows Metadata map[string]interface{} `json:"metadata,omitempty"` ConnectedAt time.Time `json:"connected_at"` LastActivityAt time.Time `json:"last_activity_at"` @@ -101,25 +101,25 @@ type ConsoleSession struct { // FileInfo represents file/directory information type FileInfo struct { - Name string `json:"name"` - Path string `json:"path"` - Size int64 `json:"size"` - IsDirectory bool `json:"is_directory"` - Permissions string `json:"permissions"` - Owner string `json:"owner"` - Group string `json:"group"` - ModifiedAt time.Time `json:"modified_at"` - MimeType string `json:"mime_type,omitempty"` - SymlinkTarget string `json:"symlink_target,omitempty"` + Name string `json:"name"` + Path string `json:"path"` + Size int64 `json:"size"` + IsDirectory bool `json:"is_directory"` + Permissions string `json:"permissions"` + Owner string `json:"owner"` + Group string `json:"group"` + ModifiedAt time.Time `json:"modified_at"` + MimeType string `json:"mime_type,omitempty"` + SymlinkTarget string `json:"symlink_target,omitempty"` } // FileOperation represents a file operation result type FileOperation struct { - Operation string `json:"operation"` // "create", "delete", "rename", "copy", "move", "upload", "download" - SourcePath string `json:"source_path"` - TargetPath string `json:"target_path,omitempty"` - Success bool `json:"success"` - Error string `json:"error,omitempty"` + Operation string `json:"operation"` // "create", "delete", "rename", "copy", "move", "upload", "download" + SourcePath string `json:"source_path"` + TargetPath string `json:"target_path,omitempty"` + Success bool `json:"success"` + Error string `json:"error,omitempty"` BytesProcessed int64 `json:"bytes_processed,omitempty"` } @@ -384,9 +384,9 @@ func (h *Handler) GetFileContent(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "path": path, - "size": info.Size(), - "content": string(content), + "path": path, + "size": info.Size(), + "content": string(content), "encoding": "utf-8", }) } @@ -439,11 +439,11 @@ func (h *Handler) UploadFile(c *gin.Context) { h.logFileOperation(sessionID, userID, "upload", filepath.Join(targetPath, header.Filename), "", bytesWritten) c.JSON(http.StatusOK, gin.H{ - "message": "file uploaded successfully", - "filename": header.Filename, - "size": header.Size, - "bytes_written": bytesWritten, - "path": filepath.Join(targetPath, header.Filename), + "message": "file uploaded successfully", + "filename": header.Filename, + "size": header.Size, + "bytes_written": bytesWritten, + "path": filepath.Join(targetPath, header.Filename), }) } @@ -647,24 +647,6 @@ func (h *Handler) RenameFile(c *gin.Context) { // Helper functions -func (h *Handler) canAccessSession(userID, sessionID string) bool { - var owner string - h.DB.QueryRow("SELECT user_id FROM sessions WHERE id = $1", sessionID).Scan(&owner) - if owner == userID { - return true - } - - // Check shared access - var hasAccess bool - h.DB.QueryRow(` - SELECT EXISTS( - SELECT 1 FROM session_shares - WHERE session_id = $1 AND shared_with_user_id = $2 - ) - `, sessionID, userID).Scan(&hasAccess) - return hasAccess -} - func (h *Handler) getSessionBasePath(sessionID string) string { // In production, this would return the actual path to the session's persistent volume // For now, return a placeholder diff --git a/api/internal/handlers/sessiontemplates.go b/api/internal/handlers/sessiontemplates.go index 9894be25..6b48688e 100644 --- a/api/internal/handlers/sessiontemplates.go +++ b/api/internal/handlers/sessiontemplates.go @@ -1045,8 +1045,8 @@ func (h *SessionTemplatesHandler) canManageTemplate(ctx context.Context, templat // Template versioning implementations -// TemplateVersion represents a version snapshot of a template -type TemplateVersion struct { +// TemplateSnapshot represents a version snapshot of a template +type TemplateSnapshot struct { ID int `json:"id"` TemplateID string `json:"templateId"` VersionNumber int `json:"versionNumber"` @@ -1101,9 +1101,9 @@ func (h *SessionTemplatesHandler) ListTemplateVersions(c *gin.Context) { } defer rows.Close() - versions := []TemplateVersion{} + versions := []TemplateSnapshot{} for rows.Next() { - var version TemplateVersion + var version TemplateSnapshot var templateDataJSON []byte var tagsArray sql.NullString diff --git a/api/internal/websocket/handlers.go b/api/internal/websocket/handlers.go index fd37b7c0..9ca3bb29 100644 --- a/api/internal/websocket/handlers.go +++ b/api/internal/websocket/handlers.go @@ -235,19 +235,22 @@ func (m *Manager) broadcastSessionUpdates() { for _, session := range sessions { // Get active connections count from database var activeConns int - err := m.db.DB().QueryRowContext(ctx, ` + if err := m.db.DB().QueryRowContext(ctx, ` SELECT active_connections FROM sessions WHERE id = $1 - `, session.Name).Scan(&activeConns) + `, session.Name).Scan(&activeConns); err != nil { + // If query fails, default to 0 + activeConns = 0 + } sessionData := map[string]interface{}{ - "name": session.Name, - "namespace": session.Namespace, - "user": session.User, - "template": session.Template, - "state": session.State, - "status": session.Status, - "createdAt": session.CreatedAt, - "activeConnections": activeConns, + "name": session.Name, + "namespace": session.Namespace, + "user": session.User, + "template": session.Template, + "state": session.State, + "status": session.Status, + "createdAt": session.CreatedAt, + "activeConnections": activeConns, } if session.Resources.Memory != "" || session.Resources.CPU != "" { @@ -279,9 +282,9 @@ func (m *Manager) broadcastSessionUpdates() { // Broadcast to all clients message := map[string]interface{}{ - "type": "sessions_update", - "sessions": enrichedSessions, - "count": len(enrichedSessions), + "type": "sessions_update", + "sessions": enrichedSessions, + "count": len(enrichedSessions), "timestamp": time.Now().Format(time.RFC3339), }