131 lines
4.2 KiB
Plaintext
131 lines
4.2 KiB
Plaintext
# workspace_tree.dspy - 项目目录树:根=项目目录,额外挂项目用到的 apps/modules 子目录
|
||
|
||
import os
|
||
|
||
node_id = (params_kw or {}).get('id', '').strip()
|
||
# 前端 id 可能被多层编码,循环解码(2026-09-03 中文目录名根因)
|
||
from urllib.parse import unquote as _unq
|
||
for _ in range(4):
|
||
if '%' not in node_id:
|
||
break
|
||
_s2 = _unq(node_id)
|
||
if _s2 == node_id:
|
||
break
|
||
node_id = _s2
|
||
|
||
uid = await get_user()
|
||
if not uid:
|
||
uid = 'user-01'
|
||
|
||
session_id = (params_kw or {}).get('session_id', '') or ''
|
||
pipeline_id = (params_kw or {}).get('pipeline_id', '') or ''
|
||
|
||
dbname = get_module_dbname('pipeline-sdlc')
|
||
|
||
async with DBPools().sqlorContext(dbname) as sor:
|
||
# 产线隔离:跨产线项目视为无项目(与弹窗入口一致,防绕过)
|
||
project_dir, _ = await get_project_dir_pl(sor, uid, session_id, pipeline_id)
|
||
space_dir, _ = await get_space_dir(sor, uid, session_id)
|
||
# 通用会话(pipeline_id=_generic):锁用户专属目录 _general/{uid},
|
||
# 不进入任何项目的工作空间(2026-09-08 用户要求)
|
||
if pipeline_id == '_generic':
|
||
_gd = generic_workspace_dir(uid)
|
||
os.makedirs(_gd, exist_ok=True)
|
||
project_dir = _gd
|
||
space_dir = _gd
|
||
# 提前读出项目用到的 apps/modules(sor 在块内有效,块外调用会因 sor 关闭返回空)
|
||
_apps, _modules = await get_project_apps_modules(sor, uid, session_id)
|
||
|
||
if not project_dir or not os.path.isdir(project_dir):
|
||
if not node_id:
|
||
return [{"id": "__root__", "parentid": "", "label": "📁 项目目录(不可用)", "path": project_dir, "is_leaf": False}]
|
||
return []
|
||
|
||
# 初始加载:只返回根节点
|
||
if not node_id:
|
||
return [{
|
||
"id": "__root__",
|
||
"parentid": "",
|
||
"label": "📁 " + os.path.basename(project_dir),
|
||
"path": project_dir,
|
||
"is_leaf": False
|
||
}]
|
||
|
||
# 展开节点:__root__ 特殊处理(加 apps/modules 引用),@apps/@modules 扫描机构工作空间,其余扫描项目目录
|
||
if node_id == '__root__':
|
||
scan_path = project_dir
|
||
id_prefix = ''
|
||
extra_children = True
|
||
elif node_id.startswith('@apps/'):
|
||
scan_path = os.path.join(space_dir, 'apps', node_id[len('@apps/'):])
|
||
id_prefix = node_id + '/'
|
||
extra_children = False
|
||
elif node_id.startswith('@modules/'):
|
||
scan_path = os.path.join(space_dir, 'modules', node_id[len('@modules/'):])
|
||
id_prefix = node_id + '/'
|
||
extra_children = False
|
||
else:
|
||
scan_path = resolve_workspace_path(project_dir, space_dir, node_id)
|
||
id_prefix = node_id + '/'
|
||
extra_children = False
|
||
|
||
if not os.path.isdir(scan_path):
|
||
return []
|
||
|
||
items = []
|
||
|
||
# 根节点:额外挂项目用到的 apps 子目录 + modules 子目录
|
||
if extra_children:
|
||
apps, modules = _apps, _modules
|
||
for app in apps:
|
||
app_path = os.path.join(space_dir, 'apps', app)
|
||
if os.path.isdir(app_path):
|
||
items.append({
|
||
"id": "@apps/" + app,
|
||
"parentid": node_id,
|
||
"label": "📱 " + app,
|
||
"path": app_path,
|
||
"is_leaf": False
|
||
})
|
||
for m in modules:
|
||
m_path = os.path.join(space_dir, 'modules', m)
|
||
if os.path.isdir(m_path):
|
||
items.append({
|
||
"id": "@modules/" + m,
|
||
"parentid": node_id,
|
||
"label": "📦 " + m,
|
||
"path": m_path,
|
||
"is_leaf": False
|
||
})
|
||
|
||
# 扫描常规内容(子目录)
|
||
try:
|
||
entries = sorted(os.listdir(scan_path))
|
||
except Exception:
|
||
entries = []
|
||
|
||
for name in entries:
|
||
full = os.path.join(scan_path, name)
|
||
if name.startswith('.') or name == '__pycache__':
|
||
continue
|
||
if os.path.isdir(full):
|
||
has_subdir = False
|
||
try:
|
||
for sub in os.listdir(full):
|
||
if sub.startswith('.') or sub == '__pycache__':
|
||
continue
|
||
if os.path.isdir(os.path.join(full, sub)):
|
||
has_subdir = True
|
||
break
|
||
except Exception:
|
||
pass
|
||
items.append({
|
||
"id": id_prefix + name,
|
||
"parentid": node_id,
|
||
"label": "📁 " + name,
|
||
"path": full,
|
||
"is_leaf": not has_subdir
|
||
})
|
||
|
||
return items
|