diff --git a/scripts/load_path.py b/scripts/load_path.py index ceb4501..4f3b6c3 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -111,6 +111,9 @@ PATHS_LOGINED = [ # 工作环境 / 部署账号(普通用户可用) f"/{MOD}/api/work_env.dspy", f"/{MOD}/api/deploy_account.dspy", + # 机构远程空间配置(页面 logined,org 级设置由 API 层校验 superuser) + f"/{MOD}/sd_org_remote/index.ui", + f"/{MOD}/sd_org_remote", ] diff --git a/wwwroot/sd_org_remote/index.ui b/wwwroot/sd_org_remote/index.ui new file mode 100644 index 0000000..911dc53 --- /dev/null +++ b/wwwroot/sd_org_remote/index.ui @@ -0,0 +1,119 @@ +{ + "widgettype": "VBox", + "options": {"width": "100%", "height": "100%", "padding": "24px", "gap": "16px"}, + "subwidgets": [ + { + "widgettype": "Html", + "options": { + "html": "", + "height": "0", + "width": "0" + } + }, + { + "widgettype": "Title3", + "options": {"text": "机构远程空间配置"} + }, + { + "widgettype": "Text", + "options": { + "text": "设置机构的远程工作空间。远程模式下,机构所有用户的工作目录与沙箱命令都在远程主机执行(远程主机需部署 bwrap)。", + "cfontsize": 0.9, + "color": "#64748b" + } + }, + { + "widgettype": "VBox", + "options": {"bgcolor": "#fff", "border": "1px solid #e2e8f0", "borderRadius": "12px", "padding": "20px", "gap": "12px"}, + "subwidgets": [ + { + "widgettype": "Form", + "id": "remote_form", + "options": { + "name": "remote_form", + "cols": 2, + "fields": [ + { + "name": "mode", + "uitype": "code", + "label": "工作模式", + "cwidth": 12, + "data": [ + {"value": "local", "text": "本地(bwrap 沙箱)"}, + {"value": "remote", "text": "远程(SSH + bwrap 沙箱)"} + ], + "valueField": "value", + "textField": "text" + }, + { + "name": "remote_host", + "uitype": "str", + "label": "远程主机", + "cwidth": 24, + "placeholder": "例如 192.168.1.10 或 remote.example.com" + }, + { + "name": "remote_port", + "uitype": "str", + "label": "远程端口", + "cwidth": 8, + "value": "22" + }, + { + "name": "remote_user", + "uitype": "str", + "label": "远程用户", + "cwidth": 12 + }, + { + "name": "remote_dir", + "uitype": "str", + "label": "远程目录", + "cwidth": 24, + "placeholder": "绝对路径,例如 /data/pipeline/ws" + } + ] + } + }, + { + "widgettype": "HBox", + "options": {"gap": "12px"}, + "subwidgets": [ + { + "widgettype": "Button", + "id": "save_btn", + "options": {"label": "保存配置"}, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "script", "target": "self", + "script": "var f=bricks.getWidgetById('remote_form',bricks.app);var v=f?f.getValue():{};var body=new URLSearchParams();body.append('action','set');body.append('owner_type','org');body.append('mode',v.mode||'local');body.append('remote_host',v.remote_host||'');body.append('remote_port',v.remote_port||'22');body.append('remote_user',v.remote_user||'');body.append('remote_dir',v.remote_dir||'');fetch('/pipeline-sdlc/api/work_env.dspy',{method:'POST',body:body}).then(function(r){return r.json()}).then(function(d){var m=new bricks.Message({title:d.ok?'成功':'失败',message:d.ok?'远程空间配置已保存':(d.error||'未知错误')});m.open();});" + }] + }, + { + "widgettype": "Button", + "id": "test_btn", + "options": {"label": "测试远程连接"}, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "script", "target": "self", + "script": "var f=bricks.getWidgetById('remote_form',bricks.app);var v=f?f.getValue():{};var body=new URLSearchParams();body.append('action','ensure_remote_bwrap');body.append('remote_host',v.remote_host||'');body.append('remote_port',v.remote_port||'22');body.append('remote_user',v.remote_user||'');fetch('/pipeline-sdlc/api/work_env.dspy',{method:'POST',body:body}).then(function(r){return r.json()}).then(function(d){var m=new bricks.Message({title:d.ok?'连接成功':'连接失败',message:d.ok?('远程 bwrap 位置: '+(d.location||'unknown')):(d.error||'未知错误')});m.open();});" + }] + }, + { + "widgettype": "Button", + "id": "pubkey_btn", + "options": {"label": "获取机构公钥"}, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "script", "target": "self", + "script": "fetch('/pipeline-sdlc/api/work_env.dspy?action=get_org_pubkey').then(function(r){return r.json()}).then(function(d){if(d.ok){var t=bricks.getWidgetById('pubkey_text',bricks.app);if(t)t.set_text('机构公钥(复制到远程主机 ~/.ssh/authorized_keys):\\n'+d.pubkey);}else{var m=new bricks.Message({title:'失败',message:d.error||'未知错误'});m.open();}});" + }] + } + ] + }, + { + "widgettype": "Text", + "id": "pubkey_text", + "options": {"text": "", "whiteSpace": "pre-wrap", "cfontsize": 0.85, "color": "#334155"} + } + ] + } + ] +}