From eaf029a16f8aefa373254dfd35b09ccc5a113dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Sun, 28 Jun 2026 18:08:43 +0800 Subject: [PATCH] fix2 --- .dockerignore | 12 +- .gitignore | 4 +- Makefile | 303 +++++++++---------- backend/etc/gateway.dev.example.yaml | 31 +- deploy/.env.dev | 29 ++ deploy/.env.example | 29 ++ deploy/Dockerfile.backend | 25 ++ deploy/Dockerfile.node-worker | 13 + deploy/Dockerfile.web | 14 + deploy/README.md | 61 ++++ deploy/docker-compose.dev.yml | 34 +++ deploy/docker-compose.yml | 138 +++++++++ infra/nginx/haixun.conf => deploy/nginx.conf | 10 +- infra/.env.example | 20 -- infra/README.md | 103 ------- infra/docker-compose.yml | 42 --- infra/etc/haixun.env.example | 23 -- infra/systemd/haixun-gateway.service | 25 -- infra/systemd/haixun-node-worker.service | 23 -- infra/systemd/haixun-worker.service | 23 -- start-dev.sh | 94 ------ start-prod.sh | 59 ---- status-dev.sh | 45 --- status-prod.sh | 20 -- stop-dev.sh | 48 --- stop-prod.sh | 6 - 26 files changed, 518 insertions(+), 716 deletions(-) create mode 100644 deploy/.env.dev create mode 100644 deploy/.env.example create mode 100644 deploy/Dockerfile.backend create mode 100644 deploy/Dockerfile.node-worker create mode 100644 deploy/Dockerfile.web create mode 100644 deploy/README.md create mode 100644 deploy/docker-compose.dev.yml create mode 100644 deploy/docker-compose.yml rename infra/nginx/haixun.conf => deploy/nginx.conf (79%) delete mode 100644 infra/.env.example delete mode 100644 infra/README.md delete mode 100644 infra/docker-compose.yml delete mode 100644 infra/etc/haixun.env.example delete mode 100644 infra/systemd/haixun-gateway.service delete mode 100644 infra/systemd/haixun-node-worker.service delete mode 100644 infra/systemd/haixun-worker.service delete mode 100755 start-dev.sh delete mode 100755 start-prod.sh delete mode 100755 status-dev.sh delete mode 100755 status-prod.sh delete mode 100755 stop-dev.sh delete mode 100755 stop-prod.sh diff --git a/.dockerignore b/.dockerignore index c0864c4..fe512c9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,11 @@ -.run .git -web/node_modules -# web/dist 保留給 deploy/Dockerfile.web.static(本機 make web-build 後 COPY) -worker/node_modules +.run +.cursor +**/node_modules +backend/bin +frontend/dist **/*_test.go **/.DS_Store +infra *.md -!deploy/** \ No newline at end of file +!deploy/** diff --git a/.gitignore b/.gitignore index cdf65d9..2eefe95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # --- secrets / env(不要 commit 實值)--- -infra/.env -infra/etc/haixun.env -/opt/haixun/ +deploy/.env # --- build 產物 --- backend/bin/ diff --git a/Makefile b/Makefile index c81678a..ae3fdee 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,32 @@ # 巡樓 monorepo Makefile # 兩種模式: -# dev - 本機開發(docker 起 Mongo/Redis,go run / vite dev) -# prod - 建置產物(前端 dist + linux Go binary),並可部署成 systemd 服務 + nginx +# dev - 本機開發(docker 起 Mongo/Redis,go run / vite dev 在本機跑) +# docker - 全棧一鍵啟動(Mongo/Redis/gateway/worker/node-worker/web 全在 docker) # # 常用: -# make dev-infra # 起本機 Mongo/Redis -# make dev-backend # 跑 gateway (:8890) -# make dev-frontend # 跑前端 (:5173,proxy 到 :8890) -# make build # 產出 frontend/dist + backend/bin/{gateway,worker} -# sudo make install # 部署到目標主機(見 infra/README.md) +# make up # 一鍵:build + 啟動整套(含前後端、worker、DB) +# make down # 停掉整套 +# make logs # 追全部日誌 +# make dev-infra # 本機開發:只起 Mongo/Redis +# make dev-backend # 本機開發:go run gateway (:8890) +# make dev-frontend # 本機開發:vite dev (:5173) SHELL := /bin/bash # --- 路徑 --- BACKEND_DIR := backend FRONTEND_DIR := frontend -INFRA_DIR := infra -BIN_DIR := $(BACKEND_DIR)/bin -# --- 部署目標(install 用)--- -DEPLOY_ROOT := /opt/haixun -WEB_ROOT := /var/www/haixun +# --- docker compose(prod 全棧)--- +COMPOSE_FILE := deploy/docker-compose.yml +ENV_FILE := deploy/.env +COMPOSE := docker compose -f $(COMPOSE_FILE) --env-file $(ENV_FILE) -# --- 交叉編譯目標(在 mac 上 build 給 linux 主機)--- -GOOS ?= linux -GOARCH ?= amd64 - -# --- docker compose --- -COMPOSE := docker compose -f $(INFRA_DIR)/docker-compose.yml --env-file $(INFRA_DIR)/.env +# --- docker compose(本地測試;base + dev override + dev env)--- +DEV_ENV_FILE := deploy/.env.dev +DEV_COMPOSE := docker compose -f $(COMPOSE_FILE) -f deploy/docker-compose.dev.yml --env-file $(DEV_ENV_FILE) +# go worker 用 gateway 內嵌,dev 不啟動獨立 worker 容器 +DEV_SERVICES := mongo redis init gateway node-worker web # --- dev 用 worker secret(對應 etc/gateway.yaml 的 InternalWorker.Secret)--- DEV_WORKER_SECRET := haixun-dev-worker-secret @@ -40,172 +39,140 @@ help: ## 顯示可用指令 @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' +$(ENV_FILE): + @test -f $(ENV_FILE) || (cp deploy/.env.example $(ENV_FILE) && echo "已從 deploy/.env.example 建立 $(ENV_FILE),請填入密碼與 secret 後再 make up") + # ============================================================ -# DEV +# DOCKER(全棧一鍵) # ============================================================ -$(INFRA_DIR)/.env: - @test -f $(INFRA_DIR)/.env || (cp $(INFRA_DIR)/.env.example $(INFRA_DIR)/.env && echo "已從 .env.example 建立 $(INFRA_DIR)/.env,請視需要修改密碼") +.PHONY: up +up: $(ENV_FILE) ## [docker] 一鍵啟動整套(build + up,背景) + $(COMPOSE) up -d --build + @echo "" + @echo "=== 啟動中 ===" + @echo " 前端: http://localhost:$$(grep -E '^WEB_PORT=' $(ENV_FILE) | cut -d= -f2)" + @echo " 健康檢查: curl http://localhost:$$(grep -E '^WEB_PORT=' $(ENV_FILE) | cut -d= -f2)/api/v1/health" + @echo " 看狀態: make ps 看日誌: make logs" -.PHONY: dev-infra -dev-infra: $(INFRA_DIR)/.env ## [dev] 起本機 Mongo + Redis (docker) - $(COMPOSE) up -d - $(COMPOSE) ps - -.PHONY: dev-infra-down -dev-infra-down: ## [dev] 停掉本機 Mongo + Redis +.PHONY: down +down: ## [docker] 停掉整套(保留資料 volume) $(COMPOSE) down -.PHONY: dev-backend -dev-backend: ## [dev] 跑 gateway API (:8890) +.PHONY: down-clean +down-clean: ## [docker] 停掉整套並刪除資料 volume(清空 Mongo/Redis) + $(COMPOSE) down -v + +.PHONY: build +build: $(ENV_FILE) ## [docker] 只 build image,不啟動 + $(COMPOSE) build + +.PHONY: rebuild +rebuild: $(ENV_FILE) ## [docker] 無快取重新 build 並重啟 + $(COMPOSE) build --no-cache + $(COMPOSE) up -d + +.PHONY: restart +restart: ## [docker] 重啟所有服務 + $(COMPOSE) restart + +.PHONY: ps +ps: ## [docker] 看服務狀態 + $(COMPOSE) ps + +.PHONY: logs +logs: ## [docker] 追全部日誌(Ctrl-C 離開) + $(COMPOSE) logs -f + +.PHONY: logs-gateway +logs-gateway: ## [docker] 只看 gateway 日誌 + $(COMPOSE) logs -f gateway + +.PHONY: init +init: $(ENV_FILE) ## [docker] 重跑一次 DB 初始化(索引/權限/admin,冪等) + $(COMPOSE) run --rm init + +.PHONY: config +config: $(ENV_FILE) ## [docker] 驗證 compose 設定 + $(COMPOSE) config >/dev/null && echo "docker compose config OK" + +# ============================================================ +# DEV(本地測試,全走 docker) +# ============================================================ + +.PHONY: dev-up +dev-up: ## [dev] 一鍵:本地 docker 測試(gateway 開 :8890,web 開 :8080,go worker 內嵌) + $(DEV_COMPOSE) up -d --build $(DEV_SERVICES) + @echo "" + @echo "=== 本地測試啟動中 ===" + @echo " 前端: http://localhost:$$(grep -E '^WEB_PORT=' $(DEV_ENV_FILE) | cut -d= -f2)" + @echo " API: http://localhost:8890/api/v1/health" + @echo " DevHeaderFallback 已開(可用 X-Tenant-ID / X-UID 模擬登入)" + +.PHONY: dev-down +dev-down: ## [dev] 停掉本地 docker(保留資料) + $(DEV_COMPOSE) down + +.PHONY: dev-down-clean +dev-down-clean: ## [dev] 停掉本地 docker 並清空資料 volume + $(DEV_COMPOSE) down -v + +.PHONY: dev-rebuild +dev-rebuild: ## [dev] 改 code 後重新 build 並重啟本地 docker + $(DEV_COMPOSE) up -d --build $(DEV_SERVICES) + +.PHONY: dev-restart +dev-restart: ## [dev] 只重啟(改了 etc/*.yaml 後不用 rebuild,掛載即生效) + $(DEV_COMPOSE) restart gateway node-worker + +.PHONY: dev-ps +dev-ps: ## [dev] 看本地 docker 狀態 + $(DEV_COMPOSE) ps + +.PHONY: dev-logs +dev-logs: ## [dev] 追本地 docker 日誌 + $(DEV_COMPOSE) logs -f + +.PHONY: dev-init +dev-init: ## [dev] 重跑一次 DB 初始化(索引/權限/admin,冪等) + $(DEV_COMPOSE) run --rm init + +.PHONY: dev +dev: ## [dev] 本地 docker 測試說明 + @echo "本地測試(全 docker):" + @echo " make dev-up # 一鍵起整套(前端 :8080、API :8890)" + @echo " make dev-logs # 看日誌" + @echo " make dev-restart # 改 etc/*.yaml 後重啟(免 rebuild)" + @echo " make dev-rebuild # 改 Go/前端 code 後重 build" + @echo " make dev-down # 停(保留資料)" + +# --- 進階:不進 docker,直接本機 go run / vite(需自備 Go/Node)--- +.PHONY: local-infra +local-infra: ## [local] 只起 Mongo + Redis(docker),其餘本機跑 + $(DEV_COMPOSE) up -d mongo redis + $(DEV_COMPOSE) ps + +.PHONY: local-backend +local-backend: ## [local] 本機 go run gateway (:8890) cd $(BACKEND_DIR) && go run . -f etc/gateway.yaml -.PHONY: dev-worker -dev-worker: ## [dev] 跑 Go job worker (:8891) +.PHONY: local-worker +local-worker: ## [local] 本機 go run Go job worker (:8891) cd $(BACKEND_DIR) && go run ./cmd/worker -f etc/gateway.worker.yaml -.PHONY: dev-node-worker -dev-node-worker: ## [dev] 跑 Node playwright worker (style-8d) +.PHONY: local-node-worker +local-node-worker: ## [local] 本機跑 Node playwright worker (style-8d) cd $(BACKEND_DIR)/worker && npm install && \ HAIXUN_WORKER_SECRET=$(DEV_WORKER_SECRET) HAIXUN_BACKEND_URL=$(DEV_BACKEND_URL) npm run style-8d -.PHONY: dev-frontend -dev-frontend: ## [dev] 跑前端 dev server (:5173) +.PHONY: local-frontend +local-frontend: ## [local] 本機跑前端 dev server (:5173) cd $(FRONTEND_DIR) && npm install && npm run dev -.PHONY: dev-init -dev-init: ## [dev] 初始化 DB(索引/權限)並建立 admin 帳號(可用 INIT_ADMIN_EMAIL / INIT_ADMIN_PASSWORD 覆寫) +.PHONY: local-init +local-init: ## [local] 本機初始化 DB(索引/權限)並建立 admin cd $(BACKEND_DIR) && go run ./cmd/tool init -f etc/gateway.yaml -.PHONY: dev -dev: ## [dev] 顯示本機開發要開的終端 - @echo "本機開發請分開幾個終端執行:" - @echo " 1) make dev-infra # Mongo/Redis" - @echo " 2) make dev-backend # gateway :8890" - @echo " 3) make dev-worker # go worker(可選)" - @echo " 4) make dev-node-worker # node worker(需 style-8d 時)" - @echo " 5) make dev-frontend # 前端 :5173" - -# ============================================================ -# 安裝依賴(新機器 clone 後執行一次) -# ============================================================ - -.PHONY: bootstrap-sys -bootstrap-sys: ## [需 sudo] 安裝系統依賴(Go, Node.js, Docker, Nginx)— Ubuntu 專用 - @echo "=== 安裝系統套件: nginx, curl, gnupg ===" - sudo apt-get update -qq - sudo apt-get install -y -qq nginx curl gnupg ca-certificates - @echo "" - @echo "=== 安裝 Go 1.22+ ===" - @GO_VER=$$(curl -sL https://go.dev/VERSION?m=text 2>/dev/null | head -1 || echo "go1.22"); \ - echo "下載 $$GO_VER.linux-amd64.tar.gz"; \ - curl -sL "https://go.dev/dl/$$GO_VER.linux-amd64.tar.gz" -o /tmp/go.tar.gz && \ - sudo rm -rf /usr/local/go && \ - sudo tar -C /usr/local -xzf /tmp/go.tar.gz && \ - rm /tmp/go.tar.gz; \ - echo 'export PATH=/usr/local/go/bin:$$PATH' | sudo tee /etc/profile.d/go.sh - @echo "" - @echo "=== 安裝 Node.js LTS ===" - curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - - sudo apt-get install -y -qq nodejs - @echo "" - @echo "=== 安裝 Docker Engine + Compose ===" - curl -fsSL https://get.docker.com | sudo sh - sudo usermod -aG docker $(USER) 2>/dev/null || true - @echo "" - @echo "=== 安裝 Nginx ===" - sudo apt-get install -y -qq nginx - @echo "" - @echo "✓ 系統依賴安裝完成。" - @echo " 登出再登入後,Go/Node/Docker 即可使用(group 變更生效)。" - @echo " 接著執行 make bootstrap 安裝專案層依賴。" - @echo "" - -.PHONY: bootstrap -bootstrap: ## 安裝專案層依賴(Go modules + npm + Playwright),需先執行 make bootstrap-sys - @echo "=== [1/4] Go modules ===" - cd $(BACKEND_DIR) && go mod tidy - @echo "" - @echo "=== [2/4] 前端 npm ===" - cd $(FRONTEND_DIR) && npm install - @echo "" - @echo "=== [3/4] Node worker npm ===" - cd $(BACKEND_DIR)/worker && npm install - @echo "" - @echo "=== [4/4] Playwright 瀏覽器(chromium)+ 系統依賴 ===" - cd $(BACKEND_DIR)/worker && sudo npx playwright install --with-deps chromium - @echo "" - @echo "✓ 全裝完成。後續步驟:" - @echo " 1) make dev-infra # 起 Mongo/Redis (Docker)" - @echo " 2) make dev-init # 初始化 DB + 建立 admin" - @echo " 3) make dev-backend # 啟動 gateway (:8890)" - @echo " 4) sudo make install # 正式部署(含 systemd + nginx)" - -# ============================================================ -# BUILD (prod 產物) -# ============================================================ - -.PHONY: build -build: build-frontend build-backend ## [prod] 建置前端 dist 與 Go binary - -.PHONY: build-frontend -build-frontend: ## [prod] 前端靜態建置 (tsc + vite) -> frontend/dist - cd $(FRONTEND_DIR) && npm ci && npm run build - -.PHONY: build-backend -build-backend: ## [prod] 交叉編譯 gateway + worker -> backend/bin (linux) - cd $(BACKEND_DIR) && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -trimpath -ldflags "-s -w" -o bin/gateway . - cd $(BACKEND_DIR) && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -trimpath -ldflags "-s -w" -o bin/worker ./cmd/worker - cd $(BACKEND_DIR) && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -trimpath -ldflags "-s -w" -o bin/tool ./cmd/tool - @echo "binary 已輸出到 $(BIN_DIR)/(gateway / worker / tool)" - -# ============================================================ -# PROD (部署) -# ============================================================ - -.PHONY: prod-infra -prod-infra: $(INFRA_DIR)/.env ## [prod] 起 Mongo + Redis (docker,背景) - $(COMPOSE) up -d - -.PHONY: prod-infra-down -prod-infra-down: ## [prod] 停掉 Mongo + Redis - $(COMPOSE) down - -.PHONY: prod-init -prod-init: ## [prod] 目標主機初始化 DB + 建立 admin(讀 /opt/haixun/etc/haixun.env;可加 INIT_ADMIN_EMAIL/PASSWORD) - @test -x $(DEPLOY_ROOT)/bin/tool || (echo "缺少 $(DEPLOY_ROOT)/bin/tool,請先 make install" && exit 1) - set -a; . $(DEPLOY_ROOT)/etc/haixun.env; set +a; \ - $(DEPLOY_ROOT)/bin/tool init -f $(DEPLOY_ROOT)/etc/gateway.prod.yaml - -.PHONY: install -install: ## [prod] 安裝 binary/前端/設定/systemd/nginx(需 root,在目標主機執行) - @test -f $(BIN_DIR)/gateway || (echo "缺少 $(BIN_DIR)/gateway,請先在能 build 的機器執行 make build" && exit 1) - @test -d $(FRONTEND_DIR)/dist || (echo "缺少 $(FRONTEND_DIR)/dist,請先 make build-frontend" && exit 1) - id haixun >/dev/null 2>&1 || useradd --system --home $(DEPLOY_ROOT) --shell /usr/sbin/nologin haixun - install -d $(DEPLOY_ROOT)/bin $(DEPLOY_ROOT)/etc $(DEPLOY_ROOT)/node-worker $(WEB_ROOT) - install -m 0755 $(BIN_DIR)/gateway $(DEPLOY_ROOT)/bin/gateway - install -m 0755 $(BIN_DIR)/worker $(DEPLOY_ROOT)/bin/worker - install -m 0755 $(BIN_DIR)/tool $(DEPLOY_ROOT)/bin/tool - install -m 0644 $(BACKEND_DIR)/etc/gateway.prod.yaml $(DEPLOY_ROOT)/etc/gateway.prod.yaml - install -m 0644 $(BACKEND_DIR)/etc/gateway.worker.prod.yaml $(DEPLOY_ROOT)/etc/gateway.worker.prod.yaml - rm -rf $(WEB_ROOT)/* && cp -r $(FRONTEND_DIR)/dist/* $(WEB_ROOT)/ - cp -r $(BACKEND_DIR)/worker/* $(DEPLOY_ROOT)/node-worker/ - cd $(DEPLOY_ROOT)/node-worker && npm ci && npx playwright install --with-deps chromium - install -m 0644 $(INFRA_DIR)/systemd/haixun-gateway.service /etc/systemd/system/haixun-gateway.service - install -m 0644 $(INFRA_DIR)/systemd/haixun-worker.service /etc/systemd/system/haixun-worker.service - install -m 0644 $(INFRA_DIR)/systemd/haixun-node-worker.service /etc/systemd/system/haixun-node-worker.service - install -m 0644 $(INFRA_DIR)/nginx/haixun.conf /etc/nginx/conf.d/haixun.conf - chown -R haixun:haixun $(DEPLOY_ROOT) $(WEB_ROOT) - @echo "----" - @echo "接著(只做一次)建立 secret 檔:" - @echo " cp $(INFRA_DIR)/etc/haixun.env.example $(DEPLOY_ROOT)/etc/haixun.env && chmod 600 $(DEPLOY_ROOT)/etc/haixun.env && sudoedit $(DEPLOY_ROOT)/etc/haixun.env" - @echo "再啟用服務:" - @echo " systemctl daemon-reload && systemctl enable --now haixun-gateway haixun-worker haixun-node-worker" - @echo " nginx -t && systemctl reload nginx" - # ============================================================ # 驗證 / 維護 # ============================================================ @@ -227,7 +194,7 @@ test: ## 跑後端測試 cd $(BACKEND_DIR) && go test ./... .PHONY: verify -verify: ## 後端 build/test + 前端 build + compose 語法 +verify: $(ENV_FILE) ## 後端 build/test + 前端 build + compose 語法 cd $(BACKEND_DIR) && go build ./... && go test ./... cd $(FRONTEND_DIR) && npm ci && npm run build $(COMPOSE) config >/dev/null && echo "docker compose config OK" diff --git a/backend/etc/gateway.dev.example.yaml b/backend/etc/gateway.dev.example.yaml index d3584e7..0b423bf 100644 --- a/backend/etc/gateway.dev.example.yaml +++ b/backend/etc/gateway.dev.example.yaml @@ -3,13 +3,17 @@ Host: 0.0.0.0 Port: 8890 Timeout: 120000 +# 本地「docker 測試」設定(make dev-up 使用)。 +# 連線走 compose service name(mongo / redis),密碼對齊 deploy/.env.dev。 +# 純本機 go run(不進 docker)請改用 etc/gateway.yaml(連 127.0.0.1)。 Mongo: - URI: mongodb://127.0.0.1:27017 - Database: haixun_dev + URI: mongodb://haixun:haixun-dev-pass@mongo:27017/?authSource=admin + Database: haixun TimeoutSeconds: 10 Redis: - Addr: 127.0.0.1:6379 + Addr: redis:6379 + Password: haixun-dev-redis DB: 0 Auth: @@ -17,4 +21,25 @@ Auth: RefreshSecret: haixun-dev-refresh-secret-change-me AccessExpireSeconds: 900 RefreshExpireSeconds: 2592000 + # 僅本地開發開啟:允許用 X-Tenant-ID / X-UID header 模擬登入。正式環境必須 false。 DevHeaderFallback: true + +Secrets: + # 留空 = 機敏資料不加密(本地測試方便)。正式環境用 ${HAIXUN_SECRETS_KEY}。 + EncryptionKey: "" + +InternalWorker: + Secret: haixun-dev-worker-secret + +# dev 用 gateway 內嵌 go worker(勿另開 worker 容器,避免兩個 worker 搶 job)。 +JobWorker: + Enabled: true + WorkerType: go + +JobScheduler: + Enabled: true + IntervalSeconds: 60 + +JobReaper: + Enabled: true + IntervalSeconds: 30 diff --git a/deploy/.env.dev b/deploy/.env.dev new file mode 100644 index 0000000..2f0b790 --- /dev/null +++ b/deploy/.env.dev @@ -0,0 +1,29 @@ +# 巡樓全棧 docker compose 環境變數範本。 +# 複製成 deploy/.env 後填入實際值;deploy/.env 不要 commit。 +# cp deploy/.env.example deploy/.env + +# === Web(對外) === +WEB_PORT=80 + +# === Mongo === +MONGO_PORT=27017 +MONGO_ROOT_USER=haixun +# 建議只用英數字。若密碼含 @ : / $ 等字元,HAIXUN_MONGO_URI 會解析錯誤, +# 需自行對 compose 內的 URI 做 URL-encode。 +MONGO_ROOT_PASSWORD=Fafafa54088 +MONGO_DATABASE=haixun + +# === Redis === +REDIS_PASSWORD=Fafafa54088 + +# === JWT / Secrets === +# openssl rand -base64 48 # 三把 secret 各產一把 +# openssl rand -base64 32 # HAIXUN_SECRETS_KEY(機敏資料落地加密,需 base64 的 32 bytes) +HAIXUN_JWT_ACCESS_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_JWT_REFRESH_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_WORKER_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_SECRETS_KEY=+m0Ws4xJBrp+UzuWRyZ43/1P3sB91HmGhtW1FZrGr44= + +# === 初始 admin(init 容器建立,冪等) === +INIT_ADMIN_EMAIL=admin@30cm.net +INIT_ADMIN_PASSWORD=Fafafa54088 diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..2f0b790 --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,29 @@ +# 巡樓全棧 docker compose 環境變數範本。 +# 複製成 deploy/.env 後填入實際值;deploy/.env 不要 commit。 +# cp deploy/.env.example deploy/.env + +# === Web(對外) === +WEB_PORT=80 + +# === Mongo === +MONGO_PORT=27017 +MONGO_ROOT_USER=haixun +# 建議只用英數字。若密碼含 @ : / $ 等字元,HAIXUN_MONGO_URI 會解析錯誤, +# 需自行對 compose 內的 URI 做 URL-encode。 +MONGO_ROOT_PASSWORD=Fafafa54088 +MONGO_DATABASE=haixun + +# === Redis === +REDIS_PASSWORD=Fafafa54088 + +# === JWT / Secrets === +# openssl rand -base64 48 # 三把 secret 各產一把 +# openssl rand -base64 32 # HAIXUN_SECRETS_KEY(機敏資料落地加密,需 base64 的 32 bytes) +HAIXUN_JWT_ACCESS_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_JWT_REFRESH_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_WORKER_SECRET=tI3g0UYzmAIC+N8vODem0bG9T9X5g0EEg15d0mH/wEJjbloca7YNo8pEHAsRW1BF +HAIXUN_SECRETS_KEY=+m0Ws4xJBrp+UzuWRyZ43/1P3sB91HmGhtW1FZrGr44= + +# === 初始 admin(init 容器建立,冪等) === +INIT_ADMIN_EMAIL=admin@30cm.net +INIT_ADMIN_PASSWORD=Fafafa54088 diff --git a/deploy/Dockerfile.backend b/deploy/Dockerfile.backend new file mode 100644 index 0000000..7f791e7 --- /dev/null +++ b/deploy/Dockerfile.backend @@ -0,0 +1,25 @@ +# 後端共用 image:一次 build gateway / worker / tool 三個 binary。 +# compose 以 entrypoint/command 覆寫,決定該容器當 gateway、job worker 或一次性 init。 +# build context = repo root(見 deploy/docker-compose.yml)。 + +FROM golang:1.23-alpine AS build +WORKDIR /src +RUN apk add --no-cache git +COPY backend/go.mod backend/go.sum ./ +RUN go mod download +COPY backend/ ./ +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w" -o /out/gateway . \ + && CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w" -o /out/worker ./cmd/worker \ + && CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w" -o /out/tool ./cmd/tool + +FROM alpine:3.20 +RUN apk add --no-cache ca-certificates tzdata wget +ENV TZ=UTC +WORKDIR /app +COPY --from=build /out/ /app/ +COPY backend/etc/ /app/etc/ + +# 預設角色為 gateway;compose 會把 worker / init 用 entrypoint 覆寫。 +EXPOSE 8890 +ENTRYPOINT ["/app/gateway"] +CMD ["-f", "/app/etc/gateway.prod.yaml"] diff --git a/deploy/Dockerfile.node-worker b/deploy/Dockerfile.node-worker new file mode 100644 index 0000000..54db7aa --- /dev/null +++ b/deploy/Dockerfile.node-worker @@ -0,0 +1,13 @@ +# Node playwright worker(style-8d)。 +# 直接使用 Playwright 官方 image(自帶 chromium 與系統依賴),版本對齊 package.json 的 playwright ^1.49.1。 +# build context = repo root(見 deploy/docker-compose.yml)。 + +FROM mcr.microsoft.com/playwright:v1.49.1-jammy +WORKDIR /app +ENV TZ=UTC +COPY backend/worker/package.json backend/worker/package-lock.json ./ +RUN npm ci +COPY backend/worker/ ./ + +# HAIXUN_BACKEND_URL / HAIXUN_WORKER_SECRET 由 compose 注入。 +CMD ["npm", "run", "style-8d"] diff --git a/deploy/Dockerfile.web b/deploy/Dockerfile.web new file mode 100644 index 0000000..85bfcbb --- /dev/null +++ b/deploy/Dockerfile.web @@ -0,0 +1,14 @@ +# 前端:vite build 出靜態檔,交給 nginx serve,並把 /api 反代到 gateway 容器。 +# build context = repo root(見 deploy/docker-compose.yml)。 + +FROM node:22-alpine AS build +WORKDIR /app +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci +COPY frontend/ ./ +RUN npm run build + +FROM nginx:alpine +COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html +EXPOSE 80 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..51f8d3f --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,61 @@ +# 巡樓部署(Docker 一鍵啟動) + +整套系統用單一 `docker compose` 拉起,對外只開前端 `:80`。 + +``` +瀏覽器 → web(nginx :80) + ├─ 前端靜態檔(vite build) + └─ /api 反代 → gateway:8890 +gateway / worker(Go)─┐ +node-worker(playwright)┤→ Mongo / Redis(同網路,不對外) +``` + +| 容器 | 角色 | +|------|------| +| `mongo` / `redis` | 資料層(只綁 `127.0.0.1`,volume 持久化) | +| `init` | run-once:建索引 / 權限 / 第一個 admin(冪等,跑完即退) | +| `gateway` | API + scheduler + reaper(:8890,僅內網) | +| `worker` | Go job worker | +| `node-worker` | playwright style-8d | +| `web` | nginx,serve 前端 + 反代 `/api` | + +## 一鍵啟動 + +```bash +cp deploy/.env.example deploy/.env # 填密碼與 secret +make up # = docker compose up -d --build +``` + +啟動順序由 `depends_on` + healthcheck 保證:Mongo/Redis 健康 → `init` 跑完 → `gateway`/`worker` 起來 → `gateway` 健康 → `node-worker`/`web` 起來。 + +## 常用指令 + +```bash +make ps # 狀態 +make logs # 全部日誌 +make logs-gateway # 只看 gateway +make down # 停(保留資料) +make down-clean # 停並清空 Mongo/Redis volume +make rebuild # 無快取重建並重啟 +make init # 重跑一次 DB 初始化 +``` + +## 健康檢查 + +```bash +curl http://localhost/api/v1/health +``` + +## 產生 secret + +```bash +openssl rand -base64 48 # JWT access / refresh / worker secret +openssl rand -base64 32 # HAIXUN_SECRETS_KEY(機敏資料落地加密) +``` + +## 注意事項 + +- `deploy/.env` 內含 secret,不要 commit。 +- `MONGO_ROOT_PASSWORD` 建議只用英數字;含 `@ : / $` 等字元時,compose 內的 `HAIXUN_MONGO_URI` 需自行 URL-encode。 +- 全系統時間 UTC+0(容器 `TZ=UTC`)。 +- 本機開發(不全容器化)見根目錄 `make dev`。 diff --git a/deploy/docker-compose.dev.yml b/deploy/docker-compose.dev.yml new file mode 100644 index 0000000..9843faf --- /dev/null +++ b/deploy/docker-compose.dev.yml @@ -0,0 +1,34 @@ +# 本地 docker 測試 override(搭配 docker-compose.yml 一起用): +# docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml --env-file deploy/.env.dev up -d --build +# 或直接 `make dev-up`。 +# +# 與正式版差異: +# - 後端(gateway/init)改用 gateway.dev.example.yaml(DevHeaderFallback=true、不加密)。 +# - 掛載 backend/etc,改 yaml 後 restart 容器即可生效,不必 rebuild。 +# - gateway 對外開 :8890 方便直接打 API;web 走 .env.dev 的 WEB_PORT(預設 8080)。 +# - go job worker 用 gateway 內嵌(不啟動獨立 worker 容器)。 +name: haixun + +services: + # 本地測試不對外 publish DB port(容器間走 service name), + # 避免與其他本機 Mongo/Redis(含舊 haixun-infra)搶 127.0.0.1:27017。 + mongo: + ports: !reset [] + + init: + command: ["init", "-f", "/app/etc/gateway.dev.example.yaml"] + volumes: + - ../backend/etc:/app/etc:ro + + gateway: + command: ["-f", "/app/etc/gateway.dev.example.yaml"] + ports: + - "8890:8890" + volumes: + - ../backend/etc:/app/etc:ro + + node-worker: + environment: + HAIXUN_BACKEND_URL: "http://gateway:8890" + HAIXUN_WORKER_SECRET: "haixun-dev-worker-secret" + TZ: "UTC" diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..74ca0f4 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,138 @@ +# 巡樓全棧一鍵啟動:Mongo + Redis + gateway + go-worker + node-worker + web(nginx)。 +# 啟動:docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build +# 或直接用 repo 根目錄的 `make up`。 +name: haixun + +# ---- 後端三個容器(gateway / worker / init)共用同一 image 與環境變數 ---- +x-backend-build: &backend-build + context: .. + dockerfile: deploy/Dockerfile.backend + +x-backend-env: &backend-env + # mongo 走 service name;若密碼含特殊字元請改用 URL-encode(見 .env.example 說明)。 + HAIXUN_MONGO_URI: "mongodb://${MONGO_ROOT_USER}:${MONGO_ROOT_PASSWORD}@mongo:27017/?authSource=admin" + HAIXUN_MONGO_DB: "${MONGO_DATABASE}" + HAIXUN_REDIS_ADDR: "redis:6379" + HAIXUN_REDIS_PASSWORD: "${REDIS_PASSWORD}" + HAIXUN_JWT_ACCESS_SECRET: "${HAIXUN_JWT_ACCESS_SECRET}" + HAIXUN_JWT_REFRESH_SECRET: "${HAIXUN_JWT_REFRESH_SECRET}" + HAIXUN_SECRETS_KEY: "${HAIXUN_SECRETS_KEY}" + HAIXUN_WORKER_SECRET: "${HAIXUN_WORKER_SECRET}" + TZ: "UTC" + +services: + mongo: + image: mongo:7 + restart: unless-stopped + ports: + - "127.0.0.1:${MONGO_PORT:-27017}:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-haixun} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD is required} + MONGO_INITDB_DATABASE: ${MONGO_DATABASE:-haixun} + volumes: + - mongo_data:/data/db + healthcheck: + test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + + redis: + image: redis:7 + restart: unless-stopped + command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?REDIS_PASSWORD is required}", "--appendonly", "yes"] + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + # 一次性:建索引 / 權限 catalog / role_permissions / 第一個 admin(冪等,跑完即退出)。 + init: + build: *backend-build + image: haixun-backend:latest + restart: "no" + depends_on: + mongo: + condition: service_healthy + redis: + condition: service_healthy + environment: + <<: *backend-env + INIT_ADMIN_EMAIL: "${INIT_ADMIN_EMAIL:-admin@30cm.net}" + INIT_ADMIN_PASSWORD: "${INIT_ADMIN_PASSWORD:?INIT_ADMIN_PASSWORD is required}" + entrypoint: ["/app/tool"] + command: ["init", "-f", "/app/etc/gateway.prod.yaml"] + + gateway: + build: *backend-build + image: haixun-backend:latest + restart: unless-stopped + depends_on: + mongo: + condition: service_healthy + redis: + condition: service_healthy + init: + condition: service_completed_successfully + environment: + <<: *backend-env + expose: + - "8890" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8890/api/v1/health"] + interval: 15s + timeout: 5s + retries: 5 + start_period: 20s + + worker: + build: *backend-build + image: haixun-backend:latest + restart: unless-stopped + depends_on: + mongo: + condition: service_healthy + redis: + condition: service_healthy + init: + condition: service_completed_successfully + environment: + <<: *backend-env + entrypoint: ["/app/worker"] + command: ["-f", "/app/etc/gateway.worker.prod.yaml"] + + node-worker: + build: + context: .. + dockerfile: deploy/Dockerfile.node-worker + image: haixun-node-worker:latest + restart: unless-stopped + depends_on: + gateway: + condition: service_healthy + environment: + HAIXUN_BACKEND_URL: "http://gateway:8890" + HAIXUN_WORKER_SECRET: "${HAIXUN_WORKER_SECRET}" + TZ: "UTC" + + web: + build: + context: .. + dockerfile: deploy/Dockerfile.web + image: haixun-web:latest + restart: unless-stopped + depends_on: + gateway: + condition: service_healthy + ports: + - "${WEB_PORT:-80}:80" + +volumes: + mongo_data: + redis_data: diff --git a/infra/nginx/haixun.conf b/deploy/nginx.conf similarity index 79% rename from infra/nginx/haixun.conf rename to deploy/nginx.conf index 69db9e0..5679de9 100644 --- a/infra/nginx/haixun.conf +++ b/deploy/nginx.conf @@ -1,10 +1,8 @@ -# 巡樓 Console nginx 設定 -# 安裝:cp infra/nginx/haixun.conf /etc/nginx/conf.d/haixun.conf && nginx -t && systemctl reload nginx -# 前端靜態檔部署在 /var/www/haixun(make install 會放 frontend/dist 內容)。 -# /api 反向代理到本機 systemd 跑的 Go gateway (127.0.0.1:8890),含 SSE 串流設定。 +# 巡樓 Console nginx(容器內):serve 前端靜態檔 + /api 反代到 gateway 容器。 +# upstream 走 compose service name(gateway:8890),不是 127.0.0.1。 upstream haixun_gateway { - server 127.0.0.1:8890; + server gateway:8890; keepalive 32; } @@ -13,7 +11,7 @@ server { listen [::]:80; server_name _; - root /var/www/haixun; + root /usr/share/nginx/html; index index.html; # 安全標頭 diff --git a/infra/.env.example b/infra/.env.example deleted file mode 100644 index 4780b12..0000000 --- a/infra/.env.example +++ /dev/null @@ -1,20 +0,0 @@ -# Infra docker compose 環境變數範本 -# 複製成 infra/.env 後填入實際值;.env 不要 commit。 - -# --- Mongo --- -MONGO_PORT=27017 -MONGO_ROOT_USER=haixun -MONGO_ROOT_PASSWORD=1qaz@WSX3edc$RFV -MONGO_DATABASE=haixun - -# --- Redis --- -REDIS_PORT=6379 -REDIS_PASSWORD=change-me-redis-pass - -INIT_ADMIN_EMAIL=admin@30cm.net -PASSWORD=Fafafafa54088 - -HAIXUN_JWT_ACCESS_SECRET=1qaz@WSX3edc$RFV -HAIXUN_JWT_REFRESH_SECRET=1qaz@WSX3edc$RFV -HAIXUN_SECRETS_KEY=1qaz@WSX3edc$RFV -HAIXUN_WORKER_SECRET=1qaz@WSX3edc$RFV diff --git a/infra/README.md b/infra/README.md deleted file mode 100644 index afb57fd..0000000 --- a/infra/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# 巡樓部署 (infra) - -部署拓樸: - -``` -瀏覽器 → nginx(systemd, :80/:443) - ├─ 靜態前端 /var/www/haixun (frontend/dist) - └─ /api 反向代理 → Go gateway (systemd, 127.0.0.1:8890) -Go gateway / Go worker (systemd) → Mongo / Redis (docker compose, 綁 127.0.0.1) -Node playwright worker (systemd) → 透過 HTTP 打 gateway -``` - -- 資料服務(Mongo/Redis)用 docker compose,只綁 `127.0.0.1`。 -- Go gateway / Go worker / Node worker 都是 systemd 原生服務。 -- secret 一律放 `/opt/haixun/etc/haixun.env`(不進 repo),yaml 用 `${VAR}` 讀取。 - -## 目錄 - -``` -infra/ - docker-compose.yml # mongo + redis - .env.example # compose 用環境變數 - etc/haixun.env.example # systemd EnvironmentFile 範本(secret) - nginx/haixun.conf # 靜態前端 + /api 反代 + SSE - systemd/ - haixun-gateway.service - haixun-worker.service - haixun-node-worker.service -``` - -## 1. 起資料服務 (docker) - -```bash -cd infra -cp .env.example .env # 填入 Mongo/Redis 密碼 -docker compose --env-file .env up -d -docker compose ps -``` - -## 2. 建置產物(本機或 CI) - -```bash -make build # 前端 dist + 兩個 linux Go binary(backend/bin/) -``` - -## 3. 安裝到目標主機 - -於目標主機(需 root): - -```bash -sudo make install -``` - -`make install` 會: - -1. 建立使用者 `haixun` 與目錄 `/opt/haixun/{bin,etc,node-worker}`、`/var/www/haixun`。 -2. 複製 `backend/bin/{gateway,worker}`、`backend/etc/gateway.prod.yaml`、`backend/etc/gateway.worker.prod.yaml`。 -3. 複製 `frontend/dist/*` → `/var/www/haixun`。 -4. 複製 `backend/worker/*`(Node worker)→ `/opt/haixun/node-worker`,並 `npm ci` + `npx playwright install`。 -5. 安裝 `infra/systemd/*.service` 與 `infra/nginx/haixun.conf`。 - -接著手動建立 secret 檔(**只做一次**): - -```bash -sudo cp infra/etc/haixun.env.example /opt/haixun/etc/haixun.env -sudo chmod 600 /opt/haixun/etc/haixun.env -sudoedit /opt/haixun/etc/haixun.env # 填入實際 secret -``` - -## 4. 初始化資料庫與 admin 帳號(只做一次) - -Mongo 起來、secret 填好後,建立索引 / 權限 catalog / role_permissions,並建立第一個 admin: - -```bash -# 可在 haixun.env 內設定 INIT_ADMIN_EMAIL / INIT_ADMIN_PASSWORD,或在這裡用環境變數覆寫 -sudo make prod-init -# 等同:source /opt/haixun/etc/haixun.env 後執行 /opt/haixun/bin/tool init -f /opt/haixun/etc/gateway.prod.yaml -``` - -之後一般使用者可走 `POST /api/v1/auth/register` 自助註冊(前端登入頁)。 - -## 5. 啟用服務 - -```bash -sudo systemctl daemon-reload -sudo systemctl enable --now haixun-gateway haixun-worker haixun-node-worker -sudo nginx -t && sudo systemctl reload nginx -``` - -## 6. 健康檢查 - -```bash -curl http://127.0.0.1:8890/api/v1/health -sudo systemctl status haixun-gateway haixun-worker haixun-node-worker -journalctl -u haixun-gateway -f -``` - -## 產生 secret - -```bash -openssl rand -base64 48 # JWT access / refresh / worker secret -openssl rand -base64 32 # HAIXUN_SECRETS_KEY(機敏資料落地加密) -``` diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml deleted file mode 100644 index fec1b16..0000000 --- a/infra/docker-compose.yml +++ /dev/null @@ -1,42 +0,0 @@ -# 巡樓資料服務:Mongo + Redis -# 只綁 127.0.0.1,給同主機上以 systemd 跑的 Go gateway / worker 連線。 -# 啟動:docker compose -f infra/docker-compose.yml --env-file infra/.env up -d -name: haixun-infra - -services: - mongo: - image: mongo:7 - restart: unless-stopped - ports: - - "127.0.0.1:${MONGO_PORT:-27017}:27017" - environment: - MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-haixun} - MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD is required} - MONGO_INITDB_DATABASE: ${MONGO_DATABASE:-haixun} - volumes: - - mongo_data:/data/db - healthcheck: - test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 20s - - redis: - image: redis:7 - restart: unless-stopped - command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?REDIS_PASSWORD is required}", "--appendonly", "yes"] - ports: - - "127.0.0.1:${REDIS_PORT:-6379}:6379" - volumes: - - redis_data:/data - healthcheck: - test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - -volumes: - mongo_data: - redis_data: diff --git a/infra/etc/haixun.env.example b/infra/etc/haixun.env.example deleted file mode 100644 index 903366c..0000000 --- a/infra/etc/haixun.env.example +++ /dev/null @@ -1,23 +0,0 @@ -# 部署到目標主機的 /opt/haixun/etc/haixun.env(chmod 600,不要 commit 實值) -# gateway.prod.yaml / gateway.worker.yaml 用 ${VAR} 讀取這些值(go-zero conf.UseEnv)。 - -# Mongo(含 docker compose 設定的帳密;authSource=admin) -HAIXUN_MONGO_URI=mongodb://haixun:change-me-mongo-pass@127.0.0.1:27017/?authSource=admin -HAIXUN_MONGO_DB=haixun - -# Redis -HAIXUN_REDIS_ADDR=127.0.0.1:6379 -HAIXUN_REDIS_PASSWORD=change-me-redis-pass - -# JWT secret(請用 openssl rand -base64 48 產生,兩把不同) -HAIXUN_JWT_ACCESS_SECRET=replace-with-strong-random -HAIXUN_JWT_REFRESH_SECRET=replace-with-another-strong-random - -# 內部 worker secret(gateway 與 node worker 必須一致) -HAIXUN_WORKER_SECRET=replace-with-strong-random - -# 機敏資料落地加密金鑰(base64 編碼的 32 bytes;openssl rand -base64 32) -HAIXUN_SECRETS_KEY=replace-with-base64-32-bytes - -# Node worker 連線設定 -HAIXUN_BACKEND_URL=http://127.0.0.1:8890 diff --git a/infra/systemd/haixun-gateway.service b/infra/systemd/haixun-gateway.service deleted file mode 100644 index 9030f3f..0000000 --- a/infra/systemd/haixun-gateway.service +++ /dev/null @@ -1,25 +0,0 @@ -[Unit] -Description=Haixun Gateway API (go-zero) -After=network-online.target docker.service -Wants=network-online.target - -[Service] -Type=simple -User=haixun -Group=haixun -WorkingDirectory=/opt/haixun -# secrets(JWT / Mongo URI / Redis 密碼 / worker secret / 加密金鑰)放這裡,不進 repo -EnvironmentFile=/opt/haixun/etc/haixun.env -ExecStart=/opt/haixun/bin/gateway -f /opt/haixun/etc/gateway.prod.yaml -Restart=always -RestartSec=5 -LimitNOFILE=65535 - -# 加固 -NoNewPrivileges=true -ProtectSystem=full -ProtectHome=true -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/infra/systemd/haixun-node-worker.service b/infra/systemd/haixun-node-worker.service deleted file mode 100644 index 7221767..0000000 --- a/infra/systemd/haixun-node-worker.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Haixun Node Playwright Worker (style-8d) -After=network-online.target haixun-gateway.service -Wants=network-online.target - -[Service] -Type=simple -User=haixun -Group=haixun -WorkingDirectory=/opt/haixun/node-worker -# 至少需要 HAIXUN_BACKEND_URL 與 HAIXUN_WORKER_SECRET(與 gateway 的 InternalWorker.Secret 一致) -EnvironmentFile=/opt/haixun/etc/haixun.env -ExecStart=/usr/bin/npx tsx style-8d-worker.ts -Restart=always -RestartSec=10 -LimitNOFILE=65535 - -NoNewPrivileges=true -ProtectSystem=full -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/infra/systemd/haixun-worker.service b/infra/systemd/haixun-worker.service deleted file mode 100644 index df7a3e6..0000000 --- a/infra/systemd/haixun-worker.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Haixun Go Job Worker -After=network-online.target docker.service haixun-gateway.service -Wants=network-online.target - -[Service] -Type=simple -User=haixun -Group=haixun -WorkingDirectory=/opt/haixun -EnvironmentFile=/opt/haixun/etc/haixun.env -ExecStart=/opt/haixun/bin/worker -f /opt/haixun/etc/gateway.worker.prod.yaml -Restart=always -RestartSec=5 -LimitNOFILE=65535 - -NoNewPrivileges=true -ProtectSystem=full -ProtectHome=true -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/start-dev.sh b/start-dev.sh deleted file mode 100755 index 21a0869..0000000 --- a/start-dev.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash -set -e - -echo "=== 啟動巡樓開發環境服務 (不含 infra) ===" -echo "前置條件: infra (Mongo/Redis) 必須已啟動,請先執行: make dev-infra" -echo "" - -# 檢查 infra 是否運行 -if ! docker compose -f infra/docker-compose.yml ps --status running 2>/dev/null | grep -q "mongo\|redis"; then - echo "⚠️ 警告: infra (Mongo/Redis) 似乎未運行" - echo " 請先執行: make dev-infra" - echo "" - read -p "是否繼續啟動服務? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi -fi - -# 1. 啟動 Go gateway (port 8890) - 背景執行 -echo "[1/4] 啟動 Go gateway (:8890)..." -cd backend -if [ ! -f etc/gateway.yaml ]; then - echo " ✗ 找不到 etc/gateway.yaml" - exit 1 -fi -nohup go run . -f etc/gateway.yaml > /tmp/haixun-gateway.log 2>&1 & -GATEWAY_PID=$! -cd .. -sleep 2 -if kill -0 $GATEWAY_PID 2>/dev/null; then - echo " ✓ Gateway 已啟動 (PID: $GATEWAY_PID, log: /tmp/haixun-gateway.log)" -else - echo " ✗ Gateway 啟動失敗,查看日誌: cat /tmp/haixun-gateway.log" - exit 1 -fi - -# 2. Go job worker 已內嵌在 gateway(etc/gateway.yaml JobWorker.Enabled=true) -echo "[2/4] Go job worker..." -echo " ✓ 使用 gateway 內嵌 worker(勿另開 cmd/worker,避免殘留舊進程搶 job)" - -# 3. 啟動 Node worker (style-8d) - 背景執行 -echo "[3/4] 啟動 Node worker (style-8d)..." -cd backend/worker -if [ ! -f package.json ]; then - echo " ✗ 找不到 package.json" - exit 1 -fi -export HAIXUN_WORKER_SECRET=haixun-dev-worker-secret -export HAIXUN_BACKEND_URL=http://127.0.0.1:8890 -nohup npm run style-8d > /tmp/haixun-node-worker.log 2>&1 & -NODE_WORKER_PID=$! -cd ../.. -sleep 3 -if kill -0 $NODE_WORKER_PID 2>/dev/null; then - echo " ✓ Node worker 已啟動 (PID: $NODE_WORKER_PID, log: /tmp/haixun-node-worker.log)" -else - echo " ✗ Node worker 啟動失敗,查看日誌: cat /tmp/haixun-node-worker.log" - exit 1 -fi - -# 4. 啟動前端 dev server (port 5173) - 背景執行 -echo "[4/4] 啟動前端 dev server (:5173)..." -cd frontend -if [ ! -f package.json ]; then - echo " ✗ 找不到 package.json" - exit 1 -fi -nohup npm run dev > /tmp/haixun-frontend.log 2>&1 & -FRONTEND_PID=$! -cd .. -sleep 3 -if kill -0 $FRONTEND_PID 2>/dev/null; then - echo " ✓ 前端已啟動 (PID: $FRONTEND_PID, log: /tmp/haixun-frontend.log)" -else - echo " ✗ 前端啟動失敗,查看日誌: cat /tmp/haixun-frontend.log" - exit 1 -fi - -echo "" -echo "=== 所有開發環境服務已啟動 ===" -echo "" -echo "服務狀態:" -echo " Gateway (API): http://localhost:8890 (PID: $GATEWAY_PID, 內嵌 Go worker)" -echo " Node Worker: style-8d worker (PID: $NODE_WORKER_PID)" -echo " Frontend (Vite): http://localhost:5173 (PID: $FRONTEND_PID)" -echo "" -echo "日誌檔案:" -echo " Gateway: tail -f /tmp/haixun-gateway.log" -echo " Node Worker: tail -f /tmp/haixun-node-worker.log" -echo " Frontend: tail -f /tmp/haixun-frontend.log" -echo "" -echo "停止所有服務: ./stop-dev.sh" -echo "查看 infra: make dev-infra # 或 docker compose -f infra/docker-compose.yml ps" \ No newline at end of file diff --git a/start-prod.sh b/start-prod.sh deleted file mode 100755 index a25f15a..0000000 --- a/start-prod.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -set -e - -echo "=== 巡樓 Production 啟動 ===" - -# 1. 確認 nginx 不會被 default site 搶走 -echo "[1/7] 確保 nginx config..." -if [ -f /etc/nginx/sites-enabled/default ]; then - sudo rm /etc/nginx/sites-enabled/default -fi - -# 2. 起 infra (Mongo + Redis) -echo "[2/7] 啟動 Mongo + Redis..." -docker compose -f infra/docker-compose.yml --env-file infra/.env up -d - -# 3. 確保 Playwright 瀏覽器已安裝 -echo "[3/7] 確認 Playwright chromium..." -if [ ! -d /opt/haixun/.cache/ms-playwright/chromium-1228 ]; then - sudo -u haixun bash -c 'cd /opt/haixun/node-worker && npx playwright install chromium' -fi - -# 4. 清除 systemd 失敗計數(之前 crash loop 的話需要 reset) -echo "[4/7] 重置 systemd 狀態..." -for svc in haixun-gateway haixun-worker haixun-node-worker; do - sudo systemctl reset-failed "$svc" 2>/dev/null || true -done - -# 5. 啟動 systemd 服務 -echo "[5/7] 啟動 backend gateway..." -sudo systemctl start haixun-gateway -echo "[5/7] 啟動 worker..." -sudo systemctl start haixun-worker -echo "[5/7] 啟動 node worker..." -sudo systemctl start haixun-node-worker - -# 6. 初始化 DB(indexes + admin,冪等) -echo "[6/7] 初始化 DB(admin: admin@30cm.net)..." -set -a; source /opt/haixun/etc/haixun.env; set +a -sudo -E /opt/haixun/bin/tool init -f /opt/haixun/etc/gateway.prod.yaml - -# 7. 確認 nginx 執行 + reload config -echo "[7/7] 確認 nginx..." -sudo nginx -t && sudo systemctl start nginx && sudo systemctl reload nginx - -echo "" -echo "=== 啟動完成 ===" -echo " nginx: $(systemctl is-active nginx)" -echo " gateway: $(systemctl is-active haixun-gateway)" -echo " worker: $(systemctl is-active haixun-worker)" -echo " node-wkr: $(systemctl is-active haixun-node-worker)" -echo " docker: $(docker compose -f infra/docker-compose.yml ps --status running 2>/dev/null | wc -l) containers running" -echo "" -echo "健康檢查:" -echo " curl http://localhost/api/v1/health" -echo "" -echo "查看日誌:" -echo " journalctl -u haixun-gateway -n 50 --no-pager -f" -echo " journalctl -u haixun-worker -n 50 --no-pager -f" -echo " docker compose -f infra/docker-compose.yml logs -f" diff --git a/status-dev.sh b/status-dev.sh deleted file mode 100755 index cd0e97f..0000000 --- a/status-dev.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -echo "=== 巡樓開發環境服務狀態 ===" -echo "" - -# Infra (Mongo/Redis) -echo "📦 Infra (Docker):" -docker compose -f infra/docker-compose.yml ps 2>/dev/null || echo " 未運行或 docker compose 不可用" -echo "" - -# Port 佔用檢查 -check_port() { - local port=$1 - local name=$2 - if lsof -ti:$port >/dev/null 2>&1; then - local pid=$(lsof -ti:$port | head -1) - local cmd=$(ps -p $pid -o comm= 2>/dev/null || echo "unknown") - echo " ✅ $name (port $port) - PID: $pid ($cmd)" - else - echo " ❌ $name (port $port) - 未運行" - fi -} - -echo "🔌 服務 Port 狀態:" -check_port 8890 "Gateway (API)" -check_port 8891 "Worker (Go)" -check_port 5173 "Frontend (Vite)" - -# Node worker 沒有固定 port,檢查進程 -echo "" -echo "🔧 Node Worker (style-8d):" -if pgrep -f "style-8d" >/dev/null 2>&1; then - pgrep -f "style-8d" | while read pid; do - echo " ✅ 運行中 - PID: $pid" - done -else - echo " ❌ 未運行" -fi - -echo "" -echo "📋 管理指令:" -echo " 啟動所有服務: ./start-dev.sh" -echo " 停止所有服務: ./stop-dev.sh" -echo " 啟動 infra: make dev-infra" -echo " 停止 infra: make dev-infra-down" \ No newline at end of file diff --git a/status-prod.sh b/status-prod.sh deleted file mode 100755 index f0c0cc6..0000000 --- a/status-prod.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -echo "=== 巡樓 Production 狀態 ===" -echo "" -echo "--- Nginx ---" -systemctl status nginx --no-pager -n 5 2>&1 | head -5 -echo "" -echo "--- Gateway ---" -systemctl status haixun-gateway --no-pager -n 5 2>&1 | head -5 -echo "" -echo "--- Worker ---" -systemctl status haixun-worker --no-pager -n 5 2>&1 | head -5 -echo "" -echo "--- Node Worker ---" -systemctl status haixun-node-worker --no-pager -n 5 2>&1 | head -5 -echo "" -echo "--- Docker Infra ---" -docker compose -f /home/daniel/thread-master/infra/docker-compose.yml ps 2>&1 -echo "" -echo "--- Port 8890 ---" -ss -tlnp | grep 8890 || echo "gateway not listening" diff --git a/stop-dev.sh b/stop-dev.sh deleted file mode 100755 index 775f757..0000000 --- a/stop-dev.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -set -e - -echo "=== 停止巡樓開發環境服務 (保留 infra) ===" - -# 1. 停止前端 dev server (Vite on :5173) -echo "[1/4] 停止前端 dev server..." -pkill -f "vite.*dev" 2>/dev/null && echo " ✓ Vite dev server 已停止" || echo " - Vite dev server 未在執行" - -# 2. 停止 Node worker (style-8d) -echo "[2/4] 停止 Node worker (style-8d)..." -pkill -f "style-8d-worker" 2>/dev/null && echo " ✓ Node worker 已停止" || echo " - Node worker 未在執行" -pkill -f "tsx.*style-8d" 2>/dev/null && echo " ✓ tsx worker 已停止" || true - -# 3. 停止 Go worker(含編譯後殘留的 exe/worker 與 go run ./cmd/worker) -echo "[3/4] 停止 Go worker..." -pkill -f "go run ./cmd/worker" 2>/dev/null || true -pkill -f "cmd/worker.*gateway.worker.yaml" 2>/dev/null || true -pkill -f "exe/worker -f etc/gateway.worker.yaml" 2>/dev/null || true -pkill -f "worker -f etc/gateway.worker.yaml" 2>/dev/null || true -if pgrep -f "gateway.worker.yaml" >/dev/null 2>&1; then - echo " ⚠ 仍有 worker 殘留,強制結束..." - pkill -9 -f "gateway.worker.yaml" 2>/dev/null || true -fi -echo " ✓ Go worker 已停止" - -# 4. 停止 Go gateway (port 8890) -echo "[4/4] 停止 Go gateway..." -pkill -f "go run . -f etc/gateway.yaml" 2>/dev/null || true -pkill -f "exe/haixun-backend -f etc/gateway.yaml" 2>/dev/null || true -pkill -f "haixun-backend -f etc/gateway.yaml" 2>/dev/null || true -if pgrep -f "etc/gateway.yaml" >/dev/null 2>&1; then - echo " ⚠ 仍有 gateway 殘留,強制結束..." - pkill -9 -f "etc/gateway.yaml" 2>/dev/null || true -fi -echo " ✓ Go gateway 已停止" - -# 5. 清理可能殘留的 port 佔用 (optional, 避免 port 衝突) -for port in 8890 8891 5173; do - if lsof -ti:$port >/dev/null 2>&1; then - echo " ⚠ Port $port 仍被佔用,強制釋放..." - lsof -ti:$port | xargs kill -9 2>/dev/null || true - fi -done - -echo "" -echo "=== 開發環境服務已停止 (infra: Mongo/Redis 保持執行) ===" -echo "提示: 若需停止 infra,請執行: make dev-infra-down" \ No newline at end of file diff --git a/stop-prod.sh b/stop-prod.sh deleted file mode 100755 index daca685..0000000 --- a/stop-prod.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -echo "=== 巡樓 Production 停止 ===" -sudo systemctl stop haixun-worker -sudo systemctl stop haixun-gateway -docker compose -f /home/daniel/thread-master/infra/docker-compose.yml down -echo "done"