fix(api): 修复邮件队列超时并补齐调度进程
延长 SendEmailJob 超时并改为超时直接失败,补充重试退避、 失败日志与收件人脱敏,避免 send_email 队列批量超时重试。 新增 MAIL_TIMEOUT 与 QUEUE_RETRY_AFTER 配置,并抽出邮件运行时 配置与 HTML 内容服务,确保 Horizon 常驻进程使用最新邮件配置。 为 Docker、supervisor 与 compose 样例补齐 scheduler 进程,并在 节点管理端开启墙检测托管时立即触发一次检测,保证定时任务持续生效。
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
| 模块名 | 说明 | 最近更新 |
|
||||
|--------|------|----------|
|
||||
| [admin-frontend](admin-frontend.md) | 管理端前端登录、布局、仪表盘、用户管理、节点管理与管理 API 封装 | 2026-04-28 |
|
||||
| [deploy](deploy.md) | 可复制到服务器的 Xboard Compose 部署模板、环境变量模板和运维脚本 | 2026-04-28 |
|
||||
| [node-gfw-check](node-gfw-check.md) | 节点墙状态检测任务、父/子节点继承规则、mi-node 检测上报链路 | 2026-04-28 |
|
||||
| [order-payment](order-payment.md) | 订单支付成功快照、第三方回调元信息透传与后台支付成功信息展示 | 2026-04-25 |
|
||||
| [queue-mail](queue-mail.md) | 邮件发送队列、SMTP 运行时配置、Horizon 超时与失败重试边界 | 2026-04-28 |
|
||||
| [subscription-protocols](subscription-protocols.md) | 用户订阅导出入口、协议适配器与 Stash / Clash 系列兼容过滤 | 2026-04-24 |
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# deploy
|
||||
|
||||
## 职责
|
||||
|
||||
- 维护可复制到服务器的一键部署模板
|
||||
- 收敛 Docker Compose 服务拓扑、环境变量模板、运行目录初始化脚本和常用运维命令
|
||||
- 为依赖 Laravel Scheduler 的后台任务提供明确的部署进程入口
|
||||
|
||||
## 行为规范
|
||||
|
||||
- `deploy/xboard-server/` 是面向服务器复制部署的自包含目录,不依赖仓库根目录的 compose 样例
|
||||
- `compose.yaml` 默认包含 `web / horizon / scheduler / admin / ws-server / redis` 六个服务
|
||||
- `scheduler` 服务固定执行 `php artisan schedule:work`,用于持续触发 `sync:server-gfw-checks`、`sync:server-auto-online` 和其他 Laravel Scheduler 任务
|
||||
- 模板默认使用外部 MySQL,不在 compose 中创建数据库服务,避免改变现有生产拓扑
|
||||
- `.env.example` 同时覆盖 Docker Compose 变量和 Laravel 运行变量,但不得包含真实 `APP_KEY`、数据库密码、邮箱密码或真实业务域名
|
||||
- `scripts/init.sh` 只创建挂载目录并在 `.env` 不存在时复制模板,不执行数据库迁移
|
||||
- `scripts/deploy.sh` 只负责初始化、拉取镜像和启动服务,不自动执行生产数据库迁移
|
||||
- `scripts/update.sh --migrate` 才会显式执行 `php artisan migrate --force`
|
||||
- `scripts/status.sh` 输出 compose 状态、scheduler 日志、`schedule:list` 结果和手动墙检测同步命令
|
||||
|
||||
## 依赖关系
|
||||
|
||||
- 依赖 `ghcr.io/micah123321/xboard:new` 作为后端默认镜像
|
||||
- 依赖 `ghcr.io/micah123321/xboard-admin-frontend:new` 作为管理端默认镜像
|
||||
- 依赖 `redis:8-alpine` 提供 `/data/redis.sock`
|
||||
- 依赖外部 MySQL,由 `.env` 中的 `DB_*` 配置提供
|
||||
- 依赖 `admin-frontend/Caddyfile` 支持 `XBOARD_BACKEND_UPSTREAM` 和 `XBOARD_UPLOAD_UPSTREAM`
|
||||
- 依赖 `app/Console/Kernel.php` 注册 `sync:server-gfw-checks` 等定时任务
|
||||
@@ -14,8 +14,9 @@
|
||||
- `server_gfw_checks.status` 使用 `pending / checking / normal / blocked / partial / failed / skipped`
|
||||
- 管理端 `POST server/manage/checkGfw` 接收 `{ ids: number[] }`,响应中区分 `started` 与 `skipped`
|
||||
- 后端定时命令 `sync:server-gfw-checks` 会自动为 `gfw_check_enabled=1` 的父节点创建检测任务;已有未超时的 `pending/checking` 任务时跳过,超过 5 分钟未领取或未上报的任务会自动标记为 `failed`
|
||||
- Docker all-in-one 镜像通过 supervisor 独立运行 `php artisan schedule:work`;`compose.sample.yaml` 的分进程样例和 `deploy/xboard-server/compose.yaml` 服务器部署模板也包含 `scheduler` 服务,确保 `sync:server-gfw-checks` 和其他 Laravel Scheduler 任务会持续执行
|
||||
- 节点端 `GET server/gfw/task` 只向父节点返回待执行任务;节点端 `POST server/gfw/report` 必须校验 `check_id` 归属当前节点
|
||||
- `v2_server.gfw_check_enabled` 控制节点是否参与自动墙检测与墙状态自动显隐;父节点开启时会自动创建检测任务,子节点不独立检测但可单独关闭随父节点自动隐藏 / 恢复
|
||||
- `v2_server.gfw_check_enabled` 控制节点是否参与自动墙检测与墙状态自动显隐;管理端开启父节点墙检测托管时会立即发起一次检测,后续由定时命令持续检测;子节点不独立检测但可单独关闭随父节点自动隐藏 / 恢复
|
||||
- `blocked` 结果会自动隐藏仍开启墙检测托管且当前显示中的父节点及其子节点,并设置 `gfw_auto_hidden=1`
|
||||
- `normal` 结果只恢复 `gfw_auto_hidden=1` 的节点,避免误恢复管理员手动隐藏的节点;`partial/failed` 只记录状态,不触发自动上线或下线
|
||||
- `sync:server-auto-online` 会把最新墙状态 `blocked` 和未恢复的 `gfw_auto_hidden` 作为显示否决条件,防止自动上线重新发布疑似被墙节点
|
||||
@@ -32,6 +33,7 @@
|
||||
- 依赖 `app/Http/Controllers/V2/Server/ServerController.php` 暴露节点端任务领取和上报接口
|
||||
- 依赖 `app/Services/NodeSyncService.php` 与 Workerman WS 通道向在线节点推送 `gfw.check`
|
||||
- 依赖 `app/Console/Commands/SyncServerGfwChecks.php` 与 Laravel Scheduler 自动创建检测任务
|
||||
- 依赖 `.docker/supervisor/supervisord.conf`、`deploy/xboard-server/compose.yaml` 中的 `scheduler` 服务,或部署环境中的 `schedule:work` / `cron + schedule:run` 持续驱动 Laravel Scheduler
|
||||
- 依赖 `app/Services/ServerAutoOnlineService.php` 在自动上线同步时尊重墙状态否决
|
||||
- 依赖 `E:/code/go/mi-node/internal/gfwcheck` 执行 ping 检测和结果判定
|
||||
- 依赖 `E:/code/go/mi-node/internal/panel`、`internal/controlplane` 与 `internal/service` 接收任务、轮询兜底并上报结果
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# queue-mail
|
||||
|
||||
## 职责
|
||||
|
||||
- 承接注册验证码、登录链接、工单通知、订阅到期/流量提醒和后台群发邮件的异步发送。
|
||||
- 通过 `App\Jobs\SendEmailJob` 统一进入 `send_email` 或 `send_email_mass` 队列。
|
||||
- 通过 `App\Services\MailService` 统一渲染邮件模板、应用运行时 SMTP 配置、发送邮件并写入 `MailLog`。
|
||||
|
||||
## 行为规范
|
||||
|
||||
- `SendEmailJob` 的默认 `timeout` 为 60 秒,`tries` 为 3,`backoff()` 为 `[60, 300]`。
|
||||
- `SendEmailJob::$failOnTimeout = true`,超时作业应直接失败,避免同一封邮件在不确定是否已发出的情况下反复重试。
|
||||
- 邮件发送返回错误时,`SendEmailJob` 抛出 `RuntimeException`,由 Laravel Queue/Horizon 统一处理重试和失败记录;不再手动 `release(60)`。
|
||||
- SMTP 传输超时由 `MAIL_TIMEOUT` 控制,默认 30 秒;`QUEUE_RETRY_AFTER` 默认 90 秒,必须大于邮件 job timeout。
|
||||
- Horizon 长驻 worker 每次发送前会通过 `MailRuntimeConfig` 应用后台邮件配置,并刷新已解析 mailer,避免后台 SMTP 配置变更后仍使用旧连接。
|
||||
- `MailLog.config` 只保存脱敏后的邮件配置,`password`、`secret`、`token`、`key` 字段不得以明文持久化。
|
||||
- `send_email_mass` 队列仍会在邮件正文追加 `[Send-Time: ...]` 标记,用于区分批量发送内容。
|
||||
|
||||
## 依赖关系
|
||||
|
||||
- 队列配置: `config/queue.php`
|
||||
- Horizon supervisor: `config/horizon.php`
|
||||
- 邮件配置: `config/mail.php`
|
||||
- 运行时配置: `App\Services\MailRuntimeConfig`
|
||||
- HTML 通知内容: `App\Services\MailHtmlContent`
|
||||
- 邮件日志模型: `App\Models\MailLog`
|
||||
|
||||
## 验证要点
|
||||
|
||||
- `SendEmailJob::$timeout` 小于 `config('queue.connections.redis.retry_after')`。
|
||||
- `MAIL_TIMEOUT` 小于 `SendEmailJob::$timeout`,确保网络层先于 job 层超时。
|
||||
- 单测应覆盖 job 超时/backoff、邮件错误抛出、批量邮件发送时间标记和 `MailLog` 配置脱敏。
|
||||
Reference in New Issue
Block a user