YD_Event.Admin/WEBSITE-COMPETITION-BACKEND.md
ltx 2e00719d56 feat(website): expose published competition state
Add an anonymous read-only state endpoint for the public website, backed by the private competition service. Keep draft reads and writes behind existing admin login and document the production routing.
2026-10-20 09:15:33 +08:00

82 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 官网赛事信息后端接口
## 功能与调用链
`YD_Event.Web` 的“官网赛事信息”页面请求当前环境的 `GET/PUT /api/website-competition`。此接口由 `YD_Event.Admin` 提供,沿用后台登录认证;管理页不需要填写接口地址或管理密钥。
正式环境访问基地址为 `https://jitsi-web.yuedongsports.com/`,管理接口完整地址为 `https://jitsi-web.yuedongsports.com/api/website-competition`。此地址是后台对外入口;`CompetitionApi:AdminUrl` 是后端转发到官网赛事服务的地址,需单独配置。
`YD_Event.Admin` 在服务端把请求转发到官网赛事服务的内部 `/api/competition/admin`。两台服务器通过私网连接,不使用服务间管理密钥。官网赛事服务按来源 IP 放行内部管理请求,持久化草稿或发布状态,供公开页面读取。本仓库不新增赛事数据库表。
官网公开页面不访问需要登录的管理接口,而是匿名请求 `GET /api/competition/state`。此路由由 `CompetitionPublicController` 通过同一私网赛事服务读取 `/api/competition/state`;赛事服务在未发布时只返回空态。公开接口只支持读取,不提供草稿或写入能力。官网每 10 秒重新读取一次。
```text
YD_Event.Web 管理页
→ 当前域名 /api/website-competition(后台登录态)
→ YD_Event.Admin(通过私网转发)
→ 官网赛事服务 /api/competition/admin(来源 IP 白名单)
→ 持久化赛事 JSON / 官网公开接口
```
## 接口
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `GET` | `/api/website-competition` | 读取当前环境的完整赛事草稿,包括未发布内容 |
| `PUT` | `/api/website-competition` | 保存赛事信息及 `published` 状态,JSON 请求体上限为 8 MB |
| `GET` | `/api/competition/state` | 匿名读取已发布数据;未发布时返回空态 |
前两个管理方法要求已有的后台登录态;公开方法不要求登录。控制器原样转发上游的 JSON 响应和 HTTP 状态码。数据的主要结构如下;详细字段由管理页 Excel 模板及官网赛事服务校验规则约束。
```json
{
"published": false,
"competition": { "nameZh": "", "nameEn": "", "dateZh": "", "dateEn": "", "venueZh": "", "venueEn": "" },
"notice": { "enabled": false, "textZh": "", "textEn": "" },
"schedule": { "currentId": "", "callingId": "", "upcomingCount": 3, "items": [] },
"results": { "overall": [], "challenge": [], "battle": [] },
"players": [],
"records": []
}
```
## 服务端配置
| 配置键 | 环境变量写法 | 说明 |
| --- | --- | --- |
| `CompetitionApi:AdminUrl` | `CompetitionApi__AdminUrl` | 官网赛事服务内部管理接口完整 URL;本地默认 `http://127.0.0.1:8787/api/competition/admin` |
正式环境两台服务器分机部署,必须将 `CompetitionApi__AdminUrl` 配置为 `http://<赛事服务私网 IP>:8787/api/competition/admin`,不能使用默认的 `127.0.0.1`。赛事服务应只在私网地址监听,并通过 `COMPETITION_ADMIN_ALLOWED_IPS` 放行 `YD_Event.Admin` 的私网 IP;防火墙只允许后台服务器连接该端口。公网 Nginx 必须屏蔽 `/api/competition/admin`,避免绕过后台登录认证直接修改赛事数据。
公开状态接口从 `CompetitionApi__AdminUrl` 推导同源的 `/api/competition/state`,因此不需要额外的地址或密钥配置。正式环境沿用现有 `/api/` 反向代理到 YD_Event.Admin;不要把公开接口直接转发到赛事服务。
## 部署步骤
1. 启动官网赛事服务,设置持久化数据路径、私网监听地址、后台服务器的来源 IP 白名单;其健康检查为 `GET /api/competition/ping`。
2. 在 `YD_Event.Admin` 的运行环境配置内网管理接口 URL,然后启动或重启后端。`Startup` 已注册专用 `WebsiteCompetition` HttpClient。
3. 将后台站点同源的 `/api/website-competition` 路由转发到 `YD_Event.Admin`。不要把该路径直接暴露或转发到官网赛事服务管理接口。
4. 管理员登录 `YD_Event.Web` 后打开“官网赛事信息”,导入 `.xlsx`、核对草稿并保存;需要公开展示时再开启“公开发布”。
5. 从 `https://www.yuedongsports.com` 携带 `Origin` 请求正式环境的 `GET /api/competition/state`,确认返回已发布数据或空态,以及允许官网来源的 CORS 响应头。
## 错误排查
| 状态 | 常见原因 |
| --- | --- |
| `401/403` | 后台未登录或无权访问;若由上游返回 `403`,检查后台服务器私网 IP 是否在 `COMPETITION_ADMIN_ALLOWED_IPS` 中 |
| `413` | `PUT` 请求体超过 8 MB |
| `502` | 官网赛事服务无法连接 |
| `503` | 上游 URL 无效 |
| `504` | 官网赛事服务超过 30 秒未响应 |
保存时上游的字段校验错误会原样返回,管理页会展示错误信息。
## 验证
```powershell
dotnet build .\YD_Event.Web.Entry\YD_Event.Web.Entry.csproj --no-restore
```
确认官网赛事服务的 `/api/competition/ping` 返回成功;使用已有后台登录态访问 `GET /api/website-competition`,应返回上述赛事结构。未经登录直接访问应被后台认证拒绝。`PUT` 应先在测试环境使用草稿数据验证,再按发布流程操作。
匿名访问 `GET /api/competition/state` 应返回公开空态或已发布数据。若返回 `404`,检查新控制器是否已部署,以及 Nginx 是否仍使用直接代理赛事服务的旧片段;若返回 `502`,检查私网连接及 `CompetitionApi__AdminUrl`。