Skip to content

feat(docker): add compose runtime and bot controls - #314

Open
YCNeo wants to merge 2 commits into
bouob:mainfrom
YCNeo:feature/docker-compose-runtime
Open

feat(docker): add compose runtime and bot controls#314
YCNeo wants to merge 2 commits into
bouob:mainfrom
YCNeo:feature/docker-compose-runtime

Conversation

@YCNeo

@YCNeo YCNeo commented May 13, 2026

Copy link
Copy Markdown

變更摘要

新增 Docker Compose 執行環境,讓使用者可以透過容器啟動設定 Web UI 並執行搶票流程。

主要內容:

  • 新增 Dockerfilecompose.yaml.dockerignore 與 Docker entrypoint
  • settings.json 保存到 local data/settings.json
  • 將 Chrome for Testing / webdriver 保存到 local data/webdriver
  • Docker 環境下處理 Xvfb、Chrome sandbox 與 Chrome 執行權限
  • / 自動導向 /settings.html
  • settings.json 更新時在 logs 顯示
  • Web UI 按下「搶票」後切換為「結束搶票」
  • 「結束搶票」只停止 bot process,不關閉 Web UI 或瀏覽器 tab
  • 避免 settings.js / help-content.js 快取造成 UI 狀態不同步

變更類型

  • ✨ 新功能 (feat)
  • 🐛 Bug 修復 (fix)
  • ♻️ 重構 (refactor)
  • 📝 文件更新 (docs)
  • 🔧 維護 (chore)

影響平台

台灣

  • TixCraft / TicketMaster / Teamear / Indievox
  • KKTIX
  • iBon / 年代售票
  • TicketPlus
  • FamiTicket
  • 寬宏售票(KHAM)
  • FANSI GO
  • FunOne

海外

  • Cityline 買飛
  • HKTicketing 快達票

通用

  • 跨平台共用功能(nodriver_common / util / settings)

檢查清單

  • 已測試功能正常
  • 無敏感資訊(密碼、Cookie、API key)
  • .py 檔案中沒有使用 emoji

相關 Issue

Closes #313

@YCNeo
YCNeo requested a review from bouob as a code owner May 13, 2026 04:47
@bouob

bouob commented May 14, 2026

Copy link
Copy Markdown
Owner

PR Review 報告

PR #314 — feat(docker): add compose runtime and bot controls
作者:@YCNeo | 審查日期:2026-05-14


總結評語

這個 PR 為 Tickets Hunter 帶來了有實質價值的 Docker Compose 支援,架構規劃清晰(data/ 資料夾掛載、Xvfb 管理、entrypoint 設計),同時也為 Web UI 補上了「結束搶票」功能,對 Linux 使用者與 CI/CD 部署場景有明確幫助。整體方向正確,程式碼品質良好。

不過,我在審查中發現兩個需要在合併前修正的問題,以及三個需要作者說明設計意圖的疑問。詳見下方各節。


功能價值

Docker 支援對以下族群有明確好處:

  • Linux 伺服器使用者:不再需要手動設定 Xvfb 與 Chrome 依賴
  • 可重現環境data/ 掛載讓設定與 webdriver 獨立於容器之外,容器可安全重建
  • headless 部署compose.yamlbot profile 讓排程執行搶票成為可能

/ 自動導向 /settings.html 以及設定變更時 log 顯示,是體驗上的細節加分。


安全性檢視

--no-sandboxsrc/nodriver_common.py 第 931–934 行)

TICKETS_HUNTER_DOCKER == "1" 時自動加入 --no-sandbox

這符合現有的安全規則精神。security.md 第 55 行原文:「--no-sandbox--disable-web-security 僅限受控環境」。Docker 容器內以 uid 1000 非 root 使用者執行、且 compose.yaml 未加 --cap-add SYS_ADMIN,因此在 Linux 容器內 Chrome sandbox 本身無法運作,--no-sandbox 是必要的技術妥協。

需要請作者補充說明的一點:Chrome 帶 --no-sandbox 啟動時,部分 Cloudflare 規則可能額外計分,Docker 模式下 Turnstile 通過率可能低於桌面模式。建議在 README 的 Docker 章節加一行說明此 trade-off,讓使用者有預期。

docker/entrypoint.sh Shell Injection 風險

entrypoint.sh 使用 set -eu,環境變數皆加引號("$DATA_DIR" 等),Python inline heredoc 使用單引號 'PY' 防止展開,"$@" 直接傳遞命令,整體安全。無 injection 風險。

Xvfb 設定

:99 以非 root 用戶啟動 Xvfb,未開放網路(-nolisten tcp),lock file 處理有保護邏輯,設計合理。


架構影響

問題一(需合併前修正):新版 launch_maxbot 破壞 PyInstaller EXE 模式

這是本 PR 最關鍵的問題。

原始 util.launch_maxbot()src/util.py 第 2092–2116 行)有明確的 frozen 分支:

if hasattr(sys, 'frozen'):
    # PyInstaller 模式:呼叫 .exe 二進位
    cmd = script_name + '.exe ' + ' '.join(cmd_argument)
    subprocess.Popen(cmd, shell=True, cwd=working_dir)
else:
    # 原始碼模式:呼叫 Python 解譯器
    cmd_array = [interpreter_binary, script_name + '.py'] + cmd_argument

新版 launch_maxbot()src/settings.py 第 358–378 行)無條件使用

cmd_array = [sys.executable, script_name + ".py", "--input", CONST_MAXBOT_CONFIG_FILE]

在 PyInstaller frozen 環境下,sys.executable 是 EXE 本身,且 nodriver_tixcraft.py 原始檔不存在。這會導致 Windows EXE 啟動時遞迴呼叫自身或直接報錯,破壞現有 Windows 桌面使用者的執行路徑

CLAUDE.md 的核心約束明確指出:「建置目標為 Windows 上的 PyInstaller EXE」。

建議解法(二選一):

  1. 在新版 launch_maxbot 中加回 frozen 分支,或
  2. 呼叫 util.launch_maxbot() 並將回傳的 Popen 物件存到 MAXBOT_PROCESS(需 util.launch_maxbot 回傳 Popen)

問題二(需合併前修正):Windows 下 stop_maxbot 只殺父行程

src/settings.pystop_maxbot() 函式在跨平台行為上不對稱:

  • Linux/macOSstart_new_session=True + os.killpg(os.getpgid(pid), signal.SIGTERM) — 正確,整個 process group 都會收到信號
  • WindowsMAXBOT_PROCESS.terminate() — 只殺 Python 父行程,Chrome browser、renderer、crash handler 等子行程會變成孤兒行程繼續佔用記憶體

建議在 Windows 路徑改用:

subprocess.run(["taskkill", "/F", "/T", "/PID", str(MAXBOT_PROCESS.pid)],
               capture_output=True)

或在 subprocess.Popen 時加 creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,停止時用 os.kill(pid, signal.CTRL_BREAK_EVENT)

nodriver_common.py 修改(+4 行)

合理且必要,符合 code-boundaries.mdget_extension_config 的定位(瀏覽器初始化屬共用基礎設施),依賴方向無違規。

settings.py 修改(+77/-6)

新增函式 is_maxbot_running()stop_maxbot()StopHandler,以及 launch_maxbot 的重構。依賴方向符合 settings → util 規則,無循環依賴。


建議改進

疑問三(請作者確認):exit_btn 在所有環境下永久隱藏

src/www/settings.jsupdate_run_button_state() 函式兩個分支都執行:

$("#exit_btn").addClass("disappear");

src/www/settings.html 第 1155 行 HTML 也預設帶有 disappear class:

<button class="btn btn-danger disappear" id="exit_btn">結束搶票</button>

這導致非 Docker 桌面使用者完全失去從 Web UI 關閉 Web Server 的入口。請問:

  • 這是刻意設計(Docker 環境不需要「結束」Web Server,桌面使用者應從終端機 Ctrl+C)?
  • 還是 update_run_button_state(false) 的分支應該改為 $("#exit_btn").removeClass("disappear") 以便桌面使用者在搶票停止後仍可關閉服務?

設定遷移路徑

docker/entrypoint.sh 第 9–18 行:當 /data/settings.json 不存在時,建立預設值。但既有的 Windows 桌面使用者若要切換到 Docker 模式,原本 src/settings.json 的內容(帳號、設定偏好)不會自動帶入。

考慮到 .dockerignore 已排除 src/settings.json,容器內不會有舊設定,使用者需要手動複製。建議在 README 的 Docker 章節補充一行遷移說明:

已有設定的使用者請先將 src/settings.json 複製到 data/settings.json,再執行 docker compose up

並發安全(低優先,可後續處理)

MAXBOT_PROCESS 是模組層級全域變數,RunHandlerStopHandler 若同時被觸發(雖然 Tornado 為單執行緒 event loop,但仍有 is_maxbot_running() 的讀寫時間窗),建議後續加 threading.Lock 保護,避免競爭。

data/ 目錄是否需要加入 .claude/ 過濾清單

若本專案使用雙庫工作流程,SENSITIVE_PATTERNS 應確認 data/ 目錄已在 publicpr.md 過濾清單中(data/settings.json 含使用者帳號密碼)。.gitignore 已正確排除,但 publicpr 的過濾需另行確認。


問題討論

想請問 @YCNeo 幾個維護面的問題:

  1. Docker 環境問題的維護分工:Docker 相關的 Issue(如 Xvfb 啟動失敗、容器內 Chrome 版本不相容)未來由誰負責回應?這對於 PR 合併後的維護期望很重要。

  2. 目標使用族群:這個 Docker 支援主要針對哪類使用者?Linux 伺服器部署、NAS 環境、還是也希望 Windows 使用者改用 Docker 模式?這會影響未來文件的撰寫方向。

  3. bot profile 的使用情境compose.yamlbot service 直接執行 nodriver_tixcraft.py --input settings.json,但沒有 port 映射。預期使用者先用 tickets-hunter service 設定好後,再另開終端機執行 bot profile,還是有其他流程?


結語

感謝 @YCNeo 完整的貢獻!Docker 支援是社群長期以來的需求,這個 PR 的整體設計思路(data volume 隔離、entrypoint 管理 Xvfb 生命週期、Web UI 狀態同步)都很扎實。修正上述兩個 blocker 問題後,這個 PR 會是一個很好的功能擴充。期待後續版本!

@bouob

bouob commented May 29, 2026

Copy link
Copy Markdown
Owner

@codex PR Review以及評估Docker新功能支援

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add Docker Compose runtime support

2 participants