feat: DSH编排基建首版(server/tests 88用例/P2插件/8020端口/PORT-NOTE/MOBILE_APP)

This commit is contained in:
2026-09-12 14:20:02 +08:00
commit f6acd20325
128 changed files with 24471 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
# dsh-sync — DeepSeek Harness 团队同步服务端
为 DeepSeek Harness 提供 **团队设置 / 用户设置 / 插件分发** 的私有同步服务。
架构设计见 [ARCHITECTURE.md](ARCHITECTURE.md),移动端见 [MOBILE_APP.md](MOBILE_APP.md)。
**已完成并通过 55 个 pytest 用例(PostgreSQL 16)+ 真实 HTTP 冒烟验证。**
## 目录结构
```
server/
dsh_sync/
main.py # 应用组装(配置 / 中间件 / 路由注册)
config.py # 环境变量配置(含 DSH_DATABASE_URL)
db.py # PostgreSQL schema + 审计写入(psycopg 3)
auth.py # token 签发 / 认证 / 请求上下文
storage.py # 制品与 APK 的路径解析
ratelimit.py # 邀请码兑换限流
routers/ # 按资源拆分的路由(pages/connect/tokens/users/settings/plugins/audit)
static/ # mobile.html(PWA)、apk.html(安装页)、sw.js
tests/ # pytest 套件(55 例,跑在专用测试库 postgres_test 上)
client/
dsh_sync_client.py # 参考客户端(移植进 harness 用)
android/ # 安卓 WebView 壳工程 + build.sh
data/dist/ # APK 构建产物(dsh-sync.apk + 元数据)
Dockerfile / docker-compose.yml / Caddyfile
```
## 安卓 App
```bash
bash android/build.sh # 需要 JDK 17 + Android SDK 34 + Gradle 8.7
```
构建后访问 `http://<服务器>:8020/apk` 下载安装(免登录页面,含二维码与安装步骤)。
## 本地运行(开发)
需要一份 PostgreSQL(本地服务或容器均可):
```bash
# 一次性测试库(二选一):
# a) Docker:docker run -d --rm --name dsh-test-pg -p 15433:5432 \
# -e POSTGRES_USER=dsh -e POSTGRES_PASSWORD=dsh -e POSTGRES_DB=dsh_test postgres:16-alpine
# b) 任意已有 PG:建一个空库即可
cd server
python -m venv .venv
.venv/Scripts/pip install -r requirements.txt -r requirements-dev.txt # Windows
# Linux/macOS: .venv/bin/pip install ...
# 测试。默认用生产实例上的独立库 postgres_test(不存在会自动建)。
# 注意:测试库每个用例都会 DROP SCHEMA 重建,所以 conftest 会在
# DSH_TEST_DATABASE_URL 指向生产库时直接拒绝运行。
.venv/Scripts/python -m pytest -q
DSH_DATABASE_URL="postgresql://dsh:secret@127.0.0.1:5432/dsh" \
DSH_BOOTSTRAP_ADMIN_TOKEN=<随机串> DSH_INVITE_CODE=<邀请码> \
.venv/Scripts/python -m uvicorn dsh_sync.main:app --reload --port 8000
```
> 注意 DSN 里用 `127.0.0.1` 而不是 `localhost`:部分 Windows 机器 `localhost` 先解析到 IPv6,
> 若 PG 未监听 IPv6 会导致每次连接白等一个 connect_timeout。
## Docker 部署
```bash
cp .env.example .env # 填入 POSTGRES_PASSWORD / DSH_BOOTSTRAP_ADMIN_TOKEN / DSH_INVITE_CODE / DSH_SYNC_DOMAIN
docker compose up -d --build
```
compose 会启动三个服务:`postgres:16`(数据存 `pg_data` 卷,带健康检查)→ `dsh-sync`(等库就绪后启动)→ `caddy`。
- 公网部署:Caddy 按 `DSH_SYNC_DOMAIN` 自动签发 TLS 证书。
- 更推荐**内网 / Tailscale 部署**(见 Caddyfile 中 `:8080` 注释块),不暴露公网。
- 备份:`docker compose exec postgres pg_dump -U dsh dsh > backup.sql` + 打包 `./data/`(插件制品)。
## 环境变量
| 变量 | 说明 | 默认 |
|---|---|---|
| `DSH_DATABASE_URL` | PostgreSQL 连接串(**必填**),建议用 `127.0.0.1` 而非 `localhost` | 无 |
| `DSH_DATA_DIR` | 插件制品存储目录 | `./data`(容器内 `/data`) |
| `DSH_BOOTSTRAP_ADMIN_TOKEN` | 首次启动时为 admin 用户创建的固定 token(幂等) | 无则跳过 |
| `DSH_ADMIN_USERNAME` | admin 用户名 | `admin` |
| `DSH_INVITE_CODE` | 团队邀请码,成员自助注册用;不设则关闭邀请注册 | 无 |
| `DSH_MAX_PLUGIN_MB` | 插件制品上传上限(MB) | `20` |
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | compose 创建 PG 实例用 | `dsh` / 必填 / `dsh` |
## API 一览(Bearer token 认证)
```
GET /healthz # 探活
POST /v1/tokens # 邀请码换 token(仅新用户)
POST /v1/tokens/self # 给自己加签 token
GET /v1/tokens # 列自己的 token
DELETE /v1/tokens/{token_hash} # 撤销 token(本人或 admin)
POST /v1/users # admin 直接建用户发 token
GET /v1/me # 身份信息
GET /v1/connect/info # admin:扫码连接数据(内网地址候选 + 邀请码)
GET /v1/connect/qr?host=x.x.x.x # admin:连接二维码(SVG,深链 /app#invite=…)
GET /v1/settings # team ∪ user 合并视图,带 ETag(304 缓存)
PUT /v1/settings/user/{key} # 写自己的设置,body {value, base_version?}
PUT /v1/settings/team/{key} # admin 写团队设置
GET /v1/plugins?harness_version=x.y # 插件列表(按最低 harness 版本过滤)
GET /v1/plugins/{id}/{version} # 插件详情(manifest + sha256)
GET /v1/plugins/{id}/{version}/download # 下载制品,响应头 X-Sha256
POST /v1/plugins/{id} # admin 上传新版本(tar.gz,内含 manifest.json)
DELETE /v1/plugins/{id}/{version} # admin 删除版本
GET /v1/audit # admin 审计日志
```
错误语义:401 未认证 / 403 无权限 / 404 不存在 / 409 版本冲突或重复 / 400 参数非法。
### manifest.json 格式(打包在 tar.gz 根目录或单一顶层目录内)
```json
{
"id": "demo",
"version": "1.0.0",
"name": "Demo Plugin",
"description": "…",
"channel": "stable",
"min_harness_version": "0.0.0",
"files": ["main.py"]
}
```
## 客户端(参考实现)
`client/dsh_sync_client.py` 演示了完整协议,可整体移植进 harness 的 `sync` 模块:
```bash
python client/dsh_sync_client.py login http://127.0.0.1:8000 <邀请码> --username alice
python client/dsh_sync_client.py pull
python client/dsh_sync_client.py push theme '"dark"'
python client/dsh_sync_client.py install demo --harness-version 1.0.0
python client/dsh_sync_client.py status
```
要点(与 ARCHITECTURE.md §6/§7 对应):
- 配置层叠:`defaults < team < user < 本地覆盖`;本地覆盖永不上传。
- `pull` 带 ETag,304 或服务器不可达时回退本地缓存并标记 `stale`。
- `push` 携带 `base_version`,409 冲突时提示重新拉取,不静默覆盖。
- `install` 强校验 sha256 与大小,防 zip-slip,临时目录解压后原子替换。
- **API Key 永远不要放进来**:每人自己的 DeepSeek Key 存本地(keychain/环境变量);团队统一管 Key 请前置 LLM 网关(one-api / LiteLLM)。
## 已验证
- `server/tests`:27 个用例(认证、token 撤销、设置合并/冲突/权限、插件上传下载/哈希/路径穿越/版本过滤/渠道优先、扫码连接 info/qr/权限/HTTPS 场景、审计)在 **PostgreSQL 16** 上全部通过。
- 真实 uvicorn + PostgreSQL 启动 + curl 全链路冒烟:健康检查、admin 认证、团队设置写入、邀请码注册、插件上传、成员下载,sha256 两侧一致。
- 扫码连接(PWA「设置 → 连接手机」):二维码深链 + 候选地址切换 + `#invite=` 自动预填,已在 390×844 手机视口实测(截图 `dsh-app-connect-qr.png`)。