diff --git a/gateway/scripts/specs/easytier-rdp.json b/gateway/scripts/specs/easytier-rdp.json deleted file mode 100644 index 423e4b6..0000000 --- a/gateway/scripts/specs/easytier-rdp.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "module": "easytier-rdp", - "version": "1.0", - "purpose": "EasyTier VPN 网络开关 + RDP 远程桌面隧道开关。允许从外网通过 ags.yoin.fun 控制内网 EasyTier 和 RDP 访问。", - "ui_location": "I Tab (Infrastructure) → EasyTier VPN section + RDP Remote Desktop section", - - "human_help": { - "title": "EasyTier VPN + RDP 远程桌面", - "description": [ - "EasyTier 是一个 P2P VPN 组网工具,用于连接 Windows(192.168.1.16) 和 Linux 246(192.168.1.246)。", - "Turn On/Off 按钮控制两台机器上的 EasyTier 进程。", - "RDP Enable/Disable 按钮控制 SSH 反向隧道,开启后可通过 47.115.32.206:8080 从外网远程桌面到 Windows。" - ], - "usage": [ - "1. 点击 Turn On 启动 EasyTier VPN(两台机器都启动)", - "2. 点击 Turn Off 关闭 EasyTier VPN", - "3. 点击 Enable 启动 RDP 隧道(需先确保 EasyTier 已连接,否则 SSH 可能不通)", - "4. 点击 Disable 关闭 RDP 隧道", - "5. RDP 连接方式:mstsc /v:47.115.32.206:8080" - ], - "troubleshooting": [ - "如果 Turn On 返回失败:检查 Windows xmpp_bot (5802) 是否运行", - "如果 RDP Enable 返回失败:检查 SSH 到 246 是否可用(EasyTier VPN 需先连接)", - "如果隧道状态一直 pending:检查 246 的 SSH GatewayPortsyes 是否配置" - ] - }, - - "ai_spec": { - "apis": [ - {"method": "GET", "path": "/api/easytier", "returns": "status{windows,246} + virtual_ips{windows,246}", "proxied_to": "xmpp_bot /easytier action=status"}, - {"method": "POST", "path": "/api/easytier/toggle", "body": "{action: start|stop}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /easytier action=start|stop"}, - {"method": "GET", "path": "/api/rdp", "returns": "rdp_enabled + tunnel_running + public_endpoint", "proxied_to": "xmpp_bot /rdp action=status"}, - {"method": "POST", "path": "/api/rdp/toggle", "body": "{action: start|stop}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /rdp action=start|stop"} - ], - "dependencies": [ - "xmpp_bot on Windows 192.168.1.16:5802 (HTTP bridge with /easytier and /rdp endpoints)", - "XMPP_BRIDGE_URL env var (default: http://192.168.1.16:5802) — set in systemd service", - "_bridge_post() helper in dashboard.py — proxies POST to xmpp_bot with X-Api-Key header", - "_BRIDGE_KEY = 'xxm_bridge_8f3a2c' — API key for xmpp_bot HTTP bridge", - "port_open() helper in dashboard.py — socket-based port check for tunnel status" - ], - "architecture": { - "flow": "Dashboard(246:5803) → _bridge_post() → xmpp_bot(Windows:5802) → subprocess/system", - "easytier_mechanism": "xmpp_bot receives POST /easytier {action} → starts/stops easytier-core process on respective machine", - "rdp_mechanism": "xmpp_bot receives POST /rdp {action} → starts/stops SSH reverse tunnel: ssh -R 0.0.0.0:8080:localhost:3389 root@47.115.32.206" - }, - "constraints": [ - "VPN IP: Windows=10.144.144.3, 246=10.144.144.1 (NOT .2 — .2 was wrong in old code)", - "RDP SSH reverse tunnel: ssh -R 0.0.0.0:8080:localhost:3389 root@47.115.32.206", - "GatewayPorts yes required in /etc/ssh/sshd_config on 47.115.32.206 (Aliyun)", - "xmpp_bot must be running on Windows with /rdp and /easytier HTTP endpoints (API key: xxm_bridge_8f3a2c)", - "dashboard.py runs on 246 (Linux), xmpp_bot runs on Windows — they communicate over LAN HTTP" - ], - "tests": [ - {"id": "ET01", "name": "EasyTier toggle start returns ok", "endpoint": "POST /api/easytier/toggle {action:start}"}, - {"id": "ET02", "name": "EasyTier toggle stop returns ok", "endpoint": "POST /api/easytier/toggle {action:stop}"}, - {"id": "RDP01", "name": "RDP toggle start returns ok", "endpoint": "POST /api/rdp/toggle {action:start}"}, - {"id": "RDP02", "name": "RDP toggle stop returns ok", "endpoint": "POST /api/rdp/toggle {action:stop}"}, - {"id": "ET-RDP01", "name": "EasyTier status returns virtual_ips", "endpoint": "GET /api/easytier"}, - {"id": "ET-RDP02", "name": "RDP status returns public_endpoint 47.115.32.206:8080", "endpoint": "GET /api/rdp"} - ], - "must_not": [ - "不要把 proxy 调用改成 local subprocess(dashboard 在 Linux,EasyTier/RDP 在 Windows)", - "不要重写整个 fI() 函数来改 EasyTier/RDP 部分 — 用 create-once/update-state pattern", - "不要把 VPN IP 从 .3 改成 .2 — .3 是正确的", - "不要删除 _bridge_post() helper 或 _BRIDGE_KEY — 它们是 proxy 的核心", - "不要在 fI() 里用 fill('infra',h) — 这会清空整个 ct-infra 导致闪烁" - ], - "known_issues": [ - "如果 xmpp_bot (5802) 停了,所有 EasyTier/RDP 操作都会失败 — 检查 Windows pythonw.exe 进程", - "如果 246 上 SSH 到 Windows 不通,RDP 隧道无法启动 — 先确保 EasyTier VPN 连接", - "EasyTier status 246 字段可能显示 unknown — 这是正常的,因为 246 的 EasyTier 状态不一定通过 xmpp_bot 返回" - ], - "related_files": [ - "gateway/scripts/dashboard.py — /api/easytier*, /api/rdp*, _bridge_post(), port_open()", - "gateway/scripts/templates/dashboard.html — fI() function creates ET/RDP sections", - "xmpp_agent_core.py — /easytier and /rdp HTTP endpoints (lines ~780, ~794)", - "/etc/systemd/system/agentsmeeting-dashboard.service — XMPP_BRIDGE_URL env var" - ] - } -} \ No newline at end of file diff --git a/gateway/scripts/specs/easytier.json b/gateway/scripts/specs/easytier.json new file mode 100644 index 0000000..ba06a1a --- /dev/null +++ b/gateway/scripts/specs/easytier.json @@ -0,0 +1,72 @@ +{ + "module": "easytier", + "version": "1.0", + "purpose": "EasyTier VPN 网络开关。控制 Windows(192.168.1.16) 和 Linux 246(192.168.1.246) 上的 EasyTier VPN 进程,让两台机器组成 P2P 内网。", + "ui_location": "I Tab (Infrastructure) → EasyTier VPN section", + + "human_help": { + "title": "EasyTier VPN", + "description": [ + "EasyTier 是一个 P2P VPN 组网工具,用于连接 Windows(192.168.1.16) 和 Linux 246(192.168.1.246) 两台机器。", + "开启后两台机器通过虚拟网卡通信:Windows 获得 10.144.144.3,246 获得 10.144.144.1。", + "RDP 远程桌面隧道依赖 EasyTier VPN 先连通,请先确保这里显示 Connected 再操作 RDP。" + ], + "usage": [ + "1. 点击 Turn On 启动 EasyTier VPN(两台机器都启动)", + "2. 点击 Turn Off 关闭 EasyTier VPN", + "3. 状态显示 Connected 表示两台机器都已加入 VPN 网络" + ], + "troubleshooting": [ + "如果 Turn On 返回失败:检查 Windows xmpp_bot (pythonw.exe, port 5802) 是否运行", + "如果状态显示 Off 但实际已启动:等待 1-2 秒再刷新,或检查 246 上 easytier-core 进程" + ], + "related": "RDP Remote Desktop 依赖本 VPN 通道,参见 RDP section 的帮助" + }, + + "ai_spec": { + "apis": [ + {"method": "GET", "path": "/api/easytier", "returns": "status{windows,246} + virtual_ips{windows,246}", "proxied_to": "xmpp_bot /easytier action=status"}, + {"method": "POST", "path": "/api/easytier/toggle", "body": "{action: start|stop}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /easytier action=start|stop"} + ], + "dependencies": [ + "xmpp_bot on Windows 192.168.1.16:5802 (HTTP bridge with /easytier endpoint)", + "XMPP_BRIDGE_URL env var (default: http://192.168.1.16:5802) — set in systemd service", + "_bridge_post() helper in dashboard.py — proxies POST to xmpp_bot with X-Api-Key header", + "_BRIDGE_KEY = 'xxm_bridge_8f3a2c' — API key for xmpp_bot HTTP bridge" + ], + "architecture": { + "flow": "Dashboard(246:5803) → _bridge_post() → xmpp_bot(Windows:5802) → starts/stops easytier-core process on respective machine", + "mechanism": "xmpp_bot receives POST /easytier {action} → starts/stops easytier-core process" + }, + "constraints": [ + "VPN IP: Windows=10.144.144.3, 246=10.144.144.1 (NOT .2 — .2 was wrong in old code, 已修正)", + "xmpp_bot must be running on Windows with /easytier HTTP endpoint (API key: xxm_bridge_8f3a2c)", + "dashboard.py runs on 246 (Linux), xmpp_bot runs on Windows — 通信走 LAN HTTP,不是 local subprocess" + ], + "must_not": [ + "不要把 proxy 调用改成 local subprocess(dashboard 在 Linux,EasyTier 在 Windows)", + "不要把 VPN IP 从 .3 改成 .2 — .3 是正确的 Windows IP", + "不要删除 _bridge_post() helper 或 _BRIDGE_KEY — 它们是 proxy 的核心", + "不要在 fI() 里用 fill('infra',h) — 这会清空整个 ct-infra 区域导致 EasyTier/RDP 元素闪烁" + ], + "related_modules": [ + {"module": "rdp", "relation": "RDP 远程桌面隧道依赖 EasyTier VPN 连接,启动 RDP 前应确保 EasyTier Connected"} + ], + "tests": [ + {"id": "ET01", "name": "EasyTier toggle start returns ok", "endpoint": "POST /api/easytier/toggle {action:start}"}, + {"id": "ET02", "name": "EasyTier toggle stop returns ok", "endpoint": "POST /api/easytier/toggle {action:stop}"}, + {"id": "ET03", "name": "EasyTier status returns virtual_ips with windows=10.144.144.3", "endpoint": "GET /api/easytier"} + ], + "known_issues": [ + "如果 xmpp_bot (5802) 停了,EasyTier 操作会失败 — 检查 Windows pythonw.exe 进程", + "EasyTier status 246 字段可能显示 unknown — 因为 246 的 EasyTier 状态不一定通过 xmpp_bot 返回" + ], + "related_files": [ + "gateway/scripts/dashboard.py — /api/easytier*, _bridge_post()", + "gateway/scripts/templates/dashboard.html — fI() function creates EasyTier section (id=easytier-section)", + "gateway/scripts/specs/easytier.json — 本 spec 文件", + "gateway/scripts/specs/rdp.json — 关联模块 RDP 的 spec", + "xmpp_agent_core.py — /easytier HTTP endpoint (line ~780)" + ] + } +} \ No newline at end of file diff --git a/gateway/scripts/specs/rdp.json b/gateway/scripts/specs/rdp.json new file mode 100644 index 0000000..d1276f7 --- /dev/null +++ b/gateway/scripts/specs/rdp.json @@ -0,0 +1,83 @@ +{ + "module": "rdp", + "version": "1.0", + "purpose": "RDP 远程桌面隧道开关。通过 SSH 反向隧道将 Windows 3389 端口暴露到 Aliyun 公网 IP,实现从外网远程桌面到 Windows。", + "ui_location": "I Tab (Infrastructure) → RDP Remote Desktop section", + + "human_help": { + "title": "RDP Remote Desktop", + "description": [ + "通过 SSH 反向隧道把 Windows 的 RDP 端口(3389)暴露到 Aliyun 公网服务器(47.115.32.206:8080)。", + "开启后在外网可用 mstsc /v:47.115.32.206:8080 远程桌面到 Windows。", + "本功能依赖 EasyTier VPN 先连通 — SSH 到 246 的连接走 VPN 内网(10.144.144.1)。", + "请先确保 EasyTier VPN 已 Connected,再点击 Enable。" + ], + "usage": [ + "1. 先在 EasyTier VPN section 点击 Turn On,确认 Connected", + "2. 回到本 section 点击 Enable 启动 SSH 反向隧道", + "3. 在外网电脑运行 mstsc /v:47.115.32.206:8080 连接远程桌面", + "4. 不用时点击 Disable 关闭隧道" + ], + "troubleshooting": [ + "如果 Enable 返回失败:检查 EasyTier VPN 是否 Connected(RDP 依赖 VPN 内网 SSH)", + "如果隧道状态一直 pending:检查 246 的 /etc/ssh/sshd_config 是否有 GatewayPorts yes", + "如果 mstsc 连不上:检查 Aliyun 安全组是否放行 8080/TCP", + "如果 timeout:从外网执行 Test-NetConnection 47.115.32.206 -Port 8080 看端口是否真的 listening" + ], + "related": "依赖 EasyTier VPN 内网通道,参见 EasyTier VPN section 的帮助" + }, + + "ai_spec": { + "apis": [ + {"method": "GET", "path": "/api/rdp", "returns": "rdp_enabled + tunnel_running + public_endpoint(47.115.32.206:8080)", "proxied_to": "xmpp_bot /rdp action=status"}, + {"method": "POST", "path": "/api/rdp/toggle", "body": "{action: start|stop}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /rdp action=start|stop"} + ], + "dependencies": [ + "xmpp_bot on Windows 192.168.1.16:5802 — /rdp HTTP endpoint", + "_bridge_post() + _BRIDGE_KEY in dashboard.py — proxy 机制同 EasyTier", + "port_open() helper in dashboard.py — 检查 SSH 隧道端口 8080 是否监听", + "EasyTier VPN 必须先 Connected — SSH 到 246 走 VPN 内网 10.144.144.1" + ], + "architecture": { + "flow": "Dashboard(246:5803) → _bridge_post() → xmpp_bot(Windows:5802) → 启动 SSH reverse tunnel", + "rdp_mechanism": "xmpp_bot receives POST /rdp {action} → 执行 ssh -R 0.0.0.0:8080:localhost:3389 root@47.115.32.206 把 Windows 3389 转发到 Aliyun 公网", + "public_endpoint": "47.115.32.206:8080 (Aliyun 公网)" + }, + "constraints": [ + "RDP SSH 反向隧道命令: ssh -R 0.0.0.0:8080:localhost:3389 root@47.115.32.206", + "GatewayPorts yes 必须配置在 47.115.32.206 (Aliyun) 的 /etc/ssh/sshd_config — 否则只 listen 127.0.0.1 外网连不进来", + "EasyTier VPN 必须先 Connected — 否则 Windows SSH 到 246 不通,隧道无法建立", + "xmpp_bot 必须运行在 Windows (pythonw.exe, port 5802) — 它是实际执行 SSH 命令的一方", + "RDP 端口固定 3389 (Windows 默认),隧道端口固定 8080" + ], + "must_not": [ + "不要在 EasyTier VPN 未 Connected 时启 RDP — 会因为 SSH 不通而失败", + "不要用 SSH 端口转发替代 SSH 反向隧道 — 这是 -R (reverse) 不是 -L (local)", + "不要把 public_endpoint 从 47.115.32.206:8080 改成其他 — 这是 Aliyun 公网入口", + "不要把 tunnel_running 判断从 port_open(8080) 改成依赖 xmpp_bot 的 tunnel 字段 — xmpp_bot 不报告隧道状态,只能从 246 主动 probe", + "不要重写整个 fI() 函数 — 用 create-once/update-state pattern 修改 RDP section" + ], + "related_modules": [ + {"module": "easytier", "relation": "RDP 隧道依赖 EasyTier VPN 内网通道。启动 RDP 前必须确保 EasyTier Connected"} + ], + "tests": [ + {"id": "RDP01", "name": "RDP toggle start returns ok", "endpoint": "POST /api/rdp/toggle {action:start}"}, + {"id": "RDP02", "name": "RDP toggle stop returns ok", "endpoint": "POST /api/rdp/toggle {action:stop}"}, + {"id": "RDP03", "name": "RDP status returns public_endpoint 47.115.32.206:8080", "endpoint": "GET /api/rdp"}, + {"id": "RDP04", "name": "After enable, port 8080 on Aliyun is listening (Test-NetConnection)", "endpoint": "external TCP check 47.115.32.206:8080"} + ], + "known_issues": [ + "如果 Enable 后 status 一直 Tunnel pending:检查 /etc/ssh/sshd_config GatewayPorts yes,检查 Aliyun 安全组 8080/TCP", + "如果 mstsc 从外网连不上:检查 Aliyun 安全组 — 需要 8080/TCP 入方向放行", + "如果隧道断了但 status 显示 Connected:这是 stale state — port_open() 会重检 8080 端口下次刷新自动修正" + ], + "related_files": [ + "gateway/scripts/dashboard.py — /api/rdp*, _bridge_post(), port_open()", + "gateway/scripts/templates/dashboard.html — fI() function creates RDP section (id=rdp-section)", + "gateway/scripts/specs/rdp.json — 本 spec 文件", + "gateway/scripts/specs/easytier.json — 关联模块 EasyTier 的 spec", + "xmpp_agent_core.py — /rdp HTTP endpoint (line ~794)", + "/etc/ssh/sshd_config on 47.115.32.206 — GatewayPorts yes 配置" + ] + } +} \ No newline at end of file diff --git a/gateway/scripts/templates/dashboard.html b/gateway/scripts/templates/dashboard.html index 6c29ed4..513f661 100644 --- a/gateway/scripts/templates/dashboard.html +++ b/gateway/scripts/templates/dashboard.html @@ -109,6 +109,7 @@ function _renderSpecModal(spec,type){ if(hh.description){h+='

说明

';} if(hh.usage){h+='

使用方法

';} if(hh.troubleshooting){h+='

常见问题

';} + if(hh.related){h+='

关联模块

'+esc(hh.related)+'

';} }else{ var ai=spec.ai_spec||{}; h+='

§ AI Spec — '+esc(spec.module||'')+'

'; @@ -119,6 +120,7 @@ function _renderSpecModal(spec,type){ if(ai.must_not){h+='

禁止行为

';} if(ai.tests){h+='

测试用例

';} if(ai.related_files){h+='

相关文件

';} + if(ai.related_modules&&ai.related_modules.length){h+='

关联模块

';} } box.innerHTML=h; } @@ -191,7 +193,7 @@ async function fI(){ var et=document.getElementById('easytier-section'); if(!et){ et=document.createElement('div');et.id='easytier-section';et.className='ps';et.style.marginTop='16px'; - et.innerHTML='

EasyTier VPN?§

Windows
246
-
'; + et.innerHTML='

EasyTier VPN?§

Windows
246
-
'; ci.appendChild(et); document.getElementById('btn-et-on').onclick=function(){fetch('/api/easytier/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})}).then(function(){fI()}).catch(function(){toast('ET Fail','err')})}; document.getElementById('btn-et-off').onclick=function(){fetch('/api/easytier/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'stop'})}).then(function(){fI()}).catch(function(){toast('ET Fail','err')})}; @@ -210,7 +212,7 @@ async function fI(){ var rd=document.getElementById('rdp-section'); if(!rd){ rd=document.createElement('div');rd.id='rdp-section';rd.className='ps';rd.style.marginTop='16px'; - rd.innerHTML='

RDP Remote Desktop?§

RDP
Tunnel
-
'; + rd.innerHTML='

RDP Remote Desktop?§

RDP
Tunnel
-
'; ci.appendChild(rd); document.getElementById('btn-rdp-on').onclick=function(){fetch('/api/rdp/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})}).then(function(){fI()}).catch(function(){toast('RDP Fail','err')})}; document.getElementById('btn-rdp-off').onclick=function(){fetch('/api/rdp/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'stop'})}).then(function(){fI()}).catch(function(){toast('RDP Fail','err')})};