diff --git a/.gitignore b/.gitignore index 1bdb9b6..49048af 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .env .env.* !.env.example +!.env.prod.example # Go bin/ diff --git a/backend/.env.example b/backend/.env.example index a5127d4..5f66713 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,7 +1,13 @@ APP_ENV=development APP_ADDR=:8080 +# MySQL 容器初始化变量,同时供后端 DSN 使用。 +MYSQL_ROOT_PASSWORD=rootsecret +MYSQL_DATABASE=hfb_sys +MYSQL_USER=hfb +MYSQL_PASSWORD=secret MYSQL_DSN=hfb:secret@tcp(127.0.0.1:3306)/hfb_sys?charset=utf8mb4&parseTime=True&loc=Local + REDIS_ADDR=127.0.0.1:6379 REDIS_PASSWORD= REDIS_DB=0 @@ -13,11 +19,15 @@ LOG_DIR=logs LOG_ENABLE_CONSOLE=true LOG_ENABLE_FILE=true +# MinIO 容器初始化变量,同时供后端对象存储使用。 +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=minioadmin STORAGE_ENDPOINT=http://localhost:9000 STORAGE_BUCKET=hfb-sys STORAGE_ACCESS_KEY_ID=minioadmin STORAGE_SECRET_ACCESS_KEY=minioadmin +# 短信服务:本地开发使用 mock。 SMS_PROVIDER=mock ALIYUN_ACCESS_KEY_ID= ALIYUN_ACCESS_KEY_SECRET= @@ -25,6 +35,7 @@ ALIYUN_SMS_ENDPOINT=dysmsapi.aliyuncs.com ALIYUN_SMS_SIGN_NAME= ALIYUN_SMS_LOGIN_TEMPLATE_CODE= +# 实名认证:本地开发使用 mock;生产可改为 cloudmarket 并填写云市场 AppCode。 REALNAME_PROVIDER=mock REALNAME_CLOUDMARKET_URL=https://sinocheck2.market.alicloudapi.com/fortest/ttttt REALNAME_CLOUDMARKET_APPCODE= diff --git a/backend/.env.prod.example b/backend/.env.prod.example new file mode 100644 index 0000000..9250f44 --- /dev/null +++ b/backend/.env.prod.example @@ -0,0 +1,42 @@ +# 生产环境示例:复制为 backend/.env 后再替换所有 change-* 占位值。 +APP_ENV=production +APP_ADDR=:8080 + +# MySQL 容器初始化变量,同时供后端 DSN 使用。 +MYSQL_ROOT_PASSWORD=change-root-password +MYSQL_DATABASE=hfb_sys +MYSQL_USER=hfb +MYSQL_PASSWORD=change-hfb-password +MYSQL_DSN=hfb:change-hfb-password@tcp(mysql:3306)/hfb_sys?charset=utf8mb4&parseTime=True&loc=Local + +REDIS_ADDR=redis:6379 +REDIS_PASSWORD= +REDIS_DB=0 + +JWT_SECRET=change-to-a-long-random-secret + +LOG_LEVEL=info +LOG_DIR=logs +LOG_ENABLE_CONSOLE=true +LOG_ENABLE_FILE=true + +# MinIO 容器初始化变量,同时供后端对象存储使用。 +MINIO_ROOT_USER=change-minio-user +MINIO_ROOT_PASSWORD=change-minio-password +STORAGE_ENDPOINT=http://minio:9000 +STORAGE_BUCKET=hfb-sys +STORAGE_ACCESS_KEY_ID=change-minio-user +STORAGE_SECRET_ACCESS_KEY=change-minio-password + +# 生产环境建议接入真实短信服务;未配置时不要使用 mock 对外运营。 +SMS_PROVIDER=aliyun +ALIYUN_ACCESS_KEY_ID= +ALIYUN_ACCESS_KEY_SECRET= +ALIYUN_SMS_ENDPOINT=dysmsapi.aliyuncs.com +ALIYUN_SMS_SIGN_NAME= +ALIYUN_SMS_LOGIN_TEMPLATE_CODE= + +# 生产环境建议接入真实实名服务;未配置时不要使用 mock 对外运营。 +REALNAME_PROVIDER=cloudmarket +REALNAME_CLOUDMARKET_URL=https://sinocheck2.market.alicloudapi.com/fortest/ttttt +REALNAME_CLOUDMARKET_APPCODE= diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..7a3ed8f --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,63 @@ +# 生产部署说明 + +目标访问地址: + +```text +http://hfb.221329.cc.cd:7890 +``` + +生产部署只暴露宿主机 `7890` 端口,避免和服务器已有的 `80`、`443` 服务冲突。后端、MySQL、Redis、MinIO 均通过 Docker 内网通信。 + +## 1. 准备环境变量 + +```bash +cp backend/.env.prod.example backend/.env +``` + +编辑 `backend/.env`,至少替换以下占位值: + +```text +MYSQL_ROOT_PASSWORD +MYSQL_PASSWORD +MYSQL_DSN +JWT_SECRET +MINIO_ROOT_USER +MINIO_ROOT_PASSWORD +STORAGE_ACCESS_KEY_ID +STORAGE_SECRET_ACCESS_KEY +``` + +如果正式对外运营,还需要配置真实短信和实名服务,避免继续使用测试配置。 + +## 2. 启动服务 + +```bash +docker compose -f deploy/docker-compose.prod.yml up -d --build +``` + +## 3. 首次初始化数据库 + +将命令中的 `change-hfb-password` 替换为 `backend/.env` 里的 `MYSQL_PASSWORD`。 + +```bash +for file in backend/migrations/*.sql; do + docker compose -f deploy/docker-compose.prod.yml exec -T mysql \ + mysql -uhfb -pchange-hfb-password hfb_sys < "$file" +done +``` + +## 4. 验证 + +```bash +curl http://127.0.0.1:7890/health +curl http://127.0.0.1:7890/api/health +``` + +浏览器访问: + +```text +http://hfb.221329.cc.cd:7890 +http://hfb.221329.cc.cd:7890/admin/login +``` + +服务器安全组或防火墙需要放行 `7890/tcp`。 diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml index e36eca4..aa7b1b8 100644 --- a/deploy/docker-compose.prod.yml +++ b/deploy/docker-compose.prod.yml @@ -1,12 +1,61 @@ services: + mysql: + image: mysql:8.4 + restart: unless-stopped + env_file: + - ../backend/.env + environment: + TZ: Asia/Shanghai + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$${MYSQL_ROOT_PASSWORD}\""] + interval: 10s + timeout: 5s + retries: 10 + + redis: + image: redis:7.4-alpine + restart: unless-stopped + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 + + minio: + image: minio/minio:RELEASE.2025-09-07T16-13-09Z + restart: unless-stopped + command: server /data --console-address ":9001" + env_file: + - ../backend/.env + environment: + TZ: Asia/Shanghai + volumes: + - minio_data:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 10s + timeout: 5s + retries: 10 + backend: build: context: ../backend restart: unless-stopped env_file: - ../backend/.env - ports: - - "8080:8080" + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + minio: + condition: service_healthy + expose: + - "8080" frontend: build: @@ -16,4 +65,9 @@ services: depends_on: - backend ports: - - "80:80" + - "7890:80" + +volumes: + mysql_data: + redis_data: + minio_data: diff --git a/deploy/nginx/default.conf b/deploy/nginx/default.conf index d18f25c..0061e88 100644 --- a/deploy/nginx/default.conf +++ b/deploy/nginx/default.conf @@ -1,6 +1,8 @@ server { listen 80; - server_name _; + server_name hfb.221329.cc.cd; + + client_max_body_size 20m; root /usr/share/nginx/html; index index.html; @@ -11,14 +13,18 @@ server { location /api/ { proxy_pass http://backend:8080/api/; - proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 3600s; + proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $http_host; } location /health { proxy_pass http://backend:8080/health; - proxy_set_header Host $host; + proxy_set_header Host $http_host; } }