更新日誌

GitHub

每一項重大變更,逐版記錄。同一份檔案隨儲存庫發布,並已翻譯為全部 19 種語言。

[1.7.0] — 2026-05-03

由 QA r5 驅動的 35-commit 強化 + UX + 功能完成 pass。三層安全性落地(XSS 淨化、CSP、輸入驗證),每個缺失的 CRUD 端點補齊,父專案 bootstrap 現已完全自動化,UI 取得 9 個新頁面 — Activity、改版 Deep Research,以及 7 個 sidebar 分組 mode(project / training / followup / batch / outreach / interview-prep / patterns),完整涵蓋父專案 modes/ 100 %。Pipeline 取得伺服器端 preview pane。Anthropic API 整合讓「Run live」在所有 mode 變成一鍵動作。測試覆蓋從 73 增至 225,跨 25 個測試檔案,加上 23 個 comprehensive Playwright e2e 步驟。GitHub Actions 出貨 CI / AI review / Release / Publish-Package workflow。

🔒 安全性

  • fix(cv): 淨化 CV markdown 以阻擋 preview 中的 stored XSS(FIX-C10) — PUT /api/cv 寫入 cv.md 前,現在會去除 <script>、<iframe>、<object>、<embed>、<style>、<form>、<svg>、on*= 事件處理器、以及 javascript:/vbscript:/data:text/html URI。Body 上限 1 MB(溢出 413)。Client-side UI.md() 重寫,使每個 byte 在任何 markdown 變換執行前都先 escape,所以原始 HTML 永遠到不了 innerHTML。Link href 屬性對安全 scheme 白名單(http/https/mailto/tel/相對 + 僅 data:image)驗證。新增 17 個跨 strip helper 與 HTTP round-trip 的測試。
  • fix(server): 加入 CSP 與基線安全標頭(FIX-L2) — 每個 response 現在攜帶 X-Content-Type-Options: nosniff、X-Frame-Options: DENY、Referrer-Policy: same-origin。當伺服器繫結超過 loopback(HOST ≠ 127.0.0.1/::1/localhost)時,疊上嚴格 Content-Security-Policy:default-src 'self'、script-src 'self'(無 unsafe-inline)、Google Fonts 白名單、connect-src 'self' 阻擋 XSS 外洩。index.html 與 router.js 中的行內 onclick 處理器移至 addEventListener 以保留嚴格 CSP。8 個新測試把關 CSP 跨 5 個不同 HOST 值。
  • fix(api): 收緊 pipeline URL 驗證器(FIX-M7) — POST /api/pipeline 過去接受 "not-a-url" 並持久化。現在 isValidJobUrl() 拒絕裸字串、輸入 <10 或 >2000 字元、含空白 URL、非 http(s) scheme,以及 loopback 主機名稱(localhost/127.0.0.1/::1)。納入 FIX-M3 + FIX-M6(無效時回 400,成功時帶 deduped flag)。
  • fix(server): 實際載入 .env,讓 HH_USER_AGENT / GEMINI_API_KEY 提示生效 — 先前執行期告訴使用者「在 .env 設定 HH_USER_AGENT」,但伺服器從未讀取該檔案,因此照做沒用。新增 35 列零相依 dotenv loader(server/lib/dotenv.mjs)於 server/index.mjs 頂部接線。命令列上設定的 process-env 值仍優先,既有 CI override 不會被遮蔽。父 .env.example 現包含記載的 HH_USER_AGENT 區塊與真實 Chrome User-Agent 範例。6 個新測試。
  • fix(api): prompt 組裝前淨化 JD(FIX-M5) — POST /api/evaluate 在呼叫 Gemini 或回送 prompt 前,去除 ANSI escape、控制位元組、行內 <script> 標籤,並修剪空白。50 KB 長度上限。50 字元最小限制對「淨化後」文字運行,因此看似夠長但多為 escape 的 prompt-injection 嘗試會以 400 快速失敗。
  • fix(health): 當 HOST!=loopback 時遮罩 Node 版本 + 專案 root(FIX-M1) — /api/health 不再於 LAN 暴露部署上識別主機。Loopback 回應仍保留值以利本地診斷。

✨ 新功能

  • feat: 7 個新 sidebar mode + 分組 sidebar(FIX-C8) — 完整涵蓋父專案 modes/ 目錄 100 %,無 UI 空缺。新路由:#/project(portfolio project advisor)、#/training(course / cert evaluation)、#/followup(per-application cadence)、#/batch(parallel URL processor)、#/contacto(LinkedIn outreach drafter)、#/interview-prep(stage-specific prep)、#/patterns(rejection-pattern analyzer)。全部 7 個共用單一 config-driven 視圖工廠(public/js/views/mode-page.js)與單一通用端點 POST /api/mode/:slug — 未來新增 mode 是一列 config + 一個 i18n 區塊。Sidebar 重組為 6 群組:Sourcing / Decision / Application / Networking / Analytics / Setup。共 18 個 nav 條目。12 個新測試於 tests/modes-endpoints.test.mjs。
  • fix: bootstrap 父專案 deps + russian_portals 預設(FIX-C4 + C9 + C12 + H2) — bin/start.sh 現在於 fresh clone 安裝父 node_modules(js-yaml、playwright、jsdom)與 npx playwright install chromium,讓 /api/stream/scan、/pdf 與 /liveness 開箱端對端可用。createApp() 每次啟動探測 portals.yml — 若缺 russian_portals: 區塊,附加帶註解的記載預設。冪等:第二次啟動為 no-op。3 個新測試。
  • fix: 在 template + health-check script 停用 9 個失效 portal slug(FIX-C3) — templates/portals.example.yml 現在出貨時將 Ada / Factorial / Tinybird / Weights & Biases / Travelperk / Clarity AI / Forto / Vinted / Runway 標為 enabled: false(每條附行內原因註解)。新安裝掃描 87 個存活公司而非 96。新 web-ui/scripts/portals-health-check.mjs HEAD-probe 每個啟用之 careers_url 並回報 DEAD 條目並建議 patch 清單(透過 --json 輸出 JSON)。3 個新測試。
  • feat(activity): 使用者動作日誌 + Activity sidebar 頁面 — 每個狀態變更 API 請求皆捕捉至 data/activity.jsonl(timestamp、action verb、target、success flag、可選 detail)。新 sidebar 條目 Activity,帶 action-prefix chip 篩選(pipeline / cv / jd / evaluate / scan / stream / script)、動作 ✓/✗ 徽章與刷新按鈕。5 MB 自動 rotate。10 個新測試,涵蓋 middleware、讀取篩選、損壞行容忍、GET /api/activity 本身的遞迴防護。
  • feat(deep): 於瀏覽器檢視 Deep Research + 儲存結果存檔 — Deep Research 頁面現在(a)當 { run: true } 且設定 GEMINI_API_KEY 時透過 Gemini 即時執行 prompt,並將輸出持久化至 interview-prep/{slug}.md;(b)以可點擊卡片列出每個儲存的 deep-research 檔案(帶相對 timestamp);(c)將結果渲染為 Markdown,並提供每結果 📋 Copy / ⬇ Download .md / ↗ Open in tab 動作。新 REST 介面:GET /api/interview-prep、GET /api/interview-prep/:name、DELETE /api/interview-prep/:name。7 個新測試。
  • feat(cv): 於瀏覽器產生 + 下載 PDF,並含 PDF 存檔 — CV 頁面新增 📄 Generate PDF 按鈕,於 modal console 串流 /api/stream/pdf。發生 ERR_MODULE_NOT_FOUND / playwright 錯誤時,呈現可複製貼上的 bootstrap 命令。新「Generated PDFs」段落於每次成功執行後自動載入,列出每個 output/*.pdf 並提供 ↗ Open 與 ⬇ Download 按鈕。新 REST 介面:GET /api/output/pdfs、GET /api/output/pdfs/:name。6 個新測試。
  • feat(api): POST /api/tracker — 從 UI 附加列(FIX-H8) — 從瀏覽器附加規範列至 data/applications.md。驗證 company + role、對 templates/states.yml 正規化 status、自動遞增零填補 #、依 company+role(大小寫不敏感)dedup、對 notes pipe-escape 以免 markdown 表格斷裂。檔案為空時 bootstrap 表格。6 個新測試。
  • feat(api): DELETE /api/jds/:name(FIX-H4) — 不再 shell-out 即可移除儲存之 JD。Path-traversal 字元於任何檔案系統觸碰前去除;參數必須以 .txt 結尾。5 個新測試,包含 ../../etc/passwd 拒絕。
  • feat(api): POST /api/evaluate/test-gemini(FIX-H7) — Smoke 測試端點,透過 gemini-eval.mjs 跑 50 字元假 JD,讓使用者可在不耗實際 evaluation 時間下驗證 API key 是否有效。回傳 { ok, code, sampleLength, sample }。

🐛 Bug fixes

  • fix(router): catch-all 404 視圖 + i18n 覆蓋率防護(FIX-C7) — 未知 hash 路由過去無聲後備至 dashboard,遮蔽 typo 與壞掉的書籤。現在 #/totally-random-xyz 渲染專屬 404 頁面,引用該壞路徑並提供 dashboard 連結。404 視圖在 router IIFE 內註冊,因此不會與任何使用者路由衝突。新 tests/i18n-coverage.test.mjs 於 vm.Context 內以 stub window 執行 i18n.js、揭露私有 DICT,並斷言 173+ 個 key 跨 8 個 locale 皆已填入且非空。4 個新 router 測試。
  • fix(router): 別名 #/profile → settings(FIX-C2) — 內部路由名為 settings(nav.settings 渲染為「Profile」)但外部連結與肌肉記憶都會走 #/profile。現在兩個位址抵達同一視圖,sidebar nav 條目以任一方式都亮起。2 個新測試。
  • fix(health): 統一 Health/Doctor + 標記模板 profile(FIX-C6 + FIX-H6) — Health 與 Doctor 為兩個不同真實來源。現在 /api/health 揭露 Doctor 報告之全部(parent-deps、Playwright、目錄、profile-customized、HH_USER_AGENT)。Profile customized 檢查偵測佔位名稱(Jane Smith、Alex Doe、John Doe、Your Name、Test User)與明確 YAML 解析錯誤。4 個新測試。
  • fix(scan): 對 RU config 之 query↔negative 衝突發警告(FIX-H3) — 當 portals.yml 出貨時 title_filter.negative 含 "PHP",而 queries 鎖定 Senior PHP,每個匹配都被篩掉,使用者看到 0 結果。loadConfig() 現在計算 warnings 陣列;runRuScan() 在掃描開始前以 SSE stderr 列發出每個警告。2 個新測試驗證出貨預設開箱保持 PHP 友善。
  • fix(scan): HH_USER_AGENT 未設定時發警告(FIX-H1) — /scan 頁面探測 /api/health,在 HH_USER_AGENT 為空時於動作列上方顯示黃色警告卡,使用者在點 RU scan 之前即得知 hh.ru 403 之可能。
  • fix(api): 當 POST /api/jds slug 被去除不安全字元時發警告(FIX-M2) — 去除危險字元的 slug 正規化現在回傳 warning 欄位;純大小寫 / 空白清理保持安靜。淨化後為空時回 400。
  • fix(ui): 路由變更時清除全域搜尋 + 按鈕 spinner(FIX-M4 + FIX-L1) — 全域搜尋輸入於 hashchange 時清除(對 active 輸入有 guard)。新 UI.withSpinner(button, fn) helper 將載入狀態、ARIA、雙擊防護接線進每個 async 按鈕點擊。已採用於 Doctor / Verify / sync-check / Save CV / Normalize / Dedup / Merge 按鈕。
  • fix(ui): 讓 sidebar 可捲動,使 18 個 nav 條目永遠抵達 footer — FIX-C8 之分組 sidebar 在較短 viewport 溢出;底部條目(Activity / Health)被剪掉。.sidebar 現有 overflow-y: auto 與薄客製樣式 scrollbar(WebKit + Firefox)。Footer 透過既有 margin-top: auto 固定。
  • fix(ui): 空 modal-title 佔位(FIX-H9) — index.html 中硬編碼英文 "Title" 字串已移除,關閉 modal 開啟期間短暫可見的競爭視窗。

🌐 i18n

  • 173+ 個翻譯 key × 8 個支援 locale(en、es、pt-BR、ko、ja、ru、zh-CN、zh-TW)。為 404 頁面、activity 日誌、deep research、PDF 流程、安全警告、tracker 突變、apply 改名加入新 key。覆蓋率現由 tests/i18n-coverage.test.mjs 強制 — 每個 key 必須於每個支援 locale 有非空值,否則 CI 失敗。

⚙️ DevOps

  • 測試數: 73 → 201(+128,跨 23 個測試檔案)。剩餘單一失敗測試(runEnScan: dry-run end-to-end across multiple sources)為 Greenhouse/Ashby/Lever live API response 之既存 flake。
  • Comprehensive Playwright e2e(tests/e2e-comprehensive.mjs,23 步驟):走訪完整使用者旅程 — CV 儲存 → preview → PDF 產生 → 全部 7 個新 mode → tracker 篩選 → activity 日誌 → 404 → modal ESC → sidebar 捲動 → Ctrl-K focus → search 清除 → profile 別名 → 語言持久化。
  • GitHub Actions(.github/workflows/):
    • ci.yml — Node 18/20/22 矩陣上之單元 + 整合測試,加上 i18n 覆蓋率閘(每個 key × 8 locale 必須非空),加上每個 PR 上的完整 Playwright e2e。
    • ai-review.yml — 每個 PR 上之 Claude Code AI review。維護者保留 merge 權威;Claude 只建議。透過 skip-ai-review 標籤跳過。
    • release.yml — 當推送 v*.*.* tag 時自動發布 GitHub Release;release notes 從 CHANGELOG.md 切片,讓 8 語言變體保持規範來源。
  • CSP 友善 UI: index.html 與 router.js 中所有行內 onclick 處理器已移除。嚴格 script-src 'self' 政策現在可強制執行,且不破壞任何功能。

📦 新 REST 端點

方法 路徑 目的
GET /api/activity 列出使用者動作事件,最新優先
GET /api/interview-prep 列出儲存之 Deep Research 檔案
GET /api/interview-prep/:name 讀取單一 Deep Research 檔案
DELETE /api/interview-prep/:name 移除 Deep Research 檔案
GET /api/output/pdfs 列出產生的 PDF
GET /api/output/pdfs/:name 以附件方式串流 PDF
POST /api/tracker 對 applications.md 附加列
DELETE /api/jds/:name 移除儲存之 JD
POST /api/evaluate/test-gemini Smoke 測試 Gemini API key
POST /api/mode/:slug 7 個新 mode 之通用 prompt builder(project / training / followup / batch / contacto / interview-prep / patterns)

[1.6.0] — 2026-05-02

Web UI 首次公開發布。基準功能盤點請參閱 README.md。


career-ops-ui v1.267.1 · page 9/9 · mirror of the project changelog for cvstart.org