開放 API
平台對外開放的 HTTP API,用於程式化控制 CDN(部署管線整合 / CI 自動刷快取 / 監控腳本等)。
平台對外開放的 HTTP API,用於程式化控制 CDN(部署管線整合 / CI 自動刷快取 / 監控腳本等)。
官方 SDK
平台官方維護的 SDK,封裝鑑權、錯誤處理、JSON 序列化,直接呼叫即可。
| 語言 | 倉庫 | 安裝 |
|---|---|---|
| Go | FurCDN/go-sdk | go get github.com/FurCDN/go-sdk@v0.1.0 |
| Java | FurCDN/java-sdk | JitPack:com.github.FurCDN:java-sdk:0.1.0 |
| Kotlin | FurCDN/kotlin-sdk | JitPack:com.github.FurCDN:kotlin-sdk:0.1.0 |
三個 SDK 都實作了:列域名 / 刷快取 / 上傳憑證。Java、Kotlin 透過 JitPack 安裝,完整 Gradle / Maven 範例見各倉庫 README。
官方 CLI
FurCDN/furcdn-cli —— 命令列工具,涵蓋 API key 精簡版端點,也涵蓋登入後的完整版網域/帳務管理。
CLI 支援兩種鑑權方式,依指令群組自動決定用哪一種:
- API Key(
furcdn config set-key):給/api/v1/**那幾支對外開放端點用,對應furcdn domains/furcdn ssl,適合寫進部署腳本、CI 自動刷快取這類免互動場景。 - Session 登入(
furcdn login,OAuth device flow):給furcdn domain/furcdn account這兩組完整版指令用,適合日常手動操作。
npm install -g @furcdn/cli
# 方式一:API Key,適合腳本/CI
furcdn config set-key fck_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx
furcdn domains list
furcdn domains purge example.com
furcdn ssl upload example.com --cert fullchain.pem --key privkey.pemdomains purge / ssl upload 都接受網域 ID 或網域名稱;加 --json 輸出機器可讀格式。設定存在 ~/.config/furcdn/config.json,也可用 FURCDN_API_KEY / FURCDN_API_BASE 環境變數覆蓋。
登入(OAuth device flow)
日常手動操作(管理網域、快取規則、WAF、帳務等)建議用 furcdn login。CLI 不會向你要密碼 —— 帳密只在瀏覽器裡輸入,走的是 OAuth 2.0 Device Authorization Grant 風格的流程,伺服器端對應 POST /api/oauth/device/code、POST /api/oauth/device/token、POST /api/oauth/device/approve 三支端點,以及瀏覽器核准頁 /oauth/device:
$ furcdn login
請在瀏覽器開啟以下網址完成授權:
https://cdn.taipei/oauth/device?user_code=ABCD-1234
或前往 https://cdn.taipei/oauth/device 手動輸入代碼:ABCD-1234
等待授權中...
...
已登入:alice(alice@example.com)
$ furcdn domain list
$ furcdn account balance
$ furcdn logout
已登出。流程:furcdn login 先呼叫 /api/oauth/device/code 拿到一組 user_code,接著嘗試自動開啟瀏覽器(偵測不到 GUI/桌面環境時靜默 fallback,網址一樣印在終端機上);使用者在瀏覽器裡確認核准後,CLI 依伺服器指定的 interval 輪詢 /api/oauth/device/token,拿到 token 就存進 ~/.config/furcdn/session.json(權限 0600,比照 SSH key 的做法)。核准逾時(10 分鐘沒人操作)或在瀏覽器按拒絕,CLI 會印出清楚的錯誤並中止,重新執行 furcdn login 即可。
furcdn-cli 刻意不提供任何管理後台(admin)操作指令 —— 即使登入的帳號是 admin,CLI 也不給任何管理能力,伺服器端也有雙重防線(device flow 核發的 token 帶 src="cli" 標記,管理 API 一律拒絕)。管理功能請透過瀏覽器 dashboard 使用。
furcdn domain:完整版網域管理
登入後可用的完整版指令,涵蓋網域 CRUD、回源/SNI/備援(furcdn domain origin list/set/remove)、源站擇優(furcdn domain origin policy get/set、furcdn domain origin probes)、TLS/HTTP 開關(furcdn domain tls status/set,控制 force-https/http2/ech)、SSL 憑證(furcdn domain ssl request/status/upload/delete)、快取規則(furcdn domain cache-rules)、WAF 規則(furcdn domain waf,含 ip-check 乾跑測試)、DNS 檢查(furcdn domain dns-check)與日誌查詢(furcdn domain logs)。
源站擇優讓每個節點各自對各源站做 ICMP/TCP/HTTP 探測後,挑出對自己最快的源站(設定存在網域的 originPolicy)。底層對應 GET/PUT /api/domains/{id} 的 originPolicy 欄位與 GET /api/domains/{id}/origin-probes;這些是 session 鑑權的 dashboard API,不在公開的 /api/v1/** 內:
# 啟用擇優:strategy=latency
furcdn domain origin policy set example.com --set strategy=latency
# 只帶要改的欄位(伺服器淺合併保留其餘設定)
furcdn domain update example.com --origin-strategy latency --switch-threshold 20 --min-hold 60
# 檢視目前設定 / 各節點探測結果
furcdn domain origin policy get example.com
furcdn domain origin probes example.com
# 重置為全部預設
furcdn domain origin policy set example.com --reset完整選項(策略、探測方式、遲滯、每源站權重/備用/探測目標等)與 ICMP 注意事項,見回源鑑別與源站防護的「源站擇優」章節。
furcdn account:帳號自助服務
balance/topup/redeem/discount-preview、change-password/change-email/settings/audit-log、notify、oauth、passkeys(僅管理既有列表,註冊需要瀏覽器 WebAuthn)、api-keys、plan(usage/history/auto-renew/buy/renew/upgrade)、stats、alipay-verify、tickets。
GitHub Action
FurCDN/github-action —— 在 GitHub Actions workflow 中直接呼叫 API,適合部署管線整合或 CI 自動刷快取。
- uses: FurCDN/github-action@v1
with:
action: purge
api-key: ${{ secrets.FURCDN_API_KEY }}
domain-id: 123支援 purge(刷快取)、upload-ssl(上傳憑證)與 pages-deploy(部署 Pages 靜態站)三個動作,完整參數見倉庫 README。pages-deploy 的 site/dir/branch/message/create-site 參數與輸出見 FurCDN Pages。
一、API 金鑰管理
建立金鑰
儀表板 > API:
- 點「新增金鑰」
- 填:
- 名稱:顯示用,如「CI 部署機器」
- 過期時間(選填):留空 = 永不過期
- 提交後彈窗顯示完整金鑰(僅此一次,務必複製保存)
金鑰格式
fck_<8 hex prefix>_<24 hex secret>fck_是固定前綴(FurCDN Key)- prefix 8 字元僅顯示用,讓你能識別「這是哪一把」
- secret 24 字元是真正驗證用的部分
- DB 只存 SHA-256(整把金鑰),前端只看得到 prefix
列表頁顯示
只看得到:
- 名稱
- prefix(
fck_xxxxxxxx) - 建立時間 / 末次使用時間 / 過期時間
完整金鑰遺失就只能刪掉重建。
過期通知
過期前 7 天系統會寄一封提醒信(expiryNotified 去重,只發一次)。
刪除金鑰
刪除即立即失效,任何用此金鑰的後續請求 401。
二、鑑權
帶上 Bearer
GET /api/v1/domains HTTP/1.1
Host: cdn.taipei
Authorization: Bearer fck_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxxcurl 範例:
curl -H "Authorization: Bearer fck_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxx" \
https://cdn.taipei/api/v1/domains驗證流程
- 取
Authorizationheader,剝Bearer前綴 - SHA-256 hash 整把金鑰
- DB 查
keyHash匹配 - 檢查
expiresAt(若有)是否過期 - 通過 → 返回對應 user(後續端點權限按該 user 檢查)
- 更新
lastUsedAt(每次呼叫)
失敗回應
| HTTP | 含義 |
|---|---|
| 401 | 沒帶金鑰 / 格式錯 / hash 對不上 |
| 401 | 金鑰已過期 |
| 403 | 金鑰合法但操作對象不屬於此 user(IDOR 防護) |
| 429 | 限速觸發 |
三、端點清單
列出我的域名
GET /api/v1/domains
Authorization: Bearer fck_xxx返回:
{
"domains": [
{"id": 1, "name": "example.com", "enabled": true},
{"id": 2, "name": "static.example.com", "enabled": true}
]
}只列名稱與啟用狀態,不含敏感配置(origin / cert 等)。
刷新快取
POST /api/v1/domains/{id}/purge
Authorization: Bearer fck_xxx廣播到該域名所屬叢集所有在線節點清 L1+L2 快取。等同 dashboard 的「立即刷新快取」按鈕。
返回:
{
"ok": true,
"total": 5,
"success": 5
}total = 應通知節點數;success = 實際成功節點數。
限速:每 user 每 5 分鐘 10 次 + 每 IP 每 5 分鐘 30 次(與 dashboard 共用 key)。
上傳 SSL 憑證
POST /api/v1/domains/{id}/ssl
Authorization: Bearer fck_xxx
Content-Type: application/json
{
"cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
"key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
}校驗:
- PEM 格式合法
- 公私鑰相符
- 未過期
- CN/SAN 涵蓋本域名
成功後自動關閉自動續約(sslAutoRenew=false),改為手動憑證。
返回:
{"ok": true}四、Pages 靜態站 API
Pages 提供兩組同形端點:
| 路徑 | 鑑權 | 適用 |
|---|---|---|
/api/v1/pages/** | API key(Authorization: Bearer fck_…) | GitHub Action、CI、腳本 |
/api/pages/** | Session cookie | Dashboard、CLI 互動登入 |
以下範例都用 /api/v1/pages,並以 $FURCDN_API_KEY 代表你的 fck_… 金鑰。兩組端點的回應格式、錯誤碼與規則完全相同。除了站台概況(GET /,未解鎖也可呼叫)與兌換端點,使用前必須先兌換內測碼,否則一律回 403 pages_beta_required;帳號被封禁 Pages 時回 403 pages_banned。建立或編輯含 pages 源站的網域同樣需要解鎖。產品規則、配額與檔案限制見 FurCDN Pages。
Pages 站點沒有公開網址
站點只是一種源站(type: "pages");對外網址來自你綁定的網域。API 回應不含 url / previewUrl,改為 domains(已綁定的網域)。
站台概況
GET /api/v1/pages
Authorization: Bearer fck_xxxcurl -H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages未解鎖也可以呼叫,用來讓 UI 判斷是否顯示兌換畫面。回應(PagesOverviewJson):
{
"unlocked": true,
"banned": false,
"quota": {
"usedBytes": 1048576,
"limitBytes": 104857600,
"perSiteLimitBytes": 20971520,
"siteCount": 1,
"maxSites": 10
},
"sites": [
{
"id": 1,
"slug": "my-site",
"productionBranch": "main",
"status": "active",
"suspendedReason": null,
"bytes": 1048576,
"activeDeployment": null,
"domains": [
{
"id": 10,
"domain": "www.example.com",
"clusterId": 3,
"clusterName": "香港 / 日本",
"cname": "hk.cdn.example.com",
"enabled": true
}
],
"createdAt": "2026-10-18T00:00:00.000Z",
"updatedAt": "2026-10-18T00:00:00.000Z"
}
]
}未解鎖時 sites 為空陣列。
兌換內測碼
POST /api/v1/pages/beta/redeem
Authorization: Bearer fck_xxx
Content-Type: application/json
{"code": "PGB-XXXX-XXXX-XXXX"}curl -X POST \
-H "Authorization: Bearer $FURCDN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code":"PGB-XXXX-XXXX-XXXX"}' \
https://cdn.taipei/api/v1/pages/beta/redeem成功回 {"unlocked": true}。已開通者再兌換直接回 200,不消耗碼。無效 / 用盡 / 過期 / 停用回 400 pages_beta_invalid。每使用者與每 IP 每小時最多 10 次。
建立站台
POST /api/v1/pages/sites
Authorization: Bearer fck_xxx
Content-Type: application/json
{"slug": "my-site", "productionBranch": "main"}curl -X POST \
-H "Authorization: Bearer $FURCDN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slug":"my-site","productionBranch":"main"}' \
https://cdn.taipei/api/v1/pages/sitesproductionBranch 選填,預設 main。成功回 201 {"site": {…}}。slug 格式錯誤或命中保留字回 400 pages_bad_slug;同一帳號已有同名站點回 409 pages_slug_taken;達 10 站上限回 409 pages_site_limit。建立站台每小時最多 20 次。
站台詳情
curl -H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages/sites/my-site回 {"site": {…}, "deployments": [{…}]},deployments 由新到舊。
PagesSiteJson 欄位:
| 欄位 | 說明 |
|---|---|
id | 站點 ID |
slug | 站點代稱(同一帳號內唯一) |
productionBranch | 生產分支 |
status | active / suspended |
suspendedReason | 停用原因 |
bytes | 上線中 deployment 的位元組數 |
activeDeployment | 上線中的 deployment(沒有則 null) |
domains | 已綁定的網域(PagesBoundDomainJson[]) |
createdAt / updatedAt | ISO 時間 |
PagesDeploymentJson 欄位:
| 欄位 | 說明 |
|---|---|
id | deployment ID |
siteSlug | 站點 slug |
branch | 分支名稱 |
isProduction | 建立當下是否為 production branch |
message | 版本說明 |
files / bytes | 檔案數 / 解壓後總位元組 |
hash | 上傳封存檔的 SHA-256 |
via | 來源(dashboard / cli / action / api) |
active | 是否為目前上線版本 |
createdAt | ISO 時間 |
沒有預覽網址
PagesDeploymentJson 不含 url / previewUrl。非 production 分支的 deployment 只會儲存,不會上線;需要時用 activate 設為上線版本。
更新站台
curl -X PATCH \
-H "Authorization: Bearer $FURCDN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"productionBranch":"release"}' \
https://cdn.taipei/api/v1/pages/sites/my-site回 {"site": {…}}。之後部署到 release 分支才會自動上線。
刪除站台
curl -X DELETE \
-H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages/sites/my-site回 {"ok": true},同時刪除所有 deployment 檔案。若仍有網域以 pages 源站指向此站點,回 409 pages_bad_request 並列出網域;請先改回 HTTP 源站或刪除網域。每日流量統計(DailyStat)會保留,當月已產生的流量仍計入套餐。
部署(deployment)
POST /api/v1/pages/sites/{slug}/deployments?branch=main&message=hello&create=1
Authorization: Bearer fck_xxx
Content-Type: application/gzip
<tar.gz 或 zip 的原始位元組>body 是原始壓縮檔位元組(不是 multipart)。格式由 magic bytes 判斷:gzip → tar.gz,ZIP → zip;其他回 400 pages_bad_archive。壓縮檔 ≤ 25MB,解壓後 ≤ 20MB、≤ 2000 個檔案,根目錄必須有 index.html。
tar -czf site.tar.gz -C dist .
curl -X POST \
-H "Authorization: Bearer $FURCDN_API_KEY" \
-H 'Content-Type: application/gzip' \
--data-binary @site.tar.gz \
"https://cdn.taipei/api/v1/pages/sites/my-site/deployments?branch=main&message=hello&create=1"Query 參數:
| 參數 | 說明 |
|---|---|
branch | 分支名稱;省略時用站台的 productionBranch |
message | 版本說明 |
create | 1 時若站台不存在會先建立(同樣檢查 slug、站數與配額) |
成功回 201 {"deployment": {…}, "site": {…}}。branch 等於 production branch 時,成功後自動上線並清除所有綁定網域的快取;其他分支僅儲存,不會上線。
其他錯誤:超過配額 413 pages_quota_exceeded(附 usedBytes / limitBytes)、單站過大 413 pages_site_too_large、缺 index 400 pages_no_index、站台停用 403 pages_site_suspended。部署限速為每使用者每分鐘 6 次。
重新上線(rollback / promote)
curl -X POST \
-H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages/sites/my-site/deployments/42/activate任何已上傳的 deployment 都可以上線,且上線後不得超過 100MB。成功回 {"site": {…}},並自動清除綁定網域的快取。這就是 rollback(回到舊版)與 promote(把非生產版本推上正式)。限速為每分鐘 20 次。
刪除 deployment
curl -X DELETE \
-H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages/sites/my-site/deployments/42回 {"ok": true}。不能刪除目前上線中的版本,會回 409。每站最多保留 5 筆,超過時系統自動刪除最舊且非上線中的版本。
列出已綁定網域
curl -H "Authorization: Bearer $FURCDN_API_KEY" \
https://cdn.taipei/api/v1/pages/sites/my-site/domains回 {"domains": [PagesBoundDomainJson, …]}:
| 欄位 | 說明 |
|---|---|
id | 網域 ID |
domain | 完整網域 |
clusterId / clusterName | 接入叢集 |
cname | 要設定的 CNAME 目標 |
enabled | 網域是否啟用 |
綁定網域
POST /api/v1/pages/sites/my-site/domains
Authorization: Bearer fck_xxx
Content-Type: application/json
{"domain": "www.example.com", "clusterId": 3}curl -X POST \
-H "Authorization: Bearer $FURCDN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"www.example.com","clusterId":3}' \
https://cdn.taipei/api/v1/pages/sites/my-site/domains成功回 201 {"domain": {…}, "cname": "…", "site": {…}}。這支端點走既有「建立網域」邏輯:檢查網域格式、套餐 maxDomains、可用叢集與實名要求,建立 origins = [{type:"pages", pagesSiteId}] 的網域,並沿用自動 SSL。錯誤原樣透傳既有網域錯誤(例如網域已存在、超過套餐網域數、套餐未含此叢集),沒有 pages_domain_* 專屬碼。綁定限速為每小時 30 次。
綁定後可在網域管理切換叢集、設定 SSL / WAF / 快取或刪除網域。
切換綁定網域的叢集
綁定後可隨時切換接入叢集(大陸 / 港日),規則與建立網域相同(可用叢集、maxClusters、實名要求):
PUT /api/domains/123
Cookie: furcdn_token=…
Content-Type: application/json
{"clusterId": 3}furcdn domain update example.com --cluster 3回應:
{
"domain": {
"id": 123,
"clusterId": 3,
"cluster": {"id": 3, "name": "香港 / 日本", "cname": "hk.cdn.example.com"},
"dnsTarget": "www.example.com.cdn-zone.example.com",
"dnsUpdated": true
}
}- 平台管理的 DNS 別名(
dnsTarget)會自動改指向新叢集;若使用者的 CNAME 設到這個別名,不需改 DNS。若直接 CNAME 到舊叢集 CNAME,需自行改成新叢集 CNAME。 - 切到同一個叢集為 no-op(
dnsUpdated: false,不寫 audit)。 dnsNote: "aliyun_not_configured"表示 DB 已切換,但別名未更新(Alibaba DNS 未設定)。- 錯誤:400 叢集無效 / 超出套餐可使用叢集數;403 套餐未含此叢集 / 需要實名 / 不是網域擁有者;404 網域不存在;409 併發切換;502 DNS 別名更新失敗(已還原原叢集)。
- 切換會清除舊、新叢集的快取;若啟用 ECH,兩邊的 HTTPS DNS 記錄也會同步。audit action 為
domain_switch_cluster。 GET /api/domains已包含clusterId、cluster、clusterCname、dnsTarget;GET /api/clusters回傳方案允許的{clusters:[{id,name,cname,region,requireRealName}]}。
把既有網域源站改指到 Pages
origins 陣列現在接受 type: "pages"。以下為 dashboard / CLI 使用的 session API(PUT /api/domains/:id):
PUT /api/domains/123
Cookie: furcdn_token=…
Content-Type: application/json
{
"origins": [
{ "type": "pages", "pagesSite": "my-site" }
]
}- 輸入可用
pagesSite(slug,限自己的站點)或pagesSiteId。 - 伺服器驗證站點歸屬、站點狀態與內測解鎖後,正規化為
{ "type": "pages", "url": "pages://<siteId>", "pagesSiteId": <id>, … };輸出與節點設定一律以pagesSiteId為準。 - 可保留 HTTP 源站作為備援:
{"origins":[{"type":"pages","pagesSite":"my-site"},{"type":"http","url":"https://origin.example.com","backup":true}]}。 - 對應 CLI:
furcdn domain origin set example.com --pages my-site(加--backup則附加為備援)。
公開檢舉(免登入)
POST /api/pages/report
Content-Type: application/json
{"target": "www.example.com", "reason": "phishing", "details": "偽造銀行登入頁", "email": "optional@example.com"}curl -X POST \
-H "Content-Type: application/json" \
-d '{"target":"www.example.com","reason":"phishing","details":"偽造銀行登入頁"}' \
https://cdn.taipei/api/pages/reporttarget 可為網域或完整 URL;伺服器取 hostname,並比對出背後是否有 Pages 站點(找不到仍會記錄)。reason 為 phishing / malware / illegal / other;details、email 選填。回 {"ok": true},每 IP 每小時 5 次。這支端點不需要 API key。
錯誤格式
{"error": "上線後會超過容量上限", "code": "pages_quota_exceeded", "usedBytes": 104857600, "limitBytes": 104857600}完整錯誤碼與處理方式見 FurCDN Pages。
furcdn pages 指令表
CLI 有 FURCDN_API_KEY(或 furcdn config set-key)時走 /api/v1/pages,否則用 furcdn login 的 session 走 /api/pages。所有指令都支援 --json。
| 指令 | 說明 |
|---|---|
furcdn pages list | 列出站台、用量與已綁網域 |
furcdn pages create <slug> | 建立站台(可加 --production-branch 指定 production branch,預設 main) |
furcdn pages deploy <dir> --site <slug> | 打包目錄成 tar.gz 並部署;支援 --branch、--message、--create |
furcdn pages deployments <slug> | 列出 deployment(新到舊) |
furcdn pages activate <slug> <id> | 把指定版本設為上線版本(rollback / promote) |
furcdn pages rm-deployment <slug> <id> | 刪除指定版本(不可刪上線中版本;-y 略過確認) |
furcdn pages delete <slug> | 刪除站台(仍有綁定網域時會失敗;-y 略過確認) |
furcdn pages usage | 顯示容量用量、站數與單站上限 |
furcdn pages beta redeem <code> | 兌換內測碼 |
furcdn pages beta status | 查詢內測開通狀態 |
furcdn pages domains <slug> | 列出站台已綁定的網域 |
furcdn pages bind <slug> <domain> --cluster <id> | 綁定網域;省略 --cluster 時 TTY 互動選擇,非 TTY 需明確指定 |
furcdn info clusters | 查叢集 ID / 名稱 / CNAME / 地區 / 是否需實名 |
furcdn domain origin set <domain> --pages <slug> | 把既有網域的主源站改指到 Pages(--backup 可附加為備援;與 --url 互斥) |
deploy 會先在本機檢查 20MB、檔案數與根目錄 index.html,打包時預設忽略 .git、node_modules、.DS_Store。
五、限速
全域限速規則
| 端點 | 限速 |
|---|---|
POST /api/v1/domains/{id}/purge | 10/5min/user + 30/5min/IP |
POST /api/v1/domains/{id}/ssl | 6/h/user(防爆破) |
GET /api/v1/* | 600/min/user |
為什麼跨入口共用 key
purge 操作的限速 key 是 purge:<userId>,dashboard 與 API 共用:
- 防止使用者透過 API 繞過 dashboard 的限速
- 帳戶級別防濫用,而不是「按入口分流」
六、IDOR 防護
每個操作前都驗證:
const d = await prisma.domain.findUnique({ where: { id } });
if (!d || d.userId !== user.id) return 403;API key 對應的 user 只能操作自己的資源。即使你拿到別人 domain 的 id,因為不屬於你,系統返回 403。
七、錯誤格式
所有錯誤回應為:
{"error": "錯誤訊息(中文)"}Pages 端點另會帶 code(例如 pages_quota_exceeded),部分錯誤附帶 reasons、usedBytes、limitBytes 等欄位。
HTTP status:
| code | 含義 |
|---|---|
| 400 | 參數錯 / 校驗失敗 |
| 401 | 未授權(沒帶金鑰 / 過期) |
| 403 | 操作對象不屬於你 |
| 404 | 資源不存在 |
| 429 | 限速觸發 |
| 500 | 伺服器錯誤(檢查 dashboard 系統公告) |
八、實踐範例
CI 部署後自動刷快取
GitHub Actions:
name: Deploy & Purge CDN
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & deploy to origin
run: |
# 你的部署邏輯,如 rsync 到源站
- name: Purge CDN cache
env:
FURCDN_KEY: ${{ secrets.FURCDN_KEY }}
run: |
curl -fsS -X POST \
-H "Authorization: Bearer $FURCDN_KEY" \
https://cdn.taipei/api/v1/domains/123/purgeFURCDN_KEY 存 GitHub Secrets,不要寫進 repo。
Let's Encrypt 自動上傳
cron 每月跑一次,從本地 ACME 客戶端拿到新憑證後上傳:
#!/bin/bash
DOMAIN_ID=123
CERT=$(cat /etc/letsencrypt/live/example.com/fullchain.pem)
KEY=$(cat /etc/letsencrypt/live/example.com/privkey.pem)
curl -fsS -X POST \
-H "Authorization: Bearer $FURCDN_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg c "$CERT" --arg k "$KEY" '{cert: $c, key: $k}')" \
https://cdn.taipei/api/v1/domains/$DOMAIN_ID/sslPython 一行刷快取
import requests
requests.post(
f"https://cdn.taipei/api/v1/domains/{DOMAIN_ID}/purge",
headers={"Authorization": f"Bearer {FURCDN_KEY}"},
timeout=10,
).raise_for_status()Node.js
const r = await fetch(
`https://cdn.taipei/api/v1/domains/${id}/purge`,
{
method: "POST",
headers: { Authorization: `Bearer ${process.env.FURCDN_KEY}` },
},
);
if (!r.ok) throw new Error(await r.text());九、安全建議
✅ Do
- 為每個服務 / 機器單獨建一把金鑰(出問題能精確 revoke)
- 設過期時間:即使洩漏也有時限
- 把金鑰存環境變數 / Secret Manager,不要 commit 進 git
- 定期看「末次使用時間」,長期沒用的金鑰刪掉
- 替生產 / 測試 / 個人分別建獨立金鑰
❌ Don't
- ❌ 把金鑰寫進前端 JS(會被 view-source 看到)
- ❌ 把金鑰存到公開 repo(GitHub 會偵測外洩金鑰並公告)
- ❌ 多人共用同一把金鑰(出事追責困難)
- ❌ 金鑰名稱裡寫密碼或敏感字串(列表頁顯示出來)
金鑰外洩的緊急處置
- 立刻進 dashboard 刪掉外洩的金鑰(刪了就立即失效)
- 建新金鑰,更新所有用到的地方
- 看「操作記錄」確認攻擊者做了什麼(
purge_cache_api、api_key_*等動作) - 若有跡象表明攻擊者嘗試其他敏感操作,額外改密 / 改郵 / 重新掃通行密鑰列表
十、更多端點規劃
目前 v1 開了最常用的:列域名、刷快取、上傳憑證,以及 Pages 靜態站(站台 / 部署 / rollback)。後續會逐步開放:
- 取 / 設域名 origin / cache rules / WAF rules
- 拉訪問日誌 / 統計
- 域名 CRUD
需求迫切的話開工單告訴我們。
十一、API 對外端點 vs 內部端點
平台同一個 Next.js 進程下兩類端點:
| 路徑 | 鑑權 | 用途 |
|---|---|---|
/api/v1/* | API key (Bearer) | 對外開放 API(本文檔) |
/api/dashboard/*,/api/user/*,/api/admin/* | JWT cookie | dashboard 前端內部 ajax,不對外 |
/api/node/* | NODE_TOKEN (Bearer) | 節點上報 / 拉 config(節點專用) |
/api/pay/* | RSA 簽名 | 支付平台 callback |
/api/acme/* | 公開 | Let's Encrypt 驗證 |
/api/pages/report | 公開 | Pages 內容檢舉(無需登入) |
不要嘗試用 API key 呼叫
/api/user/*或/api/dashboard/*,鑑權方式不同會 401。