Skip to content

Introduction

Hsiehting Lin edited this page Jul 3, 2026 · 1 revision

專案介紹

Hematok 是把 ASH Image Bank 的血液學/病理學影像,做成 TikTok 式垂直捲動的學習介面。UI fork 自 WikiTok,改寫為讀取靜態 feed(feed.json),影像位元組由 Cloudflare R2 供應。

功能特色

  • 垂直 snap-scroll 無限捲動,共 6,973 筆紀錄 / 7,950 張影像
  • 單擊隱藏介面元素;全螢幕雙指縮放檢視影像細節
  • Details sheet 顯示完整 metadata;其中 134 個 reference case 帶有完整臨床描述
  • 多影像的 reference case 以卡片內畫廊左右滑動
  • 語意推薦:依「喜歡」的影像找出相似影像(like-based recommendations),「不喜歡」可反向調整
  • PWA:可安裝、Service Worker 自動更新
  • Owner 登入:站長的 like / dislike / read 紀錄存到伺服器端,跨裝置同步並影響推薦

系統架構

瀏覽器
 ├─ Cloudflare Pages(hematok)         React 前端,載入 public/feed.json
 ├─ R2(hematok-img,公開自訂網域)      影像位元組,URL = ${VITE_IMG_BASE}/<key>
 ├─ Worker(hematok-rec-api)           /recommend 語意推薦(Vectorize + Workers AI)
 └─ Pages Functions(/api/*)           owner 登入與伺服器端互動紀錄(KV)

匿名使用者(預設)

  • like / dislike 存在 localStorage;已讀(seen)存在記憶體 + localStoragereadImages
  • 推薦呼叫公開、無狀態的 hematok-rec-api.hsieh-ting-lin.workers.dev/recommend

Owner 登入後

  • 瀏覽器改走同源的 hematok.pages.dev/api/*(Pages Functions):
    • GET /api/me — 目前身分
    • GET /api/state — 從 KV 取回 { liked[], disliked[], read[] }
    • POST /api/events — 記錄 like / unlike / dislike / undislike / read / unread
    • POST /api/merge — 登入時把 localStorage 歷史 union 進 KV
    • GET /api/login — Access 登入 trampoline(email 路徑用)
  • 狀態存 Cloudflare KV,key 為 u:<email>(binding 名 STATE)
  • 推薦邏輯不變:登入後客戶端一樣呼叫公開 /recommend,只是 liked/disliked/seen 改由伺服器 hydrate

認證的兩條路徑(frontend/pages-lib/gate.ts)

  1. Cloudflare Access email 登入(設計上的最終方案,「方向 A」) — 由 Access 注入 Cf-Access-Jwt-Assertion,函式讀取 email claim 比對 allow-list。Access 閘門尚未建立(見 Tech-Debt)。
  2. Owner bearer token(現行可用) — header 的 Login 輸入 token,以 Authorization: Bearer <token> 送出,與 Pages secret OWNER_TOKEN 做 constant-time 比對。

目前唯一允許的使用者是 hsieh.ting.lin@gmail.com

目錄結構

frontend/            React + Vite + Tailwind 前端
  public/feed.json   前端載入的 feed(由 prep/build_feed.py 產生)
  functions/api/     Pages Functions(me/state/events/merge/login)
  pages-lib/         共用後端邏輯(gate.ts 認證)
worker/              hematok-rec-api 推薦 Worker
prep/
  build_feed.py      index.jsonl → frontend/public/feed.json(含 refcase 描述)
  build_embeddings.py  建立 Vectorize 的向量索引
  upload_r2.sh       rclone 上傳器 → R2(讀取 ../.env)
docs/plans/          設計文件(見 [[Plan]])
plan-for-deploy-r2.md  部署 runbook
mock-data/           smoke test 用的小型 schema 鏡像(非真實資料)

關鍵不變量

feed.json 裡的 image key 必須等於 R2 的 object key:

來源檔案(~/ash-image-bank/data/) R2 object key
collection/<id>/<id>.jpg collection/<id>/<id>.jpg
atlas/<id>/<id>.jpg atlas/<id>/<id>.jpg
reference-cases/<id>/images/<imgid>.jpg reference-cases/<id>/images/<imgid>.jpg

若重新爬取後目錄結構改變,必須同時修改 prep/build_feed.pykeys_for()、重新產生 feed.json,並調整 rclone 上傳來源,讓兩邊 key 保持一致。

版權

影像與 metadata © American Society of Hematology 及原始貢獻者。UI fork 自 WikiTok。

Clone this wiki locally