diff --git a/.github/workflows/infinity-evolution.yml b/.github/workflows/infinity-evolution.yml index dad4d7f3..4c2ec1e7 100644 --- a/.github/workflows/infinity-evolution.yml +++ b/.github/workflows/infinity-evolution.yml @@ -31,10 +31,16 @@ on: - status - send-monthly-reset - send-update-notify + - send-update-notify-single - send-traffic-warn + - list-user-emails default: 'status' message: - description: '附加消息 (update-notify描述 / traffic-warn百分比)' + description: '附加消息 (update-notify描述 / traffic-warn百分比) · 描述支持分号分隔多条内容' + required: false + type: string + email: + description: '目标邮箱 (send-update-notify-single时需要 · 可先用list-user-emails查看)' required: false type: string @@ -67,6 +73,7 @@ jobs: env: ACTION: ${{ github.event.inputs.action || 'monthly-evolution' }} MESSAGE: ${{ github.event.inputs.message || '' }} + EMAIL: ${{ github.event.inputs.email || '' }} ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }} ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }} run: | @@ -143,7 +150,7 @@ jobs: ;; send-update-notify) - echo "发送更新通知..." + echo "一键发送更新通知 (全部用户)..." $SSH_CMD " cd $PROXY_DIR export ZY_SMTP_USER='$ZY_SMTP_USER' @@ -152,6 +159,25 @@ jobs: " ;; + send-update-notify-single) + if [ -z "$EMAIL" ]; then + echo "❌ 请提供目标邮箱 (email字段)" + echo "💡 可先使用 list-user-emails 查看所有用户邮箱" + exit 1 + fi + if ! echo "$EMAIL" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then + echo "❌ 邮箱格式不正确: $EMAIL" + exit 1 + fi + echo "单独发送更新通知给: $EMAIL ..." + $SSH_CMD " + cd $PROXY_DIR + export ZY_SMTP_USER='$ZY_SMTP_USER' + export ZY_SMTP_PASS='$ZY_SMTP_PASS' + node service/email-hub.js update-notify-single '$EMAIL' '${MESSAGE:-系统已更新}' + " + ;; + send-traffic-warn) echo "发送流量预警..." $SSH_CMD " @@ -162,6 +188,14 @@ jobs: " ;; + list-user-emails) + echo "查询所有用户邮箱..." + $SSH_CMD " + cd $PROXY_DIR + node service/email-hub.js list-emails + " + ;; + *) echo "❌ 未知操作: $ACTION" exit 1 diff --git a/server/proxy/service/email-hub.js b/server/proxy/service/email-hub.js index b53329aa..11482449 100644 --- a/server/proxy/service/email-hub.js +++ b/server/proxy/service/email-hub.js @@ -19,10 +19,12 @@ // // 用法: // node email-hub.js monthly-reset — 发送月初重置邮件给所有用户 -// node email-hub.js update-notify — 发送更新通知给所有用户 +// node email-hub.js update-notify — 一键发送更新通知给所有用户 +// node email-hub.js update-notify-single — 发送更新通知给单个用户 // node email-hub.js traffic-warn — 发送流量预警给所有用户 // node email-hub.js security-warn — 发送安全提醒给单用户 // node email-hub.js feedback-ack — 发送反馈确认给单用户 +// node email-hub.js list-emails — 列出所有启用用户的邮箱 // // 运行方式: CLI调用 (由auto-evolution.js调度) // ═══════════════════════════════════════════════ @@ -270,14 +272,26 @@ function generateMonthlyResetEmail(config) { function generateUpdateNotifyEmail(description, config) { const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }); + // 支持用分号或换行符分隔的多条更新内容,自动渲染为功能清单 + const items = description.split(/[;\n]/).map(s => s.trim()).filter(Boolean); + let detailHtml; + if (items.length > 1) { + detailHtml = ` +
    + ${items.map(item => `
  • ${escapeHtml(item)}
  • `).join('\n ')} +
`; + } else { + detailHtml = `

${escapeHtml(description).replace(/\n/g, '
')}

`; + } + const content = `
🔄 系统已完成升级

📋 本次更新内容

-
- ${escapeHtml(description).replace(/\n/g, '
')} +
+ ${detailHtml}

@@ -519,6 +533,36 @@ async function sendFeedbackAckEmail(email) { } } +/** + * 发送更新通知给单个用户 (分别发送模式) + * @param {string} email 目标邮箱 + * @param {string} description 更新说明 + */ +async function sendUpdateNotifySingleEmail(email, description) { + const config = loadConfig(); + const html = generateUpdateNotifyEmail(description, config); + + try { + await sendEmail(email, '🌐 光湖语言世界 · 系统已升级', html); + console.log(`[邮件中枢] ✅ 更新通知已发送: ${email}`); + logEmail('update-notify-single', email, true, null); + return { sent: 1, failed: 0 }; + } catch (err) { + console.error(`[邮件中枢] ❌ 更新通知发送失败: ${err.message}`); + logEmail('update-notify-single', email, false, err.message); + return { sent: 0, failed: 1 }; + } +} + +/** + * 列出所有启用用户的邮箱 (供下拉选择) + * @returns {string[]} 邮箱列表 + */ +function listUserEmails() { + const users = getEnabledUsers(); + return users.map(u => u.email); +} + // ═══════════════════════════════════════════════ // CLI 主入口 // ═══════════════════════════════════════════════ @@ -528,11 +572,16 @@ async function main() { if (!action) { console.log('📧 光湖语言世界 · 邮件通信中枢'); console.log('用法:'); - console.log(' node email-hub.js monthly-reset — 月初重置通知'); - console.log(' node email-hub.js update-notify <描述> — 更新升级通知'); - console.log(' node email-hub.js traffic-warn <百分比> — 流量预警通知'); - console.log(' node email-hub.js security-warn <邮箱> <消息> — 安全风险提醒'); - console.log(' node email-hub.js feedback-ack <邮箱> — 反馈确认回复'); + console.log(' node email-hub.js monthly-reset — 月初重置通知 (全部用户)'); + console.log(' node email-hub.js update-notify <描述> — 一键发送更新通知 (全部用户)'); + console.log(' node email-hub.js update-notify-single <邮箱> <描述> — 单独发送更新通知'); + console.log(' node email-hub.js traffic-warn <百分比> — 流量预警通知 (全部用户)'); + console.log(' node email-hub.js security-warn <邮箱> <消息> — 安全风险提醒 (单用户)'); + console.log(' node email-hub.js feedback-ack <邮箱> — 反馈确认回复 (单用户)'); + console.log(' node email-hub.js list-emails — 列出所有用户邮箱'); + console.log(''); + console.log('💡 描述支持分号分隔多条内容,自动渲染为功能清单:'); + console.log(' node email-hub.js update-notify "新增智能选路;优化连接速度;修复断连问题"'); process.exit(0); } @@ -549,7 +598,18 @@ async function main() { process.exit(1); } const result = await sendUpdateNotifyEmail(arg1); - console.log(`📧 更新通知: ${result.sent}成功 / ${result.failed}失败`); + console.log(`📧 更新通知 (一键发送): ${result.sent}成功 / ${result.failed}失败`); + break; + } + + case 'update-notify-single': { + if (!arg1 || !arg2) { + console.error('❌ 请提供邮箱和更新描述'); + console.error('用法: node email-hub.js update-notify-single <邮箱> <描述>'); + process.exit(1); + } + const result = await sendUpdateNotifySingleEmail(arg1, arg2); + console.log(`📧 更新通知 (单独发送): ${result.sent}成功 / ${result.failed}失败`); break; } @@ -582,6 +642,19 @@ async function main() { break; } + case 'list-emails': { + const emails = listUserEmails(); + if (emails.length === 0) { + console.log('[邮件中枢] 暂无启用用户'); + } else { + console.log(`📧 当前启用用户邮箱 (${emails.length}位):`); + emails.forEach((email, i) => { + console.log(` ${i + 1}. ${email}`); + }); + } + break; + } + default: console.error(`❌ 未知操作: ${action}`); process.exit(1); @@ -592,10 +665,12 @@ async function main() { module.exports = { sendMonthlyResetEmail, sendUpdateNotifyEmail, + sendUpdateNotifySingleEmail, sendTrafficWarnEmail, sendSecurityWarnEmail, sendFeedbackAckEmail, getEnabledUsers, + listUserEmails, sendEmail };