Skip to content

Repository files navigation

鐵人觀察家 Next

2026 iThome 鐵人賽的每日觀察儀表板。靜態站,GitHub Actions 定時抓資料,Cloudflare Pages 免費託管。

🔗 線上站:https://ithome-ironman-observer.happyhacking.ninja/

網站有四頁:

  • 賽事觀察台(首頁):每支參賽系列一張卡片,看得到進度和人氣
  • 團隊計分板:把組團視為參賽單位,總瀏覽、人均、平均進度、今日發文與落後警示
  • 名人堂:表列具公眾知名度的參賽作者(如高見龍),附一句話介紹、可驗證來源連結與其系列文章
  • Insights 分析:整屆賽事的分析,發文時段、人氣分佈、組別、標題文字等圖表

專案起源

本專案源自 qrtt1/ithome-ironman 的 ITHome 鐵人觀察家(觀戰區), 因原專案已停止維護,故另開新專案接手這個概念,以現代工具鏈(Bun + Astro + GH Actions + Cloudflare Pages)重寫。

這個專案的挑戰

這是一個實驗:以 Command Code 的 $1/月 訂閱,搭配 DeepSeek v4 Flash, 能否真的做出一個有趣、可用的東西?

整個專案(scraper、儀表板、Insights 分析、CI/CD、部署、測試)都由這個組合完成。

費用紀錄

第一個核心功能版本的費用:

項目 數值
Total Tokens 57.5M
費用 $0.04
Runs 839

Command Code usage

功能

賽事觀察台(首頁 /

  • 年度切換(年度清單以 meta.jsonyears 為準)
  • 組別篩選 + 我的收藏分頁(以系列 ID 為 key 跨年度共用,只存在本裝置 localStorage)
  • 排序:參賽進度、最多觀看、最新發文、當篇觀看(今日)
  • 搜尋(標題/作者/組別/團隊,token AND)
  • 團隊計分板:把組團視為參賽單位,總瀏覽/人均/平均進度/今日發文 + 落後警示(今日缺發/停更/未開賽),可展開成員並跳轉該隊系列流;另有獨立分頁 /teams/(header 右上角入口)
  • 名人堂(/hall-of-fame/):表列具公眾知名度的參賽作者(如高見龍),附一句話介紹、可驗證來源連結與其系列文章。
  • 卡片狀態標示:DAY n/30 進度條、完賽、已刪文、尚未開賽
  • 靜態頁每 60 秒客端重讀年度 JSON;抓取錯誤顯示在 scrapeLog 狀態列
  • 深色/淺色主題(預設隨系統,可手動切換)、響應式

Insights 分析(/insights/

用 ECharts 呈現整屆賽事的資料分析,支援客端切換年度:

  • 發文時段直方圖、發文星期、星期 × 小時熱力圖
  • 瀏覽數分佈(分桶 + 百分位 CDF)、訂閱數 Top 系列
  • 組別分析(系列數/文章數/平均瀏覽/總訂閱)
  • 標題關鍵字詞頻、標題長度分佈
  • 棄賽進度斷崖、互動轉換率排行、斷更風險名單

分析計算都是純函式(web/src/lib/insights.ts),附單元測試;時段和星期用臺北時間(UTC+8)算,不依賴瀏覽器時區。

品質

  • SEO: robots.txt + llms.txt,方便爬蟲和 AI agent 讀取
  • 無障礙: 循序標題層級、muted/badge 配色符合 WCAG AA 對比
  • 效能: 內嵌 critical CSS、SSR 卡片分批切片、content-visibility: auto、分塊渲染、iThome preconnect
  • Lighthouse: Performance 99、Accessibility 100、Best Practices 100、SEO 100(報告在 docs/lighthouse-report.html

架構與資料

  • 沒有後端、沒有資料庫,JSON 就是資料庫。
  • 每次抓取輸出 data/{year}.jsondata/meta.json,並寫一份每日歷史快照到 data/history/{year}/{date}.json。 快照每次都寫(看似浪費,實際上與同一輪的 data/{year}.json 內容完全相同,git 內容定址只存一份 blob)。
  • 硬限制:近乎零成本,靠 Cloudflare Workers/Pages 免費額度 + GH Actions 公開 repo 免費 runner + 自有網域撐著。
  • Non-goals:即時更新(只有每 10 分鐘一次的批次)。

本地開發

bun install
bun run scripts/scrape.ts     # 依 series-manifest 陣列逐年度抓取;全失敗零寫入、exit 1
bun run scripts/scrape.ts --full   # 完整校正(不走 RSS 快速路徑,重讀每個系列頁)
cd web && bun install && bun run dev

系列頁回傳 HTTP 403 或無效 HTML 時,RSS fallback 會依文章 ID 合併快取,保留 RSS 未包含的舊文章。 此結果記錄為 [stale]:文章完整性與互動統計尚未驗證;空白/無效 RSS 保留快取,無快取則 [failed]rssFallback 標記會保留到完整 HTML 抓取成功(如每日 --full 校正)。 增量抓取採 RSS-First 策略:先前已降級且 RSS 無新文章、天數未增加者,直接複用 RSS 摘要避免無效敲 HTML 系列頁(節省 50% 請求並防範 WAF)。 僅在系列頁與 RSS 皆遭遇 403 阻斷或無法取得時,才計入連續 403 中止與熔斷器保護,兼顧大規模封鎖防護與海外機房正常排程更新。

web/public/data/ 是建置產物(web/scripts/copy-data.mjsdata/*.json 產生), 已 gitignore;bun run devbun run build 都會先跑一次 copy。

收藏(Favorites)

卡片右上角星號可收藏系列;「我的收藏」分頁只顯示已收藏系列,沿用排序器。 收藏以系列 ID 為 key 跨年度共用,只存在本裝置瀏覽器(localStorage),不同裝置和瀏覽器之間不互通。

測試與檢查

bun run check      # lint + typecheck + 單元測試 + astro check(CI 跑的同一組)
bun test           # 只跑單元測試
bun run lint       # 只跑 Biome

CI(.github/workflows/ci.yml)在 push 到 main 與所有 PR 上跑這整組。 Biome 只開 linter、不開 formatter,理由見 docs/tooling.md

部署(已上線,僅供參考)

  1. Cloudflare Pages 專案 ironman-observer-next(workflow 會自動建立)

  2. GitHub repo secrets:CLOUDFLARE_API_TOKEN(Pages Edit 權限)、CLOUDFLARE_ACCOUNT_ID

  3. 自有網域在 Cloudflare dashboard → Pages 專案 → Custom domains 設定

  4. Cloudflare Worker ironman-observer-trigger(cron 0 */2 * * *,secrets: GITHUB_TOKENGITHUB_REPODISPATCH_SECRET)定時觸發 workflow;也可 gh workflow run scheduled-update 手動觸發

    POST /dispatch 需要 Authorization: Bearer $DISPATCH_SECRET(該端點會觸發一整輪爬蟲, 不能開放匿名呼叫);GET / 維持公開健康檢查。secrets 設定見 .github/workflows/README.md

About

2026 iThome 鐵人賽每日觀察儀表板:追蹤每支參賽系列的進度與人氣,附 Insights 分析與團隊計分板

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Contributors

Languages