This commit is contained in:
王性驊 2026-06-28 18:08:43 +08:00
parent 184e7763ac
commit eaf029a16f
26 changed files with 518 additions and 716 deletions

View File

@ -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/**

4
.gitignore vendored
View File

@ -1,7 +1,5 @@
# --- secrets / env不要 commit 實值)---
infra/.env
infra/etc/haixun.env
/opt/haixun/
deploy/.env
# --- build 產物 ---
backend/bin/

303
Makefile
View File

@ -1,33 +1,32 @@
# 巡樓 monorepo Makefile
# 兩種模式:
# dev - 本機開發docker 起 Mongo/Redisgo run / vite dev
# prod - 建置產物(前端 dist + linux Go binary並可部署成 systemd 服務 + nginx
# dev - 本機開發docker 起 Mongo/Redisgo 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 # 跑前端 (:5173proxy 到 :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 composeprod 全棧)---
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 開 :8890web 開 :8080go 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 + Redisdocker其餘本機跑
$(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"

View File

@ -3,13 +3,17 @@ Host: 0.0.0.0
Port: 8890
Timeout: 120000
# 本地「docker 測試」設定make dev-up 使用)。
# 連線走 compose service namemongo / 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

29
deploy/.env.dev Normal file
View File

@ -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=
# === 初始 admininit 容器建立,冪等) ===
INIT_ADMIN_EMAIL=admin@30cm.net
INIT_ADMIN_PASSWORD=Fafafa54088

29
deploy/.env.example Normal file
View File

@ -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=
# === 初始 admininit 容器建立,冪等) ===
INIT_ADMIN_EMAIL=admin@30cm.net
INIT_ADMIN_PASSWORD=Fafafa54088

25
deploy/Dockerfile.backend Normal file
View File

@ -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/
# 預設角色為 gatewaycompose 會把 worker / init 用 entrypoint 覆寫。
EXPOSE 8890
ENTRYPOINT ["/app/gateway"]
CMD ["-f", "/app/etc/gateway.prod.yaml"]

View File

@ -0,0 +1,13 @@
# Node playwright workerstyle-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"]

14
deploy/Dockerfile.web Normal file
View File

@ -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

61
deploy/README.md Normal file
View File

@ -0,0 +1,61 @@
# 巡樓部署Docker 一鍵啟動)
整套系統用單一 `docker compose` 拉起,對外只開前端 `:80`
```
瀏覽器 → web(nginx :80)
├─ 前端靜態檔vite build
└─ /api 反代 → gateway:8890
gateway / workerGo─┐
node-workerplaywright┤→ 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` | nginxserve 前端 + 反代 `/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`

View File

@ -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.yamlDevHeaderFallback=true、不加密
# - 掛載 backend/etc改 yaml 後 restart 容器即可生效,不必 rebuild。
# - gateway 對外開 :8890 方便直接打 APIweb 走 .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"

138
deploy/docker-compose.yml Normal file
View File

@ -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:

View File

@ -1,10 +1,8 @@
# 巡樓 Console nginx 設定
# 安裝cp infra/nginx/haixun.conf /etc/nginx/conf.d/haixun.conf && nginx -t && systemctl reload nginx
# 前端靜態檔部署在 /var/www/haixunmake install 會放 frontend/dist 內容)。
# /api 反向代理到本機 systemd 跑的 Go gateway (127.0.0.1:8890),含 SSE 串流設定。
# 巡樓 Console nginx容器內serve 前端靜態檔 + /api 反代到 gateway 容器。
# upstream compose service namegateway: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;
# 安全標頭

View File

@ -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

View File

@ -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`(不進 repoyaml 用 `${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 binarybackend/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機敏資料落地加密
```

View File

@ -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:

View File

@ -1,23 +0,0 @@
# 部署到目標主機的 /opt/haixun/etc/haixun.envchmod 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 secretgateway 與 node worker 必須一致)
HAIXUN_WORKER_SECRET=replace-with-strong-random
# 機敏資料落地加密金鑰base64 編碼的 32 bytesopenssl rand -base64 32
HAIXUN_SECRETS_KEY=replace-with-base64-32-bytes
# Node worker 連線設定
HAIXUN_BACKEND_URL=http://127.0.0.1:8890

View File

@ -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
# secretsJWT / 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

View File

@ -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

View File

@ -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

View File

@ -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 已內嵌在 gatewayetc/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"

View File

@ -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. 初始化 DBindexes + admin冪等
echo "[6/7] 初始化 DBadmin: 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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"