feat(workspace): 工作空间端点按会话解析项目(session_id)

- workspace_popup/tree/files/view/open/delete/upload/file + office_read/save 读 session_id 传 get_workspace_dir
- 内部 URL 链路(查看/编辑/删除/上传/文件/vi编辑/univer-office)透传 session_id
- workspace_edit.xterm 手写项目解析收敛到 get_workspace_dir(session_id)
This commit is contained in:
ymq 2026-08-24 16:40:49 +08:00
parent 7fdbb98117
commit 524abb23e6
11 changed files with 45 additions and 35 deletions

View File

@ -8,10 +8,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or not ws_dir:
return {"error": "未指定文件或无工作空间", "markdown": ""}

View File

@ -10,10 +10,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or not ws_dir:
return {"success": False, "error": "未指定文件或无工作空间"}

View File

@ -8,10 +8,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or file_id == '__root__':
return {"status": "error", "message": "不能删除根目录"}

View File

@ -11,10 +11,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or file_id == '__root__':
return {"widgettype": "Message", "options": {"title": "错误", "message": "未指定文件"}}

View File

@ -9,10 +9,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not folder_id or folder_id == '__root__':
full_dir = ws_dir
@ -22,7 +24,7 @@ else:
if not os.path.isdir(full_dir):
return {"widgettype": "Text", "options": {"text": "目录不可用: " + folder_id, "cfontsize": 0.9}}
upload_url = entire_url("/pipeline-sdlc/api/workspace_upload.dspy")
upload_url = entire_url("/pipeline-sdlc/api/workspace_upload.dspy") + (("?session_id=" + session_id) if session_id else "")
# 拖拽上传脚本:读取文件 → base64 → form-encoded POST → 刷新浏览器
upload_script = (

View File

@ -10,10 +10,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id:
return {"widgettype": "Message", "options": {"title": "打开失败", "message": "未指定文件"}}
@ -34,11 +36,11 @@ audio_exts = ['.mp3', '.wav', '.aac', '.flac', '.m4a', '.ogg']
image_exts = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico']
office_exts = ['.docx'] # 在线编辑univer-office。xlsx/ppt 后续接入 Univer sheet/slide 插件
file_url = entire_url("/pipeline-sdlc/api/workspace_file.dspy") + "?id=" + quote(file_id)
file_url = entire_url("/pipeline-sdlc/api/workspace_file.dspy") + "?id=" + quote(file_id) + (("&session_id=" + session_id) if session_id else "")
# 1. 文本/源码 → Wterm + vi 编辑
if ext in text_exts:
ws_url = entire_url("/wss/pipeline-sdlc/workspace_edit.xterm") + "?id=" + quote(file_id)
ws_url = entire_url("/wss/pipeline-sdlc/workspace_edit.xterm") + "?id=" + quote(file_id) + (("&session_id=" + session_id) if session_id else "")
return {
"widgettype": "PopupWindow",
"options": {"title": name + " (vi 编辑)", "width": "85%", "height": "85%", "auto_open": True, "resizable": True},
@ -87,7 +89,7 @@ if ext in image_exts:
# 4. office 文档 → iframe 嵌入 univer-office 在线编辑
if ext in office_exts:
univer_url = entire_url("/univer-office/") + "?file=" + quote(file_id)
univer_url = entire_url("/univer-office/") + "?file=" + quote(file_id) + (("&session_id=" + session_id) if session_id else "")
return {
"widgettype": "PopupWindow",
"options": {"title": name + " (在线编辑)", "width": "85%", "height": "85%", "auto_open": True, "resizable": True},

View File

@ -9,8 +9,11 @@ if not uid:
dbname = get_module_dbname('pipeline-sdlc')
import os
session_id = (params_kw or {}).get('session_id', '') or ''
_sid_q = ("?session_id=" + session_id) if session_id else ""
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
pname = os.path.basename(ws_dir) if ws_dir else ''
@ -26,12 +29,13 @@ async with DBPools().sqlorContext(dbname) as sor:
else:
view_url = entire_url("/pipeline-sdlc/api/workspace_view.dspy")
edit_url = entire_url("/pipeline-sdlc/api/workspace_open.dspy")
delete_url = entire_url("/pipeline-sdlc/api/workspace_delete.dspy")
delete_url = entire_url("/pipeline-sdlc/api/workspace_delete.dspy") + _sid_q
_sid_js = _json.dumps(("&session_id=" + session_id) if session_id else "")
# 查看txt/md 用 MdWidget 渲染,其他下载查看
view_script = (
"if(!bricks.app._ws_sel)return;"
"var r=await fetch(" + _json.dumps(view_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id));"
"var r=await fetch(" + _json.dumps(view_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id)+" + _sid_js + ");"
"var d=await r.json();"
"bricks.widgetBuild(d,bricks.app);"
)
@ -39,7 +43,7 @@ async with DBPools().sqlorContext(dbname) as sor:
# 编辑:文本 vi 编辑、媒体播放、图片预览、其他下载
edit_script = (
"if(!bricks.app._ws_sel)return;"
"var r=await fetch(" + _json.dumps(edit_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id));"
"var r=await fetch(" + _json.dumps(edit_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id)+" + _sid_js + ");"
"var d=await r.json();"
"bricks.widgetBuild(d,bricks.app);"
)
@ -73,7 +77,7 @@ async with DBPools().sqlorContext(dbname) as sor:
"tree_options": {
"widgettype": "Tree",
"options": {
"dataurl": entire_url("/pipeline-sdlc/api/workspace_tree.dspy"),
"dataurl": entire_url("/pipeline-sdlc/api/workspace_tree.dspy") + _sid_q,
"textField": "label",
"idField": "id",
"cfontsize": 1.0
@ -82,7 +86,7 @@ async with DBPools().sqlorContext(dbname) as sor:
"browser_options": {
"widgettype": "urlwidget",
"options": {
"url": entire_url("/pipeline-sdlc/api/workspace_files.dspy")
"url": entire_url("/pipeline-sdlc/api/workspace_files.dspy") + _sid_q
},
"tools": [
{

View File

@ -8,10 +8,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not ws_dir or not os.path.isdir(ws_dir):
if not node_id:

View File

@ -11,10 +11,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not ws_dir or not os.path.isdir(ws_dir):
return {"status": "error", "message": "工作空间目录不可用"}

View File

@ -10,10 +10,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or file_id == '__root__':
return {"widgettype": "Message", "options": {"title": "查看失败", "message": "未指定文件"}}
@ -32,7 +34,7 @@ if not os.path.isfile(full_path):
name = os.path.basename(full_path)
ext = os.path.splitext(name)[1].lower()
file_url = entire_url("/pipeline-sdlc/api/workspace_file.dspy") + "?id=" + quote(file_id)
file_url = entire_url("/pipeline-sdlc/api/workspace_file.dspy") + "?id=" + quote(file_id) + (("&session_id=" + session_id) if session_id else "")
# txt/md → VScrollPanel 包裹 MdWidget 渲染(可滚动,长文档不截断)
md_exts = ['.md', '.txt']

View File

@ -8,24 +8,12 @@ uid = await get_user()
if not uid:
uid = 'user-01'
session_id = (params_kw or {}).get('session_id', '') or ''
dbname = get_module_dbname('pipeline-sdlc')
workspace_base = '/d/pipeline/workspaces'
async with DBPools().sqlorContext(dbname) as sor:
recs = await sor.sqlExe(
"SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": uid})
pid = getattr(recs[0], 'current_project_id', '') if recs else ''
ws_dir = ''
if pid:
proj = await sor.sqlExe("SELECT name, org_id, workspace_dir FROM sd_projects WHERE id=${p}$", {"p": pid})
if proj:
ws = getattr(proj[0], 'workspace_dir', '') or ''
if ws.startswith('/'):
ws_dir = ws
else:
pname = getattr(proj[0], 'name', '')
org_id = getattr(proj[0], 'org_id', '0') or '0'
ws_dir = workspace_base + '/' + org_id + '/' + pname
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
if not file_id or not ws_dir:
r = DictObject()