dsh-service-portal

remotetested: passnpmMIT

Exposes any local service on a LAN address and a public cloudflared quick-tunnel domain behind one password login, with per-route open/block gates managed from the plugin settings page. Requires Python 3 and cloudflared on the machine, and reports either of them being missing or misconfigured in the

下载量 / Downloads
2,311
依赖体积 / node_modules
1.05 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-service-portal

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

<p align="center">

<img src="icons/dsh-service-portal-64.png" width="64" alt="dsh-service-portal" />

</p>

<h1 align="center">dsh-service-portal</h1>

<p align="center"><b>一个随机域名 + 一个随机口令,把本机的服务按需对外开放。</b></p>

<p align="center">

<a href="README.en.md">English</a> ·

<a href="#安装">安装</a> ·

<a href="#工作原理">工作原理</a> ·

<a href="#在设置里管理节点">节点管理</a> ·

<a href="CHANGELOG.md">更新日志</a> ·

<a href="https://github.com/dingminhua/dsh-service-portal/issues">问题反馈</a>

</p>

<p align="center">

<a href="https://www.npmjs.com/package/dsh-service-portal"><img src="https://img.shields.io/npm/v/dsh-service-portal?style=flat-square&label=npm&color=cb3837" alt="npm version"></a>

<a href="https://www.npmjs.com/package/dsh-service-portal"><img src="https://img.shields.io/npm/d18m/dsh-service-portal?style=flat-square&label=downloads&color=cb3837" alt="npm downloads"></a>

<a href="LICENSE"><img src="https://img.shields.io/github/license/dingminhua/dsh-service-portal?style=flat-square" alt="MIT license"></a>

<a href="https://github.com/dingminhua/dsh-service-portal/stargazers"><img src="https://img.shields.io/github/stars/dingminhua/dsh-service-portal?style=flat-square" alt="GitHub stars"></a>

</p>

dsh-service-portal(对外服务门户) 是一个 DeepSeek Harness (DSH) 插件。

它把任意本机服务按需暴露到外网:cloudflared 建一个随机域名,一道口令登录

(只需口令,没有账号)守住入口,设置页一键开关并显示连接与口令。

它最初长在 ld-rl-poker 里(为了把训练配置台/模型查看台放到外网),但门户本身与德州扑克无关——

任何本地服务都能用同一套「一个域名 + 一个口令 + 按需挂载」。

功能特性

  • 装好即用,不用先点开关 —— 插件加载后门户自动起来(只开内网,不会自动把本机

服务挂到公网)。打开设置页就能看到内网地址和二维码,扫一下就进。

外网暴露必须由你显式点「开启公网访问」。

  • 记得你上次的选择 —— 点过「停止门户」,下次打开 DSH 就不再自动启动它;点过

「启动」,下次打开照旧自动启动(仍然只开内网,公网永远是手动开)。

也就是"默认开着,之后跟着你走":它写回插件配置的 autoStart,可以在配置里手改,

与点按钮等效。设置页会明说当前是哪一种,不让你猜。

  • 设置页就是 dsh-pocket 那个形态 —— 局域网、公网两个区块,各自带开关 + 二维码 +

地址 + 访问密码行 + 高级(手动选地址)。

  • 节点管理就在设置页里(设置 → 左侧导航「对外服务门户」)—— 添加节点(名称 / 对外路径 / 本机端口 / 备注)、

开放 / 屏蔽(路由闸门)、编辑、删除,以及每个节点的两个访问入口:

「内网访问」(走局域网地址)与「公网访问」(走隧道地址)。

两者各自按自己的开关判断能否点击 —— 关掉局域网后内网按钮变灰、公网照常可用;

没开公网时公网按钮变灰,并写明"先点「开启公网访问」"。

不用改配置文件、不用重启。

  • 只管路由,不管启停 —— 「屏蔽」只表示门户不再把这条路由转过去,

不会停止上游程序;上游由你自己的方式启动(插件不猜它的启动命令)。

  • 自带门户网页(只读) —— 扫码打开的是测试页 + 引导页:

列出已发布的节点,每个节点有「访问」(打开它)和「测试」

(就地探一次通不通并把结论写在卡片里,不用跳走):

  • 「通了:HTTP 200」= 门户转发成功且上游有响应;
  • 「不通:HTTP 502 … 请用它自己的方式启动那个程序」= 上游没起;
  • 「上游有响应,但它自己返回了 HTTP 502」= 上游在线、是它内部出错。

最后这条必须和上一条分开说 —— 状态码一样,把上游自己的错报成

「没启动」会让人去启动一个本来就在跑的程序。

没有任何设置功能 —— 要改配置请到 DSH 设置 → 对外服务门户。

  • 内网 + 外网两种访问 —— 同一张页面,两条入口:
  • 内网(http://192.168.x.x:8899)默认免密,同一 WiFi 下手机/平板直接打开。

可开「局域网访问密码」,也可以直接关掉整个局域网访问;

关掉后内网请求直接 403(一张说明页,不是登录页 —— 手机那边不是决策者,

看到的是「请到电脑上开启」),公网与本机不受影响;

每次开启要先用勾选式免责声明确认一次(服务端也校验,防绕过前端直接打接口)。

  • 两个开关改完立即生效,不用重启门户(重启会换掉隧道域名,代价太大)。
  • 本机访问永远免密 —— 否则你在电脑上连自己的门户都进不去。
  • 手机扫码即用 —— 设置页与门户页都显示二维码(内联 SVG,不依赖外部请求),

手机扫一下就到;页面按窄屏自适应。

  • 只需口令,不要账号 —— 自定义登录页(只有口令输入框),不弹浏览器原生

「账号 + 密码」框;口令可每次随机或你自己设固定值。

  • 登录限速 —— 单 IP 连续失败 5 次锁 60 秒,另有全局阈值防分布式撞码。
  • 服务状态一目了然 —— 每个服务显示 已启用/未启用/存活探测失败,未启用的不可点;

健康检查是真探测(TCP 连上游端口),不是标记位。

  • 控制检验(本仓库硬纪律)—— 26 个 Python 控制检验脚本 + 8 个端到端脚本 + 160 项 Node

检验;依赖缺失必须「跳过并返回 2」,不得静默通过。

界面

两张图都是真页面渲染出来的截图(无头 Chrome 实拍,不是设计稿),且全部用演示数据

(设置页那张由 tools/render_panel_preview.mjs 的 ?s=svcboth 渲染):

192.168.71.10 是占位内网地址、warm-dawn-1234.trycloudflare.com 是占位域名、

口令是示例串。README 里不放任何真实地址或口令。

<p align="center">

<img src="docs/assets/panel-both.png" width="760" alt="设置页:局域网与公网两条入口,各带二维码、地址与口令">

<br>

<sub><b>设置页</b>(设置 → 左侧导航「对外服务门户」):内网 / 公网两条入口并排,

各自判断开关;扫码即开,节点增删在此</sub>

</p>

门户页是扫码打开的那个页面——只读的测试页与入口页:点「访问」直接打开节点,

点「测试」就地探一次通不通。它刻意做得极简:没有设置功能,设置只在上面那个页面里。

<p align="center">

<img src="docs/assets/portal-console.png" width="760" alt="门户控制台:节点列表,每个节点有访问与测试按钮">

<br>

<sub><b>门户页</b>:节点列表 —「访问」打开,「测试」就地探通不通</sub>

</p>

窄屏与深色

门户页是给手机扫进来的,所以窄屏与深色是主场景,不是附属:

窄屏(浅色) · 窄屏(深色) · 宽屏

<img src="https://raw.githubusercontent.com/dingminhua/dsh-service-portal/main/docs/assets/portal-phone.png" width="220" alt="门户页 窄屏 浅色"> · <img src="https://raw.githubusercontent.com/dingminhua/dsh-service-portal/main/docs/assets/portal-phone-dark.png" width="220" alt="门户页 窄屏 深色"> · <img src="https://raw.githubusercontent.com/dingminhua/dsh-service-portal/main/docs/assets/portal-wide.png" width="420" alt="门户页 宽屏">

工作原理

                    ┌─────────── 门户进程(一个端口,默认 8899)───────────┐
 内网 http://192.168.x.x:8899 ──┤                                        │
                                ├──▶ 按 Host 分类:loopback / lan / public │
 外网 https://<随机>.trycloudflare.com ─┘    │                            │
                                            ├─ /            → 门户页(只读:测试 + 引导)
                                            ├─ /_auth/login → 口令登录(外网)
                                            ├─ /_api/*      → 门户 API(受 DSH 插件设置调用)
                                            └─ /<路由路径>/… → 转发到对应服务

认证强度按来源区分(照搬 dsh-pocket 的模型,见[致谢](#致谢)):

Host 分类 · 判定 · 门户页 / 服务

loopback · localhost、127.x、::1 · 免密

lan · RFC1918 私网、CGNAT(100.64/10)、.local、单标签名 · 免密

public · 其余一切(含你自建域名) · 必须口令

兜底方向是安全的一侧:认不出的 Host 一律当公网。反着写(只把 *.trycloudflare.com
当外网、其余当内网)会让人挂个自己的域名就绕过认证——dsh-pocket 踩过这个坑
(它的 issue #66),本项目直接按 fail-closed 实现。

安装

方式一:npm(发布后)

$dsh = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"
& $dsh plugin --profile desktop add dsh-service-portal

方式二:link 本地插件(开发中)

$dsh = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"
& $dsh plugin --profile desktop add "link:D:/DshProject/dsh-service-portal"

装完需要重启 DSH(插件在宿主进程里加载)。

若旧 profile 仍装有已删除的子插件 dsh-poker-viewer,先移除再装根插件:
`& $dsh plugin --profile desktop remov

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::16,最后更新 2026-11-05

Links

GitHub · Homepage · npm

Same category