引导
GET /api/bootstrap
为仪表盘提供单次往返的初始数据加载。在单次响应中返回所有已注册引导的 Redis 缓存键,从其 seed 封装中解包出来。
- 认证:浏览器
wm-sessioncookie、X-WorldMonitor-Key或X-Api-Key别名。用户签发的密钥会针对当前 API 访问权限进行验证。 - 匿名天气:
?keys=weatherAlerts仅在未发送任何 API 密钥头时公开。如果你附加X-WorldMonitor-Key/X-Api-Key,即使是天气请求也会被完全验证 —— 格式错误的密钥返回401,没有当前 API 访问权限的密钥返回403。始终发送密钥的调用方必须发送有效的、有权限的密钥(或省略该头以使用匿名天气路径)。该 URL 为no-store,这正是该约定在边缘层与源站同样成立的原因:没有任何缓存持有它,因此无效密钥永远不会被已预热的匿名缓存条目应答。 - 公开天气:
?keys=weatherAlerts&public=1是经 CDN 缓存的天气读取。与?tier=fast&public=1一样,它对所有调用方返回同一份共享种子负载,并忽略你附加的任何凭据 —— CDN 命中先于鉴权发生,正是该标记让响应可以被安全缓存。高频匿名读取请优先使用它;需要校验自己的密钥时使用不带标记的 URL。 - 服务器到服务器:直接用
X-WorldMonitor-Key: wm_...调用https://api.worldmonitor.app/api/bootstrap。此端点没有单独的网关主机、令牌交换、激活步骤或 IP 白名单要求。 - 缓存:只有显式标记的
?...&public=1URL 可被共享缓存,因为只有它们对每个调用方返回相同结果。?tier=fast&public=1/?tier=slow&public=1使用浏览器max-age=60/max-age=300和 CDNs-maxage=600/s-maxage=7200。单键公开 URL:on-demand 键(?keys=<onDemandName>&public=1)在未声明自有配置时继承 slow 配置 —— 浏览器max-age=300、CDNs-maxage=7200;发布频率高于该缓存时长的键均声明了自有配置:correlationCards(浏览器max-age=60、CDNs-maxage=300)、chinaDecisionSignals(浏览器max-age=60、CDNs-maxage=900)、canadaRoads(浏览器max-age=60、CDNs-maxage=900)、albertaRoads(浏览器max-age=60、CDNs-maxage=900)、manitobaRoads(浏览器max-age=60、CDNs-maxage=900)、marketCorrelationSeries(浏览器max-age=60、CDNs-maxage=900)、bcOpen511(浏览器max-age=60、CDNs-maxage=1800)、flightDelays(浏览器max-age=60、CDNs-maxage=1800)和forecasts(浏览器max-age=300、CDNs-maxage=3600);?keys=weatherAlerts&public=1使用Cache-Control: public, s-maxage=600, stale-while-revalidate=120, stale-if-error=900并配合 fast 层 CDN 屏蔽。其余所有形态 —— 密钥认证、会话认证、未标记的?tier=...URL,以及匿名?keys=weatherAlerts路径 —— 均使用Cache-Control: no-store且不发出 CDN 缓存头。这一区分是有意为之:CDN 命中先于鉴权发生,因此答案取决于凭据的 URL 绝不能可缓存。 - 速率限制:此端点上的用户 API 密钥验证具有 fail-closed 的固定每 IP 60 秒 600 次预验证上限,与默认的 API 滑动窗口限流器分开。
- 结构:
{ "data": { "earthquakes": ..., "outages": ..., "marketQuotes": ... }, "missing": [] }—— 约 40+ 个已解包的已 seed 领域负载嵌套在data之下,外加一个missing列表用于 Redis 中不存在的缓存键。
版本
GET /api/version
返回 koala73/worldmonitor 的最新 GitHub Release。桌面应用使用此端点检测是否有更新的发布版本,并提示用户升级。它不是当前部署的 Vercel commit。
public, s-maxage=300, stale-while-revalidate=60, stale-if-error=3600。当 GitHub API 不可达时,返回 502 { "error": "upstream" } 或 502 { "error": "fetch_failed" }。
缓存清理
POST /api/cache-purge
内部端点。通过显式列表或 glob 模式使 Redis 缓存键失效。
- 认证:
Authorization: Bearer $RELAY_SHARED_SECRET(使用 timing-safe 比较)。其他任何凭据返回401。 - 请求体(
keys/patterns至少需要其一): - 限制:最多 20 个显式键,最多 3 个模式(每个必须以
*结尾,裸*会被拒绝),总共最多 200 次删除,每个模式最多 5 次 SCAN 迭代。 - 安全:前缀为
rl:/__的键始终被跳过;会匹配military:bases:*、conflict:iran-events:*、conflict:ucdp-events:*(持久 seed)的模式会被跳过。 - 非生产环境:在 preview / development 部署上,键会自动加上
{env}:{git-sha}:前缀,因此清理操作不会影响生产数据。 - 响应:
健康检查
GET /api/health
针对所有已注册 seed 键的聚合新鲜度报告。在 JSON status 字段中返回 HEALTHY、WARNING、DEGRADED、UNHEALTHY 或 REDIS_DOWN。
除 REDIS_DOWN 外的所有状态均返回 HTTP 200。REDIS_DOWN 返回 HTTP 503,因为 Redis 不可达且端点无法评估 seed 状态。响应不被缓存(private, no-store, max-age=0 加上 CDN-Cache-Control: no-store)。
通过 UptimeRobot / Better Stack 并使用 ?compact=1 进行监控 —— 任何非 HEALTHY 状态都应触发告警。完整的详细视图需要 operator/enterprise API 密钥,因为它包含规范的缓存键名和新鲜度阈值。
GET /api/seed-health
针对由 Railway cron 驱动的 seeder 的并行注册表,具有各自的节奏阈值。与 /api/health 不同 —— 当节奏变化时两者都必须更新。参见健康检查端点。
POST /api/seed-contract-probe
内部探针,用于验证每个 seed 生产者的封装结构是否与其消费者匹配。如果任何消费者读取了生产者不再输出的字段,则返回违规。
用户偏好设置
GET /api/user-prefs
POST /api/user-prefs
每用户仪表盘偏好设置(布局、开关、过滤器)。需要 Clerk bearer 凭据。由 Convex 提供支持。
- 幂等性:
POST /api/user-prefs上支持可选的Idempotency-Key。使用相同键重试并附带相同请求体会重放原始偏好设置响应,而非再次应用更新。
API key 缓存失效
POST /api/invalidate-user-api-key-cache
在订阅变更后(Dodo webhook → Convex → 此端点)使用户的权益(entitlement)缓存失效。内部端点 —— 需要 RELAY_SHARED_SECRET。
地理工具
GET /api/geo
Geo-IP 回显:根据调用方 IP 的 CDN 国家头返回 { "country": "<ISO2>" }。不接受任何参数。(坐标反向地理编码请使用 GET /api/infrastructure/v1/reverse-geocode?lat=…&lon=…。)
GET /api/reverse-geocode?lat=40.7&lon=-74.0
通过 OpenStreetMap Nominatim 将 lat/lon 反向地理编码为最近的国家 + 城市。结果按 0.1 度网格缓存七天,该路由限制为 60 请求/分钟/IP。
账户与会话辅助端点
这些路由服务于仪表盘和设置界面。在此记录以便可被发现,但它们是内部辅助端点,不是有版本的产品契约 —— 其形状会随消费它们的 UI 变化。GET /api/me/entitlement
返回已登录用户的 {"isPro": true|false}。需要 Clerk bearer token(Authorization: Bearer …);缺失或无效的 token 返回 401 {"error":"unauthenticated"},以便调用方区分”未登录”与”已登录的免费层”。供 /pro 营销页切换升级 CTA 使用。始终 Cache-Control: private, no-store。
GET /api/user/mcp-quota
设置界面读取调用者的 MCP 每日配额,读取的正是 MCP 服务器执行限额所用的同一计数器。需要 Clerk 会话。返回 {"used": 12, "limit": 250, "resetsAt": "<下一个 UTC 午夜>"};limit: null 表示不限量。免费账户调用者看到的是免费额度计量。后端故障软失败(used: 0 / 套餐默认值)而不报错。Cache-Control: no-store。
POST /api/user/mcp-revoke
设置界面撤销单个 Pro MCP token。需要 Clerk 会话;用户 id 来自已验证的会话,绝不取自 body。Body:{"tokenId": "<id>"}。返回 200 {"ok":true};错误:400(invalid_json、missing_token_id)、401、404 not_found(为防枚举而刻意折叠)、409 already_revoked、503 service_unavailable 附 Retry-After: 5。撤销在 MCP 服务器 60 秒负缓存窗口内生效。
运维端点
内部运维表面 —— 匿名但按来源门控为 WorldMonitor 来源(其他来源得到纯文本403 Forbidden),不属于公开 API 契约。
POST /api/security/report
浏览器 Reporting API 的 COOP/COEP 违规报告接收端(通过站点级 Reporting-Endpoints: wm-coop-coep="/api/security/report" 头接线)。接受 application/reports+json / application/report+json / application/json(否则 415),body 上限 32 KiB(否则 413),始终以无 body 的 204 应答。记录前 URL 会被裁剪到 origin —— 不保留查询串或令牌。
POST /api/analytics-health
分析采集器自身健康状况的聚合遥测计数器。接受一个很小的 JSON body(≤1 KiB),内容为按 cohort 的写入/失败计数 —— 不接受事件负载、用户 id、URL 或指纹。应答 204。限流每分钟 60 次(失败关闭)。
GET /api/correlation-runtime-mode
只读控制面开关:返回 {"mode": "legacy"|"exact"|"fuzzy"},告知浏览器与 seeder 路径当前激活的关联引擎,无需重新部署。没有写入表面。后备存储不可达时仍返回 200 与 "legacy"。Cache-Control: no-store。
实用工具
GET /api/download?platform=<id>&variant=<id>
重定向到 koala73/worldmonitor 最新 GitHub release 中匹配的资产。成功时返回 302 指向资产 URL,任何失败时(未知平台、无匹配、GitHub 错误)返回 302 指向 releases/latest。
platform(必填,精确字符串):
variant(可选):full、world、tech、finance、commodity、energy、happy。
桌面端只发布一个二进制文件,所有变体均在安装后于应用内切换,因此每个受支持的值都会解析到所请求平台的同一个 World Monitor 资产 —— 该参数只记录调用方来自哪个变体,并不会选择不同的下载。无法识别的值将重定向到 releases/latest,且不会调用 GitHub。省略 variant 时的解析方式与传入受支持值时完全相同 —— 身份过滤在所有路径上都会生效,因此发行版中仅仅匹配平台后缀的无关资产永远不会被选中。
将 302 缓存 5 分钟(s-maxage=300、stale-while-revalidate=60、stale-if-error=600)。
POST /api/leads/v1/submit-contact
公开的企业联系表单。通过 Turnstile 验证,按 IP 限流。属于 LeadsService 的一部分。
POST /api/leads/v1/register-interest
捕获邮箱用于 Pro 候补名单注册。写入 Convex 并发送确认邮件。属于 LeadsService 的一部分。
浏览器调用方必须通过 Turnstile。使用 source: "desktop-settings" 的桌面调用方仅当请求使用共享的桌面密钥进行身份验证时,才能绕过 Turnstile:
X-WorldMonitor-Desktop-Timestamp:Unix epoch 毫秒,与服务器时间相差不超过 5 分钟。X-WorldMonitor-Desktop-Signature:sha256=<hex HMAC-SHA256>。
<timestamp>\n<canonical JSON>,其中 canonical JSON 按顺序包含 email、source、appVersion、referredBy、website 和 turnstileToken。在桌面 sidecar 和云 API 上都要配置 WM_DESKTOP_SHARED_SECRET。在滚动推出期间,WM_DESKTOP_AUTH_ALLOW_LEGACY=true 仅在云 API 未配置 WM_DESKTOP_SHARED_SECRET 时接受未签名的旧版桌面请求。一旦设置了云密钥,桌面请求在缺少有效签名时将拒绝访问(fail closed),且仍受更严格的桌面限流约束。