# 巡樓 monorepo Makefile # 兩種模式: # dev - 本機開發(docker 起 Mongo/Redis,go run / vite dev) # prod - 建置產物(前端 dist + linux Go binary),並可部署成 systemd 服務 + nginx # # 常用: # 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) 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 # --- 交叉編譯目標(在 mac 上 build 給 linux 主機)--- GOOS ?= linux GOARCH ?= amd64 # --- docker compose --- COMPOSE := docker compose -f $(INFRA_DIR)/docker-compose.yml --env-file $(INFRA_DIR)/.env # --- dev 用 worker secret(對應 etc/gateway.yaml 的 InternalWorker.Secret)--- DEV_WORKER_SECRET := haixun-dev-worker-secret DEV_BACKEND_URL := http://127.0.0.1:8890 .DEFAULT_GOAL := help .PHONY: help help: ## 顯示可用指令 @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' # ============================================================ # DEV # ============================================================ $(INFRA_DIR)/.env: @test -f $(INFRA_DIR)/.env || (cp $(INFRA_DIR)/.env.example $(INFRA_DIR)/.env && echo "已從 .env.example 建立 $(INFRA_DIR)/.env,請視需要修改密碼") .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 $(COMPOSE) down .PHONY: dev-backend dev-backend: ## [dev] 跑 gateway API (:8890) cd $(BACKEND_DIR) && go run . -f etc/gateway.yaml .PHONY: dev-worker dev-worker: ## [dev] 跑 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) 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) cd $(FRONTEND_DIR) && npm install && npm run dev .PHONY: dev-init dev-init: ## [dev] 初始化 DB(索引/權限)並建立 admin 帳號(可用 INIT_ADMIN_EMAIL / INIT_ADMIN_PASSWORD 覆寫) 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" # ============================================================ # 驗證 / 維護 # ============================================================ .PHONY: tidy tidy: ## go mod tidy cd $(BACKEND_DIR) && go mod tidy .PHONY: fmt fmt: ## gofmt 後端 cd $(BACKEND_DIR) && gofmt -w . .PHONY: test test: ## 跑後端測試 cd $(BACKEND_DIR) && go test ./... .PHONY: verify verify: ## 後端 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"