Files
aiagent/backend/app/websocket/manager.py
renjianbo 876789fac1 feat: multi-tenant workspace isolation, RBAC, sidebar nav, billing, and Android enhancements
- Backend: workspace_id isolation for 14 model tables + safe migration/backfill
- Backend: RBAC system with 4 roles and 23 permissions, seeded on startup
- Backend: workspace admin endpoints (list/manage all workspaces)
- Backend: admin user management API (CRUD, reset password)
- Backend: billing API with subscription plans, usage tracking, rate limiting
- Backend: fix system_logs.py UNION query and wrong column references
- Backend: WebSocket JWT auth and workspace enforcement
- Frontend: sidebar navigation replacing top dropdown menu
- Frontend: user management page (Users.vue) for admins
- Frontend: enhanced Workspaces.vue with admin table view
- Frontend: workspace RBAC computed properties in user store
- Android: agent marketplace, billing/subscription UI, onboarding wizard
- Android: phone login, analytics tracker, crash handler, network diagnostics
- Android: splash screen, encrypted token storage, app update enhancements
- Docs: multi-tenant RBAC guide with 8 sections and role-permission matrix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-04 01:00:22 +08:00

87 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
WebSocket连接管理器 — 支持 workspace 隔离
"""
from typing import Dict, Set, Optional
from fastapi import WebSocket
import json
class WebSocketManager:
"""WebSocket连接管理器workspace 感知)"""
def __init__(self):
# execution_id -> Set[WebSocket]
self.active_connections: Dict[str, Set[WebSocket]] = {}
# websocket -> workspace_id (用于 workspace 隔离广播)
self._ws_workspace: Dict[int, str] = {}
async def connect(
self, websocket: WebSocket, execution_id: str, workspace_id: Optional[str] = None
):
"""建立 WebSocket 连接,标记 workspace。"""
await websocket.accept()
if execution_id not in self.active_connections:
self.active_connections[execution_id] = set()
self.active_connections[execution_id].add(websocket)
if workspace_id:
self._ws_workspace[id(websocket)] = workspace_id
def disconnect(self, websocket: WebSocket, execution_id: str):
"""断开 WebSocket 连接,清理 workspace 标记。"""
self._ws_workspace.pop(id(websocket), None)
if execution_id in self.active_connections:
self.active_connections[execution_id].discard(websocket)
if not self.active_connections[execution_id]:
del self.active_connections[execution_id]
def get_workspace_id(self, websocket: WebSocket) -> Optional[str]:
"""获取连接关联的 workspace_id。"""
return self._ws_workspace.get(id(websocket))
async def send_personal_message(self, message: dict, websocket: WebSocket):
"""
发送个人消息
Args:
message: 消息内容
websocket: WebSocket连接
"""
try:
await websocket.send_json(message)
except Exception as e:
print(f"发送WebSocket消息失败: {e}")
async def broadcast_to_execution(self, execution_id: str, message: dict):
"""
向特定执行的所有连接广播消息
Args:
execution_id: 执行记录ID
message: 消息内容
"""
if execution_id not in self.active_connections:
return
# 需要断开连接的连接
disconnected = set()
for websocket in self.active_connections[execution_id]:
try:
await websocket.send_json(message)
except Exception as e:
print(f"广播消息失败: {e}")
disconnected.add(websocket)
# 移除断开的连接
for websocket in disconnected:
self.disconnect(websocket, execution_id)
# 全局WebSocket管理器实例
websocket_manager = WebSocketManager()