Files

63 lines
4.0 KiB
Markdown

# WebDrop 服务端
.NET 10,NativeAOT。设计见 [../docs](../docs),接口契约见 [../docs/openapi.yaml](../docs/openapi.yaml)。
## 结构
| 项目 | 职责 |
|---|---|
| `src/WebDrop.Core` | 领域类型、规则和业务逻辑(`ChannelService`、`ShareService`),不做 IO,只依赖抽象 |
| `src/WebDrop.Storage` | SQLite 存储(`SqliteChannelStore`)和磁盘 blob(`DiskBlobStore`) |
| `src/WebDrop.Frontend` | 可选嵌入 Vite dist,提供静态文件、SPA fallback 与页面 CSP;不依赖业务层 |
| `src/WebDrop.Api` | Minimal API、WebSocket / SSE / 长轮询、订阅枢纽、后台清理、限流、安全头 |
| `tests/` | `Core.Tests`(单元与服务层,真实 SQLite)、`Api.Tests`(真实 Kestrel 上的集成测试) |
## 常用命令
```powershell
dotnet test # 全部测试
dotnet run --project src/WebDrop.Api # 开发运行,默认 http://localhost:5000
npm --prefix ../web run build # 先构建前端时,dotnet publish 会自动嵌入 web/dist
# NativeAOT 发布(Windows 需要 vswhere.exe 在 PATH 里)
$env:PATH += ";${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer"
dotnet publish src/WebDrop.Api -c Release -r win-x64 -o out/win-x64
pwsh scripts/smoke.ps1 -Exe out/win-x64/webdrop.exe # 对原生产物做端到端冒烟(含 WebSocket)
python3 scripts/smoke.py out/linux-x64/webdrop # 跨平台版本,只用标准库,不含 WebSocket
```
## 配置
配置节 `WebDrop`,环境变量写法 `WebDrop__DataDir`、`WebDrop__Captcha__Provider`。完整默认值见 `src/WebDrop.Api/appsettings.json`。
| 键 | 默认 | 说明 |
|---|---|---|
| `DataDir` | `data` | SQLite 数据库和 blob 目录 |
| `CorsOrigins` | 空 | 前端所在的源;前端托管在独立域名,必须列出,否则不发 CORS 头 |
| `TrustForwardedHeaders` | false | 在反向代理之后开启,限流按真实 IP 分区 |
| `EnableHttp3` | false | 仅在 `/info` 里通告,服务进程不内置 QUIC |
| `RequestsPerMinute` / `ChannelCreatesPerMinute` | 600 / 30 | 按 IP 的限流,只在内存里 |
| `ShareDraftTtlSeconds` | 86400 | 未完成分享草稿的保留时间 |
| `Captcha:Provider` | `none` | 创建频道的验证码:`none` 或 `cap`。选 `cap` 时下面三项必填,缺失会在启动时报错 |
| `Captcha:ApiEndpoint` | 空 | 浏览器里 Cap widget 连接的地址,形如 `https://cap.example.com/<site_key>/`,经 `/info` 下发 |
| `Captcha:SiteverifyUrl` | 空 | 本服务调用 Cap 校验 token 的地址,形如 `https://cap.example.com/<site_key>/siteverify`,可以是内网地址 |
| `Captcha:Secret` | 空 | Cap 站点密钥的 secret,只在服务端使用 |
| `Limits:*` | 见 `ServerLimits` | 负载、帧数、频道字节数、成员人数上限、TTL 上限等 |
## 约束
- 单实例部署:订阅枢纽在进程内,多实例需要换成外部发布订阅。
- 日志不记录请求头、请求体、完整 URL;Hosting 诊断日志已关闭,避免频道 id 进入日志。
- NativeAOT:不用 SignalR、EF Core、MVC、反射式 JSON;所有 AOT / 裁剪警告视为错误。
- `WebDrop.Frontend` 只负责嵌入静态资源与 SPA fallback。`Containerfile` 自动构建 `web/`;单独发布 API 时,只有 `web/dist` 存在或指定 `WebDropFrontendDist` 后才包含前端。
## 验证码(Cap)
工作量证明不由本项目实现,交给 [Cap](https://github.com/tiagozip/cap)。默认 `Provider=none`,适合开发和小型自托管。需要防刷时:
1. 部署 Cap Standalone(官方是 Docker 容器,依赖 Redis/Valkey),建一个站点密钥。
2. 配置 `Captcha:Provider=cap`、`ApiEndpoint`、`SiteverifyUrl`、`Secret`。
3. 前端读取 `/info` 的 `captcha.api_endpoint`,用 Cap widget 求解后把 token 放进 `X-WD-Cap` 创建频道。
**测试状态**:服务端对 Cap 的调用只用一个假 Cap 服务(按其文档的 `POST /<site_key>/siteverify`、`{secret, response}` → `{success}` 实现)测过,**尚未与真实的 Cap 联调**。部署前请用真实实例跑一遍创建流程。Cap 不可用时创建频道会返回 `503 captcha_unavailable`(fail closed),不会放行。