/** * 邮件 SMTP 连接测试脚本 * 测试 Gmail SMTP 465 端口连接和认证 */ const nodemailer = require('nodemailer') const path = require('path') // 加载环境变量 const fs = require('fs') const localCredsPath = path.join(process.env.USERPROFILE || 'C:\\Users\\Administrator', 'Toolbox_local_creds.env.local') if (fs.existsSync(localCredsPath)) { fs.readFileSync(localCredsPath, 'utf-8') .split('\n') .filter(l => l.trim() && !l.startsWith('#')) .forEach(line => { const [key, ...valParts] = line.split('=') const val = valParts.join('=').trim().replace(/^["']|["']$/g, '') if (key && val) process.env[key.trim()] = val }) } // 也加载项目根目录的 .env(如果存在) const projectEnvPath = path.join(__dirname, '..', '..', '..', '..', '.env') if (fs.existsSync(projectEnvPath)) { fs.readFileSync(projectEnvPath, 'utf-8') .split('\n') .filter(l => l.trim() && !l.startsWith('#')) .forEach(line => { const [key, ...valParts] = line.split('=') const val = valParts.join('=').trim().replace(/^["']|["']$/g, '') if (key && val) process.env[key.trim()] = val }) } const smtpHost = process.env.EMAIL_SENDER_SMTP_HOST || 'smtp.gmail.com' const smtpUser = process.env.EMAIL_SENDER_SMTP_USER || '' const smtpPass = process.env.EMAIL_SENDER_SMTP_PASS || '' console.log('=== Email Sender SMTP 连接测试 ===\n') console.log(`SMTP Host: ${smtpHost}`) console.log(`SMTP User: ${smtpUser || '(未配置)'}`) console.log(`SMTP Pass: ${smtpPass ? smtpPass.substring(0, 4) + '****' + smtpPass.substring(smtpPass.length - 2) : '(未配置)'}`) console.log(`\n`) if (!smtpUser || !smtpPass) { console.error('❌ 错误: EMAIL_SENDER_SMTP_USER 或 EMAIL_SENDER_SMTP_PASS 未配置') console.error('请在 Toolbox_local_creds.env.local 中配置这两个环境变量') process.exit(1) } async function testPort(port, secure) { const mode = secure ? 'SSL (465)' : 'STARTTLS (587)' console.log(`\n--- 测试端口 ${port} (${mode}) ---\n`) // DNS console.log('[1/3] 测试 DNS 解析...') try { const dns = require('dns') const addresses = await dns.promises.resolve(smtpHost) console.log(`✅ ${smtpHost} -> ${addresses.join(', ')}\n`) } catch (e) { console.error(`❌ DNS 解析失败: ${e.message}\n`) return false } // TCP console.log(`[2/3] 测试 TCP 连接到 ${smtpHost}:${port}...`) const net = require('net') const tcpTimeout = new Promise((_, reject) => { setTimeout(() => reject(new Error('TCP 连接超时 (10s)')), 10000) }) const tcpConnect = new Promise((resolve, reject) => { const socket = net.createConnection({ host: smtpHost, port }) socket.on('connect', () => { socket.end() resolve() }) socket.on('error', reject) }) try { await Promise.race([tcpConnect, tcpTimeout]) console.log(`✅ TCP 连接成功\n`) } catch (e) { console.error(`❌ TCP 连接失败: ${e.message}\n`) return false } // SMTP console.log('[3/3] 测试 SMTP 认证和发信...') const transporter = nodemailer.createTransport({ host: smtpHost, port, secure, auth: { user: smtpUser, pass: smtpPass }, connectionTimeout: 15000, socketTimeout: 30000, greetingTimeout: 15000 }) try { console.log(' → 验证 SMTP 连接...') await transporter.verify() console.log(' ✅ SMTP 验证成功\n') console.log(' → 尝试发送测试邮件...') const info = await transporter.sendMail({ from: smtpUser, to: smtpUser, subject: 'TRAE-Toolbox SMTP 测试', text: `这是一封自动测试邮件\n发送时间: ${new Date().toISOString()}\n如果收到此邮件,说明 SMTP 配置正常。` }) console.log(` ✅ 邮件发送成功!`) console.log(` Message ID: ${info.messageId}`) console.log(` Response: ${info.response}`) } catch (e) { console.error(`❌ SMTP 操作失败: ${e.message}`) return false } finally { transporter.close() } return true } async function test() { // 先试 465 SSL const ok465 = await testPort(465, true) if (ok465) { console.log('\n=== 465 SSL 测试通过 ✅ ===') return } console.log('\n465 SSL 失败,尝试 587 STARTTLS...') // 再试 587 STARTTLS const ok587 = await testPort(587, false) if (ok587) { console.log('\n=== 587 STARTTLS 测试通过 ✅ ===') console.log('\n建议: 将 EMAIL_SENDER_SMTP_PORT 改为 587,EMAIL_SENDER_SMTP_SECURE 设为 false') return } console.log('\n=== 两个端口都失败了 ❌ ===') console.log('\n可能原因:') console.log('1. Gmail 限制了当前 IP 的 SMTP 访问') console.log('2. 企业防火墙/安全组阻断了 Gmail SMTP') console.log('3. 需要检查 Gmail 账号安全设置') process.exit(1) } test().catch(e => { console.error('未预期错误:', e) process.exit(1) })