合并 main 分支并融合订单列表最小化与 SQL 治理
- Auth/权限中间件取 main 演进版(错误细分、token 来源),保留 dev 的用户 cookie 与登出撤销语义 - 后台订单列表:融合 main 的首页免 COUNT 优化与关键词索引路由,保留 dev 的最小化 AdminOrderListItemDTO(结账明细仍由详情接口返回,同步调整对应集成测试) - 保留 dev 的 token_version 冻结/解冻递增与 main 的状态变化判断及通知 - 前端:保留 dev 登出接口与 main 的 fetchMe silent 参数、SSE 登录跳转;后台订单表按 dev 最小化 DTO 移除号主到手列
This commit is contained in:
+71
-20
@@ -8,7 +8,7 @@ https://选号网域名 # CADDY_SHOW_DOMAIN(可选),hfb_show 静态
|
||||
https://monitor.主站域名 # Beszel 监控
|
||||
```
|
||||
|
||||
生产部署使用 Caddy 作为公网入口,只暴露宿主机 `80`、`443` 端口。Caddy 会自动申请和续签 HTTPS 证书,并直接托管前端静态资源(主站 `dist` 与可选的选号网 `show-dist` 在构建 Caddy 镜像时打入);后端、MySQL、Redis、MinIO 均通过 Docker 内网通信。选号网与主站**共用同一 backend**,浏览器访问选号网域名下的 `/api/*` 由 Caddy 反代到本机 backend,**不会直连数据库**。
|
||||
生产部署使用 Caddy 作为公网入口,只暴露宿主机 `80`、`443` 端口。Caddy 会自动申请和续签 HTTPS 证书,并直接托管前端静态资源(主站 `dist` 与可选的选号网 `show-dist` 在构建 Caddy 镜像时打入);后端、MySQL、Redis 均通过 Docker 内网通信,图片对象存储使用阿里云 OSS。选号网与主站**共用同一 backend**,浏览器访问选号网域名下的 `/api/*` 由 Caddy 反代到本机 backend,**不会直连数据库**。
|
||||
|
||||
部署前请确认:
|
||||
|
||||
@@ -32,12 +32,16 @@ MYSQL_ROOT_PASSWORD
|
||||
MYSQL_PASSWORD
|
||||
MYSQL_DSN
|
||||
JWT_SECRET
|
||||
MINIO_ROOT_USER
|
||||
MINIO_ROOT_PASSWORD
|
||||
STORAGE_ACCESS_KEY_ID
|
||||
STORAGE_SECRET_ACCESS_KEY
|
||||
BACKEND_UID
|
||||
BACKEND_GID
|
||||
BACKUP_DIR
|
||||
BACKUP_STATUS_DIR
|
||||
BACKUP_RESTORE_DIR
|
||||
BACKUP_OSS_URI
|
||||
BACKUP_PASSPHRASE
|
||||
BACKUP_MYSQL_PASSWORD
|
||||
```
|
||||
|
||||
`BACKEND_UID`、`BACKEND_GID` 填写服务器业务用户的数值 ID,例如:
|
||||
@@ -49,29 +53,76 @@ id -g yml
|
||||
|
||||
后端容器会使用该 UID/GID 运行。部署脚本由 root 执行时会自动把已有日志修正为该属主;非 root 执行且发现旧的 root 日志时,会给出需要执行的 `chown` 命令后停止。
|
||||
|
||||
### 迁移对象存储到阿里云 OSS
|
||||
### 对象存储:阿里云 OSS
|
||||
|
||||
创建私有 Bucket 后,先保持 `STORAGE_*` 指向当前 MinIO,在 `backend/.env` 配置 OSS 镜像端:
|
||||
图片等业务文件存储在私有 OSS Bucket,`backend/.env` 的 `STORAGE_*` 直接指向 OSS:
|
||||
|
||||
```text
|
||||
STORAGE_MIRROR_ENDPOINT=https://oss-cn-hangzhou-internal.aliyuncs.com
|
||||
STORAGE_MIRROR_BUCKET=hfb-sys-assets
|
||||
STORAGE_MIRROR_ACCESS_KEY_ID=change-oss-access-key-id
|
||||
STORAGE_MIRROR_SECRET_ACCESS_KEY=change-oss-access-key-secret
|
||||
STORAGE_MIRROR_REGION=cn-hangzhou
|
||||
STORAGE_MIRROR_BUCKET_LOOKUP=dns
|
||||
STORAGE_ENDPOINT=https://oss-cn-hangzhou-internal.aliyuncs.com
|
||||
STORAGE_BUCKET=hfb-sys-assets
|
||||
STORAGE_ACCESS_KEY_ID=change-oss-access-key-id
|
||||
STORAGE_SECRET_ACCESS_KEY=change-oss-access-key-secret
|
||||
STORAGE_REGION=cn-hangzhou
|
||||
STORAGE_BUCKET_LOOKUP=dns
|
||||
```
|
||||
|
||||
RAM 凭证只授予该 Bucket 的列举、读取、写入对象权限,不要使用阿里云主账号 AccessKey。镜像端启用后,新上传文件会同步写入 MinIO 和 OSS;任一镜像写入失败时上传会失败,避免数据库引用未同步文件。
|
||||
RAM 凭证只授予该 Bucket 的列举、读取、写入对象权限,不要使用阿里云主账号 AccessKey。杭州 ECS 必须使用内网 Endpoint,避免跨公网访问。
|
||||
|
||||
部署支持镜像配置的镜像后,执行以下命令同步历史对象;命令可重复执行,不会删除 MinIO 数据:
|
||||
历史 MinIO 数据已通过 `scripts/migrate-minio-to-oss.sh` 迁移完成,生产编排不再包含 MinIO;旧 MinIO volume 观察期结束后可删除。
|
||||
|
||||
### 数据库在线备份与时间点恢复
|
||||
|
||||
生产 MySQL 开启了 `ROW` 格式 binlog 和崩溃安全刷盘。备份期间业务持续读写:每日用 XtraBackup 做物理全量备份,调度器每分钟检查 binlog 位点,仅在有新增已提交事务时轮转并归档。备份对象先做客户端加密,再由 Bucket 的服务器端加密保护;远端仅在 payload、checksum 和 manifest 都上传成功后写入 `complete.env`。
|
||||
|
||||
部署脚本会创建或更新 `BACKUP_MYSQL_USER`,该用户只有 XtraBackup 所需的全局备份权限和业务库只读权限,不开放远程 root。
|
||||
|
||||
备份 Bucket 必须独立于业务 Bucket,开启“OSS 完全托管”服务器端加密和版本控制。备份 RAM 身份仅授予该 Bucket 的列举、读写对象权限;若填写 `BACKUP_KMS_KEY_ID`,再授予该 Key 的加解密权限。推荐配置不可变保留策略。
|
||||
|
||||
部署机需安装 `ossutil`、`openssl` 和 `flock`,并使用与 cron 相同的业务用户完成 `ossutil` 凭证配置;部署脚本会在启动服务前检查这些依赖。
|
||||
|
||||
```bash
|
||||
./scripts/migrate-minio-to-oss.sh
|
||||
./scripts/migrate-minio-to-oss.sh --verify
|
||||
# 每日全量热备,本地留存并上传 OSS 备份桶
|
||||
./scripts/backup-online.sh full
|
||||
|
||||
# 检查并归档有新增事务的关闭 binlog,配合全量实现时间点恢复
|
||||
./scripts/archive-binlog.sh archive
|
||||
|
||||
# 校验本地状态对应的远端完整对象 / 清理过期本地备份
|
||||
./scripts/archive-binlog.sh verify
|
||||
./scripts/backup-online.sh status
|
||||
./scripts/backup-online.sh prune
|
||||
```
|
||||
|
||||
校验通过后,将 `STORAGE_*` 改为同一组 OSS 配置,再清空所有 `STORAGE_MIRROR_*` 配置并重新部署。杭州 ECS 必须使用 `oss-cn-hangzhou-internal.aliyuncs.com`,避免跨公网访问。MinIO 保留至少 30 天后再考虑下线。
|
||||
后台“数据备份”页面只读取 `${BACKUP_STATUS_DIR}/status.json`;该目录以只读方式挂载给 backend,不包含备份载荷。首次部署后先生成状态摘要:
|
||||
|
||||
```bash
|
||||
./scripts/backup-status.sh write
|
||||
```
|
||||
|
||||
推荐 cron(以部署用户运行,并把日志写到受限目录):
|
||||
|
||||
```cron
|
||||
# 每分钟读取后台定时配置,按需归档 binlog 和执行全量热备。
|
||||
* * * * * cd /srv/hfb_sys && ./scripts/backup-scheduler.sh >> /data/backups/backup-scheduler.log 2>&1
|
||||
15 5 * * * cd /srv/hfb_sys && ./scripts/backup-online.sh prune >> /data/backups/prune.log 2>&1
|
||||
10 5 * * 0 cd /srv/hfb_sys && ./scripts/archive-binlog.sh verify >> /data/backups/archive-verify.log 2>&1
|
||||
```
|
||||
|
||||
全量备份时间、是否启用定时任务和 binlog 间隔在后台“数据备份”页面配置;宿主机 cron 必须保留每分钟的调度器入口。不要将 Docker socket、备份目录或恢复操作暴露给 backend。
|
||||
|
||||
OSS 生命周期建议:`mysql/full/daily/` 30 天、`weekly/` 60 天、`monthly/` 365 天、`mysql/binlog/` 至少 45 天。时间点恢复窗口受最早全量备份和 binlog 保留期共同限制。
|
||||
|
||||
每周必须在隔离目录做一次恢复演练,绝不向生产容器回放:
|
||||
|
||||
```bash
|
||||
# 从 status 输出选取一个带 complete.env 的全量备份 URI。
|
||||
./scripts/restore-online.sh prepare oss://hfb-backup/mysql/full/daily/<server_uuid>/<timestamp>/complete.env
|
||||
./scripts/restore-online.sh fetch-all /data/restore/<server_uuid>/<timestamp>
|
||||
./scripts/restore-online.sh start /data/restore/<server_uuid>/<timestamp>
|
||||
./scripts/restore-online.sh replay /data/restore/<server_uuid>/<timestamp> hfb-restore-<timestamp> '2026-08-16 20:00:00'
|
||||
```
|
||||
|
||||
恢复容器使用 `--network none`,只可通过 `docker exec` 检查。演练结束后手动删除名为 `hfb-restore-*` 的恢复容器和 `/data/restore` 对应目录;生产容器不在恢复工具的可选目标中。
|
||||
|
||||
### 同机部署选号网(hfb_show,可选)
|
||||
|
||||
@@ -108,14 +159,13 @@ HFB_SHOW_DIR=../hfb_show
|
||||
|
||||
脚本会自动完成:
|
||||
|
||||
- 检查 `backend/.env` 是否仍指向 `127.0.0.1`、`localhost` 或占位值。
|
||||
- 使用内置 MinIO 时,检查 `STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY` 是否和 `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` 一致。
|
||||
- 检查 `backend/.env` 是否仍指向 `127.0.0.1`、`localhost`、占位值或已下线的 MinIO。
|
||||
- 构建并启动生产容器。
|
||||
- 使用配置的非 root UID/GID 运行后端,并检查日志目录属主。
|
||||
- 通过 Caddy 自动申请或续签 HTTPS 证书。
|
||||
- 等待 MySQL、Redis、MinIO 就绪。
|
||||
- 等待 MySQL、Redis 就绪。
|
||||
- 按顺序执行尚未应用的数据库迁移。
|
||||
- 重启后端并检查 `https://你的域名/api/health`。
|
||||
- 重启后端并在容器内检查 `http://127.0.0.1:8080/api/health`。默认不依赖公网 DNS、TLS 或 Caddy;如需检查公网入口,可设置 `PROD_HEALTH_URL=https://你的域名/api/health`。
|
||||
|
||||
如需部署完成后直接跟随查看后端日志:
|
||||
|
||||
@@ -166,6 +216,7 @@ docker compose -f deploy/docker-compose.prod.yml run --rm --no-deps backend \
|
||||
|
||||
```bash
|
||||
curl https://主站域名/health
|
||||
curl https://主站域名/health/ready
|
||||
curl https://主站域名/api/health
|
||||
# 若启用了选号网:
|
||||
curl -I https://选号网域名/
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
handle /health {
|
||||
@health path /health /health/ready
|
||||
handle @health {
|
||||
reverse_proxy backend:8080 {
|
||||
import backend_proxy
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
# 第一阶段:构建主站前端静态资源
|
||||
FROM node:24-alpine AS build
|
||||
|
||||
@@ -6,7 +8,8 @@ ENV VITE_ADMIN_BASE_PATH=${VITE_ADMIN_BASE_PATH}
|
||||
|
||||
WORKDIR /src
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm install
|
||||
RUN --mount=type=cache,target=/root/.npm \
|
||||
npm ci --prefer-offline --no-audit --fund=false
|
||||
COPY frontend/ .
|
||||
RUN npm run build
|
||||
|
||||
|
||||
@@ -75,30 +75,6 @@ services:
|
||||
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
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 256M
|
||||
cpus: '0.25'
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ../backend
|
||||
@@ -111,6 +87,8 @@ services:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ../backend/logs:/app/logs
|
||||
# 仅挂载备份状态摘要,后端无法读取备份文件、Docker socket 或 OSS 凭证文件。
|
||||
- ${BACKUP_STATUS_DIR:-/data/backups/status}:/var/run/hfb-backup-status:ro
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
@@ -124,10 +102,14 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
expose:
|
||||
- "8080"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -q -O /dev/null -T 5 http://127.0.0.1:8080/api/health"]
|
||||
interval: 10s
|
||||
timeout: 6s
|
||||
retries: 12
|
||||
start_period: 10s
|
||||
|
||||
beszel:
|
||||
image: henrygd/beszel:0.18.7
|
||||
@@ -189,7 +171,6 @@ volumes:
|
||||
caddy_config:
|
||||
mysql_data:
|
||||
redis_data:
|
||||
minio_data:
|
||||
beszel_data:
|
||||
beszel_socket:
|
||||
beszel_agent_data:
|
||||
|
||||
@@ -8,3 +8,19 @@ default-character-set = utf8mb4
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
init-connect = 'SET NAMES utf8mb4'
|
||||
|
||||
# 生产慢查询观测:应用日志记录 SQL 形状,MySQL 原生日志记录超过阈值的语句。
|
||||
# performance_schema 默认开启,使用 events_statements_summary_by_digest 做聚合分析。
|
||||
slow_query_log = ON
|
||||
long_query_time = 0.5
|
||||
log_slow_admin_statements = ON
|
||||
|
||||
# 时间点恢复基础:binlog 由 scripts/archive-binlog.sh 每分钟归档到 OSS 备份桶。
|
||||
# 本地按 7 天自动过期,归档与本地保留策略见 scripts/backup-online.sh。
|
||||
server-id = 1
|
||||
log_bin = mysql-bin
|
||||
binlog_format = ROW
|
||||
binlog_row_image = FULL
|
||||
binlog_expire_logs_seconds = 604800
|
||||
sync_binlog = 1
|
||||
innodb_flush_log_at_trx_commit = 1
|
||||
|
||||
Reference in New Issue
Block a user