FurCDN 文檔

開放 API

平台對外開放的 HTTP API,用於程式化控制 CDN(部署管線整合 / CI 自動刷快取 / 監控腳本等)。

平台對外開放的 HTTP API,用於程式化控制 CDN(部署管線整合 / CI 自動刷快取 / 監控腳本等)。

官方 SDK

平台官方維護的 SDK,封裝鑑權、錯誤處理、JSON 序列化,直接呼叫即可。

語言倉庫安裝
GoFurCDN/go-sdkgo get github.com/FurCDN/go-sdk@v0.1.0
JavaFurCDN/java-sdkJitPack:com.github.FurCDN:java-sdk:0.1.0
KotlinFurCDN/kotlin-sdkJitPack: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.pem

domains 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:

  1. 點「新增金鑰」
  2. 填:
    • 名稱:顯示用,如「CI 部署機器」
    • 過期時間(選填):留空 = 永不過期
  3. 提交後彈窗顯示完整金鑰(僅此一次,務必複製保存)

金鑰格式

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_xxxxxxxxxxxxxxxxxxxxxxxx

curl 範例:

curl -H "Authorization: Bearer fck_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxx" \
     https://cdn.taipei/api/v1/domains

驗證流程

  1. 取 Authorization header,剝 Bearer 前綴
  2. SHA-256 hash 整把金鑰
  3. DB 查 keyHash 匹配
  4. 檢查 expiresAt(若有)是否過期
  5. 通過 → 返回對應 user(後續端點權限按該 user 檢查)
  6. 更新 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 cookieDashboard、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_xxx
curl -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/sites

productionBranch 選填,預設 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生產分支
statusactive / suspended
suspendedReason停用原因
bytes上線中 deployment 的位元組數
activeDeployment上線中的 deployment(沒有則 null)
domains已綁定的網域(PagesBoundDomainJson[])
createdAt / updatedAtISO 時間

PagesDeploymentJson 欄位:

欄位說明
iddeployment ID
siteSlug站點 slug
branch分支名稱
isProduction建立當下是否為 production branch
message版本說明
files / bytes檔案數 / 解壓後總位元組
hash上傳封存檔的 SHA-256
via來源(dashboard / cli / action / api)
active是否為目前上線版本
createdAtISO 時間

沒有預覽網址

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版本說明
create1 時若站台不存在會先建立(同樣檢查 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/report

target 可為網域或完整 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}/purge10/5min/user + 30/5min/IP
POST /api/v1/domains/{id}/ssl6/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/purge

FURCDN_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/ssl

Python 一行刷快取

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 會偵測外洩金鑰並公告)
  • ❌ 多人共用同一把金鑰(出事追責困難)
  • ❌ 金鑰名稱裡寫密碼或敏感字串(列表頁顯示出來)

金鑰外洩的緊急處置

  1. 立刻進 dashboard 刪掉外洩的金鑰(刪了就立即失效)
  2. 建新金鑰,更新所有用到的地方
  3. 看「操作記錄」確認攻擊者做了什麼(purge_cache_api、api_key_* 等動作)
  4. 若有跡象表明攻擊者嘗試其他敏感操作,額外改密 / 改郵 / 重新掃通行密鑰列表

十、更多端點規劃

目前 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 cookiedashboard 前端內部 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。

© 2022-2026 Taipei101 Network 版權所有

9900 Corporate Campus Dr Ste 3000, Louisville, KY 40223, United States

由 langya.io 驅動

On this page