Skip to content

Deployment

Justintunsday edited this page Sep 14, 2026 · 4 revisions

部署与运维

环境变量

变量 默认 说明
PORT 8787 监听端口(云平台通常自动注入)
HOST 0.0.0.0 监听地址
CORS_ORIGIN * 允许来源,多个用逗号分隔;* 为全部
API_KEY 未设置 设置后 /v1/* 需要 X-API-Key / Bearer / ?key=
RATE_LIMIT_PER_MINUTE 0(关闭) 每 IP 每分钟请求上限
DATA_DIR ./data 本地数据集目录
DATA_BASE_URL 未设置 GitHub / jsDelivr 数据集地址(末尾无斜杠)
CITIES_CACHE_TTL_MS 21600000(6h) 城市数据集内存缓存时长;0 为不缓存
LOG_REQUESTS true 访问日志开关

示例(Vercel 项目 → Settings → Environment Variables → Production):

DATA_BASE_URL=https://cdn.jsdelivr.net/gh/Justintunsday/chelaile-api-server@main/data

静态数据托管在 GitHub

城市列表等低频数据存放在仓库 data/cities.json,由 GitHub Actions 每天 03:17 UTC 自动同步(内置 3 次重试、城市数骤减保护、无变化不提交)。API 读取优先级:

内存缓存(CITIES_CACHE_TTL_MS)
  └─ 1. DATA_BASE_URL(GitHub / jsDelivr,如配置)
  └─ 2. 本地文件 DATA_DIR/cities.json
  └─ 3. 回源上游 /wwd/ncitylist(兜底)
  • GET /v1/cities?live=true 始终绕过缓存直接回源,响应 originupstream
  • 手动同步:npm run sync-data;强制写入:bun scripts/sync-data.ts --force
  • 首次使用工作流需在仓库 Settings → Actions → General → Workflow permissions 选择 Read and write permissions

data/cities.json 格式:

{
  "updatedAt": "2026-09-14T08:54:05.173Z",
  "source": "chelaile:/wwd/ncitylist",
  "count": 480,
  "cities": [
    { "cityId": "027", "cityName": "北京", "pinyin": "BeiJing", "supportSubway": true, "hot": true }
  ]
}

数据集地址(可用于其他部署):

https://cdn.jsdelivr.net/gh/Justintunsday/chelaile-api-server@main/data
https://raw.githubusercontent.com/Justintunsday/chelaile-api-server/main/data

部署方式

Cloudflare Workers(无冷启动,需自定义域名)

仓库 worker/ 目录是 Worker 版本,复用与 Node 版完全相同的业务逻辑(签名、解密、字段整理), 只是把网络传输换成 Workers 的 fetch。本仓库已有部署实例:https://ts-api.tundrey.com

自定义域名已在 worker/wrangler.toml 里声明:

[[routes]]
pattern = "ts-api.tundrey.com"
custom_domain = true
npx wrangler login
npx wrangler deploy -c worker/wrangler.toml

部署后到 Cloudflare Dashboard → Workers & Pages → chelaile-api → Settings → Domains & Routes 绑定自定义域名(域名需已托管在 Cloudflare)。*.workers.dev 在大陆被屏蔽,自定义域名是必须的。

  • 本地测试:npm run dev:worker(默认 http://127.0.0.1:8787
  • 环境变量:DATA_BASE_URLCORS_ORIGINworker/wrangler.toml[vars] 中; API_KEYnpx wrangler secret put API_KEY -c worker/wrangler.toml
  • 注意:
    • wrangler.toml 已开启 nodejs_compatbrotli_content_encoding(缺后者会导致大响应挂起)
    • 免费版 CPU 限制 10ms/请求;城市缓存与内存限流按 isolate 隔离,不跨实例共享
    • 大陆流量会走 Cloudflare 境外节点(香港/美西等),延迟约 50–300ms;要更稳请用大陆 VPS

Vercel(已实际部署验证)

  1. 打开 https://vercel.com/new,导入仓库 Justintunsday/chelaile-api-server
  2. 仓库 vercel.json 已声明 Services + container(构建 Dockerfile 并转发全路径),无需手动改 Framework Preset
  3. 在 Production 环境变量添加 DATA_BASE_URL(见上)
  4. Deploy,完成后地址形如 https://chelaile-api-server.vercel.app
  5. 验证:curl https://<你的域名>/v1/health

线上实例:https://chelaile-api-server.vercel.app

注意:如果不是通过 Git 集成导入(例如只用 CLI 部署),后续 push 不会自动更新, 需要 vercel --prod 或到项目 Settings → Git 绑定仓库。

Render

一键 Blueprint:https://render.com/deploy?repo=https://github.com/Justintunsday/chelaile-api-server

仓库 render.yaml 已配置:Docker、free、singapore、healthCheckPath=/v1/healthDATA_BASE_URL。注意 Render 现在创建服务前要求银行卡验证($1 临时预授权,不扣费), 且免费套餐 15 分钟无请求休眠、冷启动约 30–60 秒。

Railway

New Project → Deploy from GitHub repo → 选择仓库。railway.json 已配置 Docker 构建与健康检查。

Docker / VPS

docker build -t chelaile-api .
docker run -d --name chelaile-api -p 8787:8787 \
  -e DATA_BASE_URL="https://cdn.jsdelivr.net/gh/Justintunsday/chelaile-api-server@main/data" \
  -e API_KEY="your-secret" \
  chelaile-api

或直接用 Node:

npm ci && npm run build
PORT=8787 DATA_BASE_URL="..." npm start

建议用 systemd / pm2 守护,前置 Nginx 并透传 X-Forwarded-For

location / {
    proxy_pass http://127.0.0.1:8787;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

GitHub Codespaces(临时体验)

Code → Codespaces 启动 → npm ci && npm run build && npm start → 把 8787 端口设为 Public,即可获得临时公网地址。会休眠且有免费额度限制,仅用于测试。

为什么不能用 GitHub Pages / Actions 直接跑 API

  • Pages 只托管静态文件,无法向 web.chelaile.net.cn 发请求、做 MD5 签名与 AES 解密。
  • Actions 无法暴露常驻公网端口,任务结束后即销毁。

本仓库的 Pages 用来托管这份文档站,Actions 用来定时同步数据集;API 进程必须部署到 能运行 Node/Docker 的平台。

部署后自检清单

  1. GET /v1/healthstatus: ok
  2. GET /v1/citiesorigingithub(配置了 DATA_BASE_URL)或 file
  3. GET /v1/search?city_id=034&keyword=71 → 返回 name: "71"
  4. GET /v1/lines/detail?city_id=034&line_id=21283603183stationsNum: 24
  5. POST /v1/health405GET /v1/nope404

Clone this wiki locally