diff --git a/.env.example b/.env.example index f9d34c1..8d3e27a 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -# MySQL +# MySQL (docker-compose 内部默认值,wizard 配置优先) MYSQL_ROOT_PASSWORD=harborforge_root MYSQL_DATABASE=harborforge MYSQL_USER=harborforge @@ -10,10 +10,10 @@ SECRET_KEY=change_me_in_production LOG_LEVEL=INFO BACKEND_PORT=8000 -# AbstractWizard -WIZARD_URL=http://wizard:8080 -WIZARD_CONFIG=harborforge.json +# AbstractWizard (localhost only) +WIZARD_PORT=18080 # Frontend FRONTEND_PORT=3000 VITE_API_BASE=/api +VITE_WIZARD_PORT=18080 diff --git a/HarborForge.Backend b/HarborForge.Backend index 4b20444..c1288b5 160000 --- a/HarborForge.Backend +++ b/HarborForge.Backend @@ -1 +1 @@ -Subproject commit 4b20444a5e420a59a4fb50457787cc257bed4b59 +Subproject commit c1288b5fa92233c3cc1ebaddb91d7392bee2384a diff --git a/HarborForge.Frontend b/HarborForge.Frontend index 54d4c43..f8fac48 160000 --- a/HarborForge.Frontend +++ b/HarborForge.Frontend @@ -1 +1 @@ -Subproject commit 54d4c4379a017571d8ecdb8959a30a70879df84a +Subproject commit f8fac48fcc500e0e3f63f4b5cc88309899fad8d7 diff --git a/README.md b/README.md index 2798e17..018a4f0 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,50 @@ git clone https://git.hangman-lab.top/zhi/HarborForge.git cd HarborForge git submodule update --init --recursive -# 配置环境变量 -cp .env.example .env - # 启动服务 docker compose up -d ``` +## 首次部署 — 初始化向导 + +HarborForge 使用 [AbstractWizard](https://git.hangman-lab.top/hzhang/AbstractWizard) 进行安全初始化。 +Wizard 仅监听 `127.0.0.1`,必须通过 SSH 隧道访问。 + +```bash +# 1. SSH 隧道映射 wizard 端口到本地 +ssh -L 18080:127.0.0.1:18080 user@your-server + +# 2. 浏览器访问前端(或通过宿主机 nginx) +# 前端检测到后端未就绪 → 自动跳转初始化向导 + +# 3. 在向导中配置: +# - 数据库连接信息 +# - 管理员账号 +# - 默认项目(可选) + +# 4. 配置保存后,后端自动检测到配置并启动 +# 刷新页面 → 进入登录界面 +``` + +### 启动流程 + +``` +docker compose up + ├── mysql → 数据库启动 + ├── wizard → AbstractWizard 启动 (127.0.0.1:18080) + ├── backend → 等待配置文件... (轮询 /config/harborforge.json) + └── frontend → 检测后端状态 + ├── 后端未就绪 → 显示初始化向导 (SSH 隧道连 wizard) + └── 后端就绪 → 正常登录界面 +``` + +### 安全模型 + +- Wizard 端口绑定 `127.0.0.1`,不暴露到外部网络 +- 初始化必须通过 SSH 隧道完成(与 AbstractWizard 安全模型一致) +- 配置完成后 Wizard 自动切换为只读模式 +- 配置通过 Docker volume 共享给后端(不走网络) + ## 部署架构 ``` @@ -35,32 +72,11 @@ docker compose up -d ├── / → frontend (Docker, port 3000) └── /api/ → backend (Docker, port 8000) -Docker 内部: - wizard (AbstractWizard) → 初始化配置管理 - wizard-init → 首次启动上传默认配置 - backend → 启动时从 wizard 读取配置,创建 admin 用户等 +Docker 内部 (不暴露): + wizard (127.0.0.1:18080) → 配置管理,SSH 隧道访问 + wizard_config volume → wizard 写入,backend 读取 ``` -前端 Docker 容器不包含 nginx,使用轻量的 `serve` 提供静态文件。 -API 代理由宿主机 nginx 统一处理。 - -### AbstractWizard 初始化 - -首次部署时,`wizard-init` 会将 `init-config/harborforge.json` 上传到 AbstractWizard。 -后端启动时自动从 AbstractWizard 读取配置并创建 admin 用户和默认项目。 - -修改初始化配置: -```bash -# 直接编辑 init-config/harborforge.json(首次部署前) -# 或通过 AbstractWizard API 修改(部署后) -curl -X PATCH http://localhost:18080/api/v1/config/harborforge.json \ - -d '{"admin": {"password": "new_secure_password"}}' -``` - -### 宿主机 nginx 配置 - -参考 `nginx-host.conf.example`,复制到 `/etc/nginx/sites-available/` 并修改域名。 - ## 子模块 - [HarborForge.Backend](https://git.hangman-lab.top/zhi/HarborForge.Backend) - FastAPI 后端 API @@ -68,14 +84,16 @@ curl -X PATCH http://localhost:18080/api/v1/config/harborforge.json \ ## 端口 -| 服务 | 默认端口 | 环境变量 | -|------|----------|----------| -| Frontend | 3000 | `FRONTEND_PORT` | -| Backend | 8000 | `BACKEND_PORT` | -| MySQL | 3306 | `MYSQL_PORT` | +| 服务 | 默认端口 | 绑定 | 环境变量 | +|------|----------|------|----------| +| Frontend | 3000 | 0.0.0.0 | `FRONTEND_PORT` | +| Backend | 8000 | 0.0.0.0 | `BACKEND_PORT` | +| MySQL | 3306 | 127.0.0.1 | `MYSQL_PORT` | +| Wizard | 18080 | 127.0.0.1 | `WIZARD_PORT` | ## 前端页面 +- 🔧 初始化向导 — 首次部署配置(SSH 隧道) - 📊 仪表盘 — 统计概览 - 📋 Issues — 创建、列表、详情、状态变更、评论 - 📁 项目 — 项目管理、成员、关联 issue diff --git a/docker-compose.yml b/docker-compose.yml index a939204..36aab29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: volumes: - mysql_data:/var/lib/mysql ports: - - "${MYSQL_PORT:-3306}:3306" + - "127.0.0.1:${MYSQL_PORT:-3306}:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s @@ -25,59 +25,25 @@ services: cpus: '0.5' memory: 512M - # AbstractWizard — 初始化配置管理 + # AbstractWizard — 初始化配置管理 (localhost only, SSH tunnel access) wizard: image: git.hangman-lab.top/hzhang/abstract-wizard:latest container_name: harborforge-wizard restart: unless-stopped volumes: - wizard_config:/config - - ./init-config:/init-config:ro environment: CONFIG_DIR: /config LISTEN_ADDR: "0.0.0.0:8080" MAX_BACKUPS: "5" - # distroless image — no shell for healthcheck - # wizard-init will retry until wizard is reachable + ports: + - "127.0.0.1:${WIZARD_PORT:-18080}:8080" deploy: resources: limits: cpus: '0.1' memory: 64M - # 初始化 — 将默认配置写入 AbstractWizard - wizard-init: - image: curlimages/curl:latest - container_name: harborforge-wizard-init - depends_on: - - wizard - volumes: - - ./init-config:/init-config:ro - entrypoint: ["/bin/sh", "-c"] - command: - - | - echo "Waiting for AbstractWizard to be ready..." - for i in $$(seq 1 30); do - if curl -sf http://wizard:8080/health > /dev/null 2>&1; then - break - fi - echo " attempt $$i/30..." - sleep 2 - done - - echo "Checking if harborforge.json exists in wizard..." - STATUS=$$(curl -s -o /dev/null -w '%%{http_code}' http://wizard:8080/api/v1/config/harborforge.json) - if [ "$$STATUS" = "404" ]; then - echo "Config not found, uploading init-config/harborforge.json..." - curl -s -X PUT http://wizard:8080/api/v1/config/harborforge.json \ - -H "Content-Type: application/json" \ - -d @/init-config/harborforge.json - echo "" - echo "Init config uploaded successfully." - else - echo "Config already exists (status=$$STATUS), skipping upload." - fi - backend: build: context: ./HarborForge.Backend @@ -85,18 +51,17 @@ services: container_name: harborforge-backend restart: unless-stopped environment: - DATABASE_URL: mysql+pymysql://${MYSQL_USER:-harborforge}:${MYSQL_PASSWORD:-harborforge_pass}@mysql:3306/${MYSQL_DATABASE:-harborforge} + CONFIG_DIR: /config + CONFIG_FILE: harborforge.json SECRET_KEY: ${SECRET_KEY:-change_me_in_production} LOG_LEVEL: ${LOG_LEVEL:-INFO} - WIZARD_URL: http://wizard:8080 - WIZARD_CONFIG: harborforge.json + volumes: + - wizard_config:/config:ro ports: - "${BACKEND_PORT:-8000}:8000" depends_on: mysql: condition: service_healthy - wizard-init: - condition: service_completed_successfully deploy: resources: limits: @@ -115,13 +80,13 @@ services: dockerfile: Dockerfile args: VITE_API_BASE: ${VITE_API_BASE:-/api} + VITE_WIZARD_PORT: ${WIZARD_PORT:-18080} container_name: harborforge-frontend restart: unless-stopped ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: - backend: - condition: service_healthy + - backend deploy: resources: limits: diff --git a/init-config/harborforge.json b/init-config/harborforge.json deleted file mode 100644 index f7f4c2c..0000000 --- a/init-config/harborforge.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "admin": { - "username": "admin", - "email": "admin@harborforge.local", - "password": "changeme", - "full_name": "HarborForge Admin" - }, - "default_project": { - "name": "Default", - "description": "默认项目" - } -}