fix(admin): 修复管理面板创建旧式命名孤儿容器,切除 app_legacy 旧架构死代码
This commit is contained in:
parent
cf51ab0555
commit
b78504c31e
@ -209,13 +209,23 @@ def admin_dashboard_snapshot_api():
|
|||||||
return guard
|
return guard
|
||||||
try:
|
try:
|
||||||
# 若当前管理员没有容器句柄,主动确保容器存在,避免面板始终显示“宿主机模式”
|
# 若当前管理员没有容器句柄,主动确保容器存在,避免面板始终显示“宿主机模式”
|
||||||
|
# 注意:容器 key 必须带工作区后缀({username}::{workspace_id}),
|
||||||
|
# 否则会创建出旧式命名的 agent-term-{username} 孤儿容器。
|
||||||
try:
|
try:
|
||||||
record = get_current_user_record()
|
record = get_current_user_record()
|
||||||
uname = record.username if record else None
|
uname = record.username if record else None
|
||||||
if uname:
|
if uname:
|
||||||
handles = state.container_manager.list_containers()
|
handles = state.container_manager.list_containers()
|
||||||
if uname not in handles:
|
default_ws_id = "default"
|
||||||
state.container_manager.ensure_container(uname, str(state.user_manager.ensure_user_workspace(uname).project_path), preferred_mode="docker")
|
container_key = f"{uname}::{default_ws_id}"
|
||||||
|
if container_key not in handles:
|
||||||
|
workspace = state.user_manager.ensure_user_workspace(uname, default_ws_id)
|
||||||
|
state.container_manager.ensure_container(
|
||||||
|
uname,
|
||||||
|
str(workspace.project_path),
|
||||||
|
container_key=container_key,
|
||||||
|
preferred_mode="docker",
|
||||||
|
)
|
||||||
except Exception as ensure_exc:
|
except Exception as ensure_exc:
|
||||||
logging.getLogger(__name__).warning("ensure_container for admin failed: %s", ensure_exc)
|
logging.getLogger(__name__).warning("ensure_container for admin failed: %s", ensure_exc)
|
||||||
|
|
||||||
|
|||||||
@ -881,178 +881,6 @@ def admin_api_required(view_func):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def make_terminal_callback(username: str):
|
|
||||||
"""生成面向指定用户的广播函数"""
|
|
||||||
def _callback(event_type, data):
|
|
||||||
try:
|
|
||||||
socketio.emit(event_type, data, room=f"user_{username}")
|
|
||||||
except Exception as exc:
|
|
||||||
debug_log(f"广播事件失败 ({username}): {event_type} - {exc}")
|
|
||||||
return _callback
|
|
||||||
|
|
||||||
|
|
||||||
def attach_user_broadcast(terminal: WebTerminal, username: str):
|
|
||||||
"""确保终端的广播函数指向当前用户的房间"""
|
|
||||||
callback = make_terminal_callback(username)
|
|
||||||
terminal.message_callback = callback
|
|
||||||
if terminal.terminal_manager:
|
|
||||||
terminal.terminal_manager.broadcast = callback
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_resources(username: Optional[str] = None) -> Tuple[Optional[WebTerminal], Optional[UserWorkspace]]:
|
|
||||||
username = (username or get_current_username())
|
|
||||||
if not username:
|
|
||||||
return None, None
|
|
||||||
record = get_current_user_record()
|
|
||||||
workspace = user_manager.ensure_user_workspace(username)
|
|
||||||
container_handle = container_manager.ensure_container(username, str(workspace.project_path), preferred_mode="docker")
|
|
||||||
usage_tracker = get_or_create_usage_tracker(username, workspace)
|
|
||||||
terminal = user_terminals.get(username)
|
|
||||||
if not terminal:
|
|
||||||
run_mode = session.get('run_mode')
|
|
||||||
thinking_mode_flag = session.get('thinking_mode')
|
|
||||||
if run_mode not in {"fast", "thinking", "deep"}:
|
|
||||||
preferred_run_mode = None
|
|
||||||
try:
|
|
||||||
personal_config = load_personalization_config(workspace.data_dir)
|
|
||||||
candidate_mode = (personal_config or {}).get('default_run_mode')
|
|
||||||
if isinstance(candidate_mode, str) and candidate_mode.lower() in {"fast", "thinking", "deep"}:
|
|
||||||
preferred_run_mode = candidate_mode.lower()
|
|
||||||
except Exception as exc:
|
|
||||||
debug_log(f"[UserInit] 加载个性化偏好失败: {exc}")
|
|
||||||
|
|
||||||
if preferred_run_mode:
|
|
||||||
run_mode = preferred_run_mode
|
|
||||||
thinking_mode_flag = preferred_run_mode != "fast"
|
|
||||||
elif thinking_mode_flag:
|
|
||||||
run_mode = "deep"
|
|
||||||
else:
|
|
||||||
run_mode = "fast"
|
|
||||||
thinking_mode = run_mode != "fast"
|
|
||||||
terminal = WebTerminal(
|
|
||||||
project_path=str(workspace.project_path),
|
|
||||||
thinking_mode=thinking_mode,
|
|
||||||
run_mode=run_mode,
|
|
||||||
message_callback=make_terminal_callback(username),
|
|
||||||
data_dir=str(workspace.data_dir),
|
|
||||||
container_session=container_handle,
|
|
||||||
usage_tracker=usage_tracker
|
|
||||||
)
|
|
||||||
if terminal.terminal_manager:
|
|
||||||
terminal.terminal_manager.broadcast = terminal.message_callback
|
|
||||||
user_terminals[username] = terminal
|
|
||||||
terminal.username = username
|
|
||||||
terminal.user_role = get_current_user_role(record)
|
|
||||||
terminal.quota_update_callback = lambda metric=None: emit_user_quota_update(username)
|
|
||||||
session['run_mode'] = terminal.run_mode
|
|
||||||
session['thinking_mode'] = terminal.thinking_mode
|
|
||||||
else:
|
|
||||||
terminal.update_container_session(container_handle)
|
|
||||||
attach_user_broadcast(terminal, username)
|
|
||||||
terminal.username = username
|
|
||||||
terminal.user_role = get_current_user_role(record)
|
|
||||||
terminal.quota_update_callback = lambda metric=None: emit_user_quota_update(username)
|
|
||||||
|
|
||||||
# 应用管理员策略(工具分类、强制开关、模型禁用)
|
|
||||||
try:
|
|
||||||
from core.tool_config import ToolCategory
|
|
||||||
|
|
||||||
policy = resolve_admin_policy(user_manager.get_user(username))
|
|
||||||
categories_map = {
|
|
||||||
cid: ToolCategory(
|
|
||||||
label=cat.get("label") or cid,
|
|
||||||
tools=list(cat.get("tools") or []),
|
|
||||||
default_enabled=bool(cat.get("default_enabled", True)),
|
|
||||||
silent_when_disabled=bool(cat.get("silent_when_disabled", False)),
|
|
||||||
)
|
|
||||||
for cid, cat in policy.get("categories", {}).items()
|
|
||||||
}
|
|
||||||
forced_states = policy.get("forced_category_states") or {}
|
|
||||||
disabled_models = policy.get("disabled_models") or []
|
|
||||||
terminal.set_admin_policy(categories_map, forced_states, disabled_models)
|
|
||||||
terminal.admin_policy_ui_blocks = policy.get("ui_blocks") or {}
|
|
||||||
terminal.admin_policy_version = policy.get("updated_at")
|
|
||||||
# 若当前模型被禁用,则回退到第一个可用模型
|
|
||||||
if terminal.model_key in disabled_models:
|
|
||||||
for candidate in get_registered_model_keys(visible_only=True):
|
|
||||||
if candidate not in disabled_models:
|
|
||||||
try:
|
|
||||||
terminal.set_model(candidate)
|
|
||||||
session["model_key"] = terminal.model_key
|
|
||||||
break
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
except Exception as exc:
|
|
||||||
debug_log(f"[admin_policy] 应用失败: {exc}")
|
|
||||||
return terminal, workspace
|
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_usage_tracker(username: Optional[str], workspace: Optional[UserWorkspace] = None) -> Optional[UsageTracker]:
|
|
||||||
if not username:
|
|
||||||
return None
|
|
||||||
tracker = usage_trackers.get(username)
|
|
||||||
if tracker:
|
|
||||||
return tracker
|
|
||||||
if workspace is None:
|
|
||||||
workspace = user_manager.ensure_user_workspace(username)
|
|
||||||
record = user_manager.get_user(username)
|
|
||||||
role = getattr(record, "role", "user") if record else "user"
|
|
||||||
tracker = UsageTracker(str(workspace.data_dir), role=role or "user")
|
|
||||||
usage_trackers[username] = tracker
|
|
||||||
return tracker
|
|
||||||
|
|
||||||
|
|
||||||
def emit_user_quota_update(username: Optional[str]):
|
|
||||||
if not username:
|
|
||||||
return
|
|
||||||
tracker = get_or_create_usage_tracker(username)
|
|
||||||
if not tracker:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
snapshot = tracker.get_quota_snapshot()
|
|
||||||
socketio.emit('quota_update', {
|
|
||||||
'quotas': snapshot
|
|
||||||
}, room=f"user_{username}")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def with_terminal(func):
|
|
||||||
"""注入用户专属终端和工作区"""
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
username = get_current_username()
|
|
||||||
try:
|
|
||||||
terminal, workspace = get_user_resources(username)
|
|
||||||
except RuntimeError as exc:
|
|
||||||
return jsonify({"error": str(exc), "code": "resource_busy"}), 503
|
|
||||||
if not terminal or not workspace:
|
|
||||||
return jsonify({"error": "System not initialized"}), 503
|
|
||||||
kwargs.update({
|
|
||||||
'terminal': terminal,
|
|
||||||
'workspace': workspace,
|
|
||||||
'username': username
|
|
||||||
})
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
def get_terminal_for_sid(sid: str) -> Tuple[Optional[str], Optional[WebTerminal], Optional[UserWorkspace]]:
|
|
||||||
username = connection_users.get(sid)
|
|
||||||
if not username:
|
|
||||||
return None, None, None
|
|
||||||
try:
|
|
||||||
terminal, workspace = get_user_resources(username)
|
|
||||||
except RuntimeError:
|
|
||||||
return username, None, None
|
|
||||||
return username, terminal, workspace
|
|
||||||
|
|
||||||
|
|
||||||
def get_gui_manager(workspace: UserWorkspace) -> GuiFileManager:
|
|
||||||
"""构建 GUI 文件管理器"""
|
|
||||||
return GuiFileManager(str(workspace.project_path))
|
|
||||||
|
|
||||||
|
|
||||||
def get_upload_guard(workspace: UserWorkspace) -> UploadQuarantineManager:
|
def get_upload_guard(workspace: UserWorkspace) -> UploadQuarantineManager:
|
||||||
"""构建上传隔离管理器"""
|
"""构建上传隔离管理器"""
|
||||||
return UploadQuarantineManager(workspace)
|
return UploadQuarantineManager(workspace)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user