# 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`)。