# cockpit_context_update.dspy - Save user's selected project/iteration to pipeline_agent_settings # POST params: project_id, iteration_id (at least one required) # Also looks up names for the newly set values import json uid = await get_user() if not uid: return json.dumps({"success": False, "error": "请先登录"}, ensure_ascii=False) pid = (params_kw or {}).get('project_id', '') iid = (params_kw or {}).get('iteration_id', '') session_id = (params_kw or {}).get('session_id', '') if not pid and not iid: return json.dumps({"success": False, "error": "请提供 project_id 或 iteration_id"}, ensure_ascii=False) dbname = get_module_dbname('pipeline-sdlc') # 归属门禁(2026-09-18 银联事故实锤):旧版无校验,任何登录用户可把**他机构** # 项目 id 写进自己的会话指针(cockpit 上下文栏直传),随后工作空间列表/读文件 # 端点按指针解析目录 → 看到他机构项目文件。校验对齐 agent_project_popup 切换 # 门禁:同机构 + 本人创建(owner.superuser 放行,与 _check_confirm_operator 同口径)。 if pid: async with DBPools().sqlorContext(dbname) as _gsor: _pr = await _gsor.sqlExe( "SELECT org_id, created_by FROM sd_projects WHERE id=${p}$ LIMIT 1", {"p": pid}) await _gsor.sqlExe("COMMIT", {}) if not _pr: return json.dumps({"success": False, "error": "项目不存在"}, ensure_ascii=False) _porg = getattr(_pr[0], 'org_id', '') or '' _pby = getattr(_pr[0], 'created_by', '') or '' _uorg = await get_userorgid() or '' if _pby != uid: from pipeline_service.human_task_capability import _get_user_roles _roles = await _get_user_roles(_gsor, uid) if "owner.superuser" not in _roles or (_porg and _uorg != _porg): return json.dumps({"success": False, "error": "无权切换到该项目(仅限本人创建的项目)"}, ensure_ascii=False) async with DBPools().sqlorContext(dbname) as sor: # Upsert agent_settings existing = await sor.sqlExe( "SELECT id, current_project_id, current_iteration_id FROM pipeline_agent_settings WHERE user_id=${uid}$", {"uid": uid} ) if existing: cur_pid = getattr(existing[0], 'current_project_id', '') or '' cur_iid = getattr(existing[0], 'current_iteration_id', '') or '' new_pid = pid if pid else cur_pid new_iid = iid if iid else ('' if pid else cur_iid) # If project changed but iteration not specified, clear iteration if pid and pid != cur_pid and not iid: new_iid = '' await sor.sqlExe( "UPDATE pipeline_agent_settings SET current_project_id=${pid}$, current_iteration_id=${iid}$ WHERE user_id=${uid}$", {"pid": new_pid, "iid": new_iid, "uid": uid} ) else: new_pid = pid new_iid = iid await sor.C('pipeline_agent_settings', { 'id': getID(), 'user_id': uid, 'current_project_id': new_pid, 'current_iteration_id': new_iid, }) # 会话级项目上下文(多 tab 隔离):写 pipeline_session_settings if session_id: try: await sor.sqlExe( "UPDATE pipeline_session_settings SET current_project_id=${pid}$, current_iteration_id=${iid}$ " "WHERE user_id=${uid}$ AND session_id=${sid}$", {"pid": new_pid, "iid": new_iid, "uid": uid, "sid": session_id}) sexists = await sor.sqlExe( "SELECT 1 FROM pipeline_session_settings WHERE user_id=${uid}$ AND session_id=${sid}$", {"uid": uid, "sid": session_id}) if not sexists: await sor.C('pipeline_session_settings', { 'id': getID(), 'user_id': uid, 'session_id': session_id, 'current_project_id': new_pid, 'current_iteration_id': new_iid, }) except Exception: pass # Look up names pname = '' iname = '' ws_dir = '' if new_pid: precs = await sor.sqlExe( "SELECT name, workspace_dir FROM sd_projects WHERE id=${pid}$", {"pid": new_pid} ) if precs and len(precs) > 0: pname = getattr(precs[0], 'name', '') or '' ws_dir = getattr(precs[0], 'workspace_dir', '') or '' if new_iid: irecs = await sor.sqlExe( "SELECT iteration_name FROM sd_iterations WHERE id=${iid}$", {"iid": new_iid} ) if irecs and len(irecs) > 0: iname = getattr(irecs[0], 'iteration_name', '') or '' return json.dumps({ "success": True, "project_id": new_pid, "iteration_id": new_iid, "project_name": pname, "iteration_name": iname, "workspace_dir": ws_dir, }, ensure_ascii=False)