Files
eduEmail-cloudflare/script.js
wojiadexiaoming-copy d800cdd503 Add files via upload
2025-08-01 15:40:44 +08:00

710 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 全局变量
let config = {
cloudflare: {
api_token: '',
zone_id: '',
domain: ''
},
qq_email: {
username: '',
password: '',
smtp_server: 'smtp.qq.com',
smtp_port: 465,
imap_server: 'imap.qq.com',
imap_port: 993
},
forward_settings: {
check_interval: 10,
max_temp_emails: 10,
send_notification: false
}
};
// 环境变量映射
const ENV_VARS = {
'TEMP_EMAIL_QQ_USERNAME': 'qq_email.username',
'TEMP_EMAIL_QQ_PASSWORD': 'qq_email.password',
'TEMP_EMAIL_CF_API_TOKEN': 'cloudflare.api_token',
'TEMP_EMAIL_CF_ZONE_ID': 'cloudflare.zone_id',
'TEMP_EMAIL_DOMAIN': 'cloudflare.domain',
'TEMP_EMAIL_CHECK_INTERVAL': 'forward_settings.check_interval'
};
let tempEmails = {};
let isMonitoring = false;
let monitorInterval = null;
let startTime = null;
let receivedEmailCount = 0;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
loadConfigFromEnv();
updateStats();
updateEmailList();
});
// 从环境变量加载配置
async function loadConfigFromEnv() {
try {
// 尝试从不同来源获取环境变量
await loadFromProcessEnv();
await loadFromServerEnv();
await loadFromLocalStorage();
// 更新状态显示
updateConfigStatus();
// 加载保存的临时邮箱
const savedEmails = localStorage.getItem('tempEmails');
if (savedEmails) {
tempEmails = JSON.parse(savedEmails);
}
} catch (error) {
console.error('加载配置失败:', error);
showNotification('配置加载失败,请检查环境变量设置', 'error');
}
}
// 从 process.env 加载Node.js环境
function loadFromProcessEnv() {
return new Promise((resolve) => {
try {
if (typeof process !== 'undefined' && process.env) {
for (const [envVar, configPath] of Object.entries(ENV_VARS)) {
const value = process.env[envVar];
if (value) {
setConfigValue(configPath, value);
}
}
}
} catch (error) {
console.log('process.env 不可用');
}
resolve();
});
}
// 从服务器端点加载环境变量
async function loadFromServerEnv() {
try {
const response = await fetch('/api/env');
if (response.ok) {
const envData = await response.json();
for (const [envVar, configPath] of Object.entries(ENV_VARS)) {
const value = envData[envVar];
if (value) {
setConfigValue(configPath, value);
}
}
}
} catch (error) {
console.log('服务器环境变量不可用');
}
}
// 从本地存储加载(作为后备)
function loadFromLocalStorage() {
const savedConfig = localStorage.getItem('tempEmailConfig');
if (savedConfig) {
const saved = JSON.parse(savedConfig);
config = { ...config, ...saved };
}
}
// 设置配置值
function setConfigValue(path, value) {
const keys = path.split('.');
let obj = config;
for (let i = 0; i < keys.length - 1; i++) {
if (!obj[keys[i]]) obj[keys[i]] = {};
obj = obj[keys[i]];
}
// 处理数字类型
if (path === 'forward_settings.check_interval') {
obj[keys[keys.length - 1]] = parseInt(value) || 10;
} else {
obj[keys[keys.length - 1]] = value;
}
}
// 更新配置状态显示
function updateConfigStatus() {
const statusItems = [
{
id: 'qqEmail',
value: config.qq_email.username,
label: '邮箱地址'
},
{
id: 'cfApi',
value: config.cloudflare.api_token,
label: 'API Token'
},
{
id: 'domain',
value: config.cloudflare.domain,
label: '域名'
}
];
let allConfigured = true;
statusItems.forEach(item => {
const statusElement = document.getElementById(`${item.id}Status`);
const indicatorElement = document.getElementById(`${item.id}Indicator`);
if (item.value && item.value.trim()) {
if (item.id === 'qqEmail') {
statusElement.textContent = item.value;
} else if (item.id === 'cfApi') {
statusElement.textContent = `${item.value.substring(0, 8)}...`;
} else {
statusElement.textContent = item.value;
}
indicatorElement.textContent = '✅';
indicatorElement.style.color = '#28a745';
} else {
statusElement.textContent = `未配置 ${item.label}`;
indicatorElement.textContent = '❌';
indicatorElement.style.color = '#dc3545';
allConfigured = false;
}
});
// 更新系统状态
const systemStatusElement = document.getElementById('systemStatus');
const systemIndicatorElement = document.getElementById('systemIndicator');
if (allConfigured) {
systemStatusElement.textContent = '配置完成,系统就绪';
systemIndicatorElement.textContent = '✅';
systemIndicatorElement.style.color = '#28a745';
} else {
systemStatusElement.textContent = '配置不完整,请设置环境变量';
systemIndicatorElement.textContent = '⚠️';
systemIndicatorElement.style.color = '#ffc107';
}
}
// 刷新配置
async function refreshConfig() {
showNotification('正在刷新配置...', 'info');
await loadConfigFromEnv();
showNotification('配置已刷新', 'success');
}
// 显示环境变量帮助
function showEnvHelp() {
const modal = document.createElement('div');
modal.className = 'env-help-modal';
modal.innerHTML = `
<div class="env-help-content">
<button class="env-help-close" onclick="this.parentElement.parentElement.remove()">×</button>
<h2 style="color: #4facfe; margin-bottom: 20px;">
<i class="fas fa-info-circle"></i> 环境变量配置说明
</h2>
<p style="margin-bottom: 20px; color: #666;">
系统需要以下环境变量来正常运行。请在您的系统中设置这些变量:
</p>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_QQ_USERNAME</div>
<div class="env-var-desc">QQ邮箱地址例如[email protected]</div>
</div>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_QQ_PASSWORD</div>
<div class="env-var-desc">QQ邮箱授权码不是QQ密码在QQ邮箱设置中生成</div>
</div>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_CF_API_TOKEN</div>
<div class="env-var-desc">Cloudflare API Token需要Zone:Edit权限</div>
</div>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_CF_ZONE_ID</div>
<div class="env-var-desc">Cloudflare Zone ID在域名概览页面可以找到</div>
</div>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_DOMAIN</div>
<div class="env-var-desc">用于临时邮箱的域名例如example.com</div>
</div>
<div class="env-var">
<div class="env-var-name">TEMP_EMAIL_CHECK_INTERVAL</div>
<div class="env-var-desc">邮件检查间隔默认为10秒</div>
</div>
<h3 style="color: #4facfe; margin: 25px 0 15px 0;">设置方法:</h3>
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
<strong>Windows:</strong><br>
<code style="background: #e9ecef; padding: 2px 5px; border-radius: 3px;">
set [email protected]
</code>
</div>
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
<strong>Linux/Mac:</strong><br>
<code style="background: #e9ecef; padding: 2px 5px; border-radius: 3px;">
export [email protected]
</code>
</div>
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
<strong>.env 文件:</strong><br>
<code style="background: #e9ecef; padding: 2px 5px; border-radius: 3px;">
[email protected]
</code>
</div>
<div style="margin-top: 20px; padding: 15px; background: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;">
<strong>注意:</strong> 设置环境变量后需要重启应用程序或刷新页面。
</div>
</div>
`;
document.body.appendChild(modal);
// 点击模态框外部关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.remove();
}
});
}
// 创建临时邮箱
async function createTempEmail() {
if (!validateConfig()) {
showNotification('系统配置不完整,请检查环境变量设置', 'error');
showEnvHelp();
return;
}
const emailPrefix = 'temp_' + Math.random().toString(36).substr(2, 8);
const tempEmail = `${emailPrefix}@${config.cloudflare.domain}`;
try {
// 模拟API调用创建Cloudflare转发规则
showNotification('正在创建临时邮箱...', 'info');
// 这里应该调用Cloudflare API由于是纯前端我们模拟成功
await simulateApiCall();
tempEmails[tempEmail] = {
rule_id: 'rule_' + Math.random().toString(36).substr(2, 8),
created_at: new Date().toISOString(),
last_checked: null
};
localStorage.setItem('tempEmails', JSON.stringify(tempEmails));
updateEmailList();
updateStats();
showNotification(`成功创建临时邮箱: ${tempEmail}`, 'success');
// 复制到剪贴板
navigator.clipboard.writeText(tempEmail).then(() => {
showNotification('邮箱地址已复制到剪贴板', 'info');
});
} catch (error) {
showNotification('创建临时邮箱失败: ' + error.message, 'error');
}
}
// 删除选中的邮箱
function deleteSelectedEmails() {
const checkboxes = document.querySelectorAll('.email-checkbox:checked');
if (checkboxes.length === 0) {
showNotification('请选择要删除的邮箱', 'warning');
return;
}
if (confirm(`确定要删除 ${checkboxes.length} 个临时邮箱吗?`)) {
checkboxes.forEach(checkbox => {
const email = checkbox.value;
delete tempEmails[email];
});
localStorage.setItem('tempEmails', JSON.stringify(tempEmails));
updateEmailList();
updateStats();
showNotification(`已删除 ${checkboxes.length} 个临时邮箱`, 'success');
}
}
// 切换监控状态
function toggleMonitor() {
if (isMonitoring) {
stopMonitor();
} else {
startMonitor();
}
}
// 开始监控
function startMonitor() {
if (Object.keys(tempEmails).length === 0) {
showNotification('请先创建临时邮箱', 'warning');
return;
}
if (!validateConfig()) {
showNotification('系统配置不完整,请检查环境变量设置', 'error');
showEnvHelp();
return;
}
isMonitoring = true;
startTime = new Date();
// 更新UI
document.getElementById('statusDot').classList.add('active');
document.getElementById('statusText').textContent = '监控运行中...';
document.getElementById('monitorBtn').innerHTML = '<i class="fas fa-stop"></i> 停止监控';
document.getElementById('monitorBtn').className = 'btn btn-danger';
document.getElementById('monitorLog').classList.remove('hidden');
// 开始监控循环
monitorInterval = setInterval(checkEmails, config.forward_settings.check_interval * 1000);
// 开始运行时间计时
updateRunTime();
addLog('开始监控临时邮箱...');
addLog(`检查间隔: ${config.forward_settings.check_interval}`);
addLog(`监控邮箱数量: ${Object.keys(tempEmails).length}`);
updateStats();
showNotification('邮件监控已启动', 'success');
}
// 停止监控
function stopMonitor() {
isMonitoring = false;
if (monitorInterval) {
clearInterval(monitorInterval);
monitorInterval = null;
}
// 更新UI
document.getElementById('statusDot').classList.remove('active');
document.getElementById('statusText').textContent = '监控已停止';
document.getElementById('monitorBtn').innerHTML = '<i class="fas fa-play"></i> 开始监控';
document.getElementById('monitorBtn').className = 'btn btn-success';
const endTime = new Date();
const duration = Math.floor((endTime - startTime) / 1000);
addLog(`监控已停止 (运行了 ${formatTime(duration)})`);
updateStats();
showNotification('邮件监控已停止', 'info');
}
// 检查一次邮件
async function checkOnce() {
if (!validateConfig()) {
showNotification('系统配置不完整,请检查环境变量设置', 'error');
showEnvHelp();
return;
}
if (Object.keys(tempEmails).length === 0) {
showNotification('请先创建临时邮箱', 'warning');
return;
}
showNotification('正在检查邮件...', 'info');
await checkEmails();
}
// 检查邮件(核心功能)
async function checkEmails() {
try {
addLog(`[${new Date().toLocaleTimeString()}] 🔍 检查邮件中...`);
// 模拟检查邮件的过程
await simulateApiCall(1000);
// 随机生成一些测试邮件实际应用中这里会调用IMAP API
const hasNewEmail = Math.random() < 0.1; // 10%概率有新邮件
if (hasNewEmail) {
const testEmails = generateTestEmails();
testEmails.forEach(email => {
displayEmailNotification(email);
receivedEmailCount++;
});
addLog(`[${new Date().toLocaleTimeString()}] ✅ 发现 ${testEmails.length} 封新邮件`);
} else {
addLog(`[${new Date().toLocaleTimeString()}] 📭 暂无新邮件`);
}
updateStats();
} catch (error) {
addLog(`[${new Date().toLocaleTimeString()}] ❌ 检查邮件时发生错误: ${error.message}`);
showNotification('检查邮件失败: ' + error.message, 'error');
}
}
// 显示邮件通知
function displayEmailNotification(emailData) {
const notificationsContainer = document.getElementById('emailNotifications');
const notification = document.createElement('div');
notification.className = 'email-notification';
// 提取验证码
const verificationCode = extractVerificationCode(emailData.content);
notification.innerHTML = `
<div class="notification-header">
<i class="fas fa-envelope"></i>
<strong>临时邮箱收到新邮件!</strong>
</div>
<div style="margin-bottom: 10px;">
<strong>📧 临时邮箱:</strong> ${emailData.temp_email}<br>
<strong>👤 发件人:</strong> ${emailData.from}<br>
<strong>📝 主题:</strong> ${emailData.subject}<br>
<strong>📅 日期:</strong> ${emailData.date}
</div>
${verificationCode ? `
<div class="verification-code">
<div style="margin-bottom: 10px;"><strong>🔑 验证码:</strong></div>
<div class="code">${verificationCode}</div>
</div>
` : ''}
<div class="notification-content">
<strong>📄 邮件内容:</strong>
<div style="margin-top: 10px; max-height: 200px; overflow-y: auto; background: #f8f9fa; padding: 10px; border-radius: 5px;">
${emailData.content.substring(0, 500)}${emailData.content.length > 500 ? '...' : ''}
</div>
</div>
<div style="margin-top: 15px; text-align: center; color: #28a745;">
✅ 邮件已自动转发到QQ邮箱: ${config.qq_email.username}
</div>
`;
notificationsContainer.insertBefore(notification, notificationsContainer.firstChild);
// 5秒后自动隐藏可选
setTimeout(() => {
notification.style.opacity = '0.7';
}, 5000);
}
// 提取验证码
function extractVerificationCode(content) {
const patterns = [
/验证码[:\s]*(\d{4,8})/i,
/verification code[:\s]*(\d{4,8})/i,
/code[:\s]*(\d{4,8})/i,
/enter this code[:\s]*(\d{4,8})/i,
/<[^>]*>(\d{6})<[^>]*>/,
/\b(\d{6})\b/,
/\b(\d{4})\b/
];
for (const pattern of patterns) {
const match = content.match(pattern);
if (match && match[1]) {
const code = match[1];
// 过滤掉明显不是验证码的数字
if (!['2024', '2025', '1000', '8080', '443', '80', '3000'].includes(code)) {
return code;
}
}
}
return null;
}
// 生成测试邮件数据
function generateTestEmails() {
const emails = Object.keys(tempEmails);
if (emails.length === 0) return [];
const testEmail = emails[Math.floor(Math.random() * emails.length)];
const services = ['TikTok', 'GitHub', 'Google', 'Microsoft', 'Apple'];
const service = services[Math.floor(Math.random() * services.length)];
const code = Math.floor(100000 + Math.random() * 900000);
return [{
temp_email: testEmail,
from: `${service} <noreply@${service.toLowerCase()}.com>`,
subject: `${service} verification code`,
date: new Date().toUTCString(),
content: `
<html>
<body>
<h2>Verification Code</h2>
<p>To verify your account, enter this code:</p>
<p style="font-size: 24px; font-weight: bold; color: #007bff;">${code}</p>
<p>This code expires in 10 minutes.</p>
<p>If you didn't request this code, you can ignore this message.</p>
<p>${service} Support Team</p>
</body>
</html>
`
}];
}
// 更新邮箱列表
function updateEmailList() {
const emailList = document.getElementById('emailList');
const emails = Object.keys(tempEmails);
if (emails.length === 0) {
emailList.innerHTML = '<p style="text-align: center; color: #666;">暂无临时邮箱</p>';
return;
}
emailList.innerHTML = emails.map(email => {
const emailData = tempEmails[email];
const createdTime = new Date(emailData.created_at).toLocaleString();
return `
<div class="email-item">
<div class="email-info">
<div class="email-address">${email}</div>
<div class="email-time">创建时间: ${createdTime}</div>
</div>
<div>
<input type="checkbox" class="email-checkbox" value="${email}" style="margin-right: 10px;">
<button class="btn" onclick="copyToClipboard('${email}')" style="padding: 5px 10px; font-size: 12px;">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
`;
}).join('');
}
// 更新统计信息
function updateStats() {
document.getElementById('emailCount').textContent = Object.keys(tempEmails).length;
document.getElementById('receivedCount').textContent = receivedEmailCount;
document.getElementById('monitorStatus').textContent = isMonitoring ? '运行中' : '已停止';
if (isMonitoring && startTime) {
updateRunTime();
} else {
document.getElementById('runTime').textContent = '00:00:00';
}
}
// 更新运行时间
function updateRunTime() {
if (!isMonitoring || !startTime) return;
const now = new Date();
const duration = Math.floor((now - startTime) / 1000);
document.getElementById('runTime').textContent = formatTime(duration);
if (isMonitoring) {
setTimeout(updateRunTime, 1000);
}
}
// 格式化时间
function formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
// 添加日志
function addLog(message) {
const logContent = document.getElementById('logContent');
const logEntry = document.createElement('div');
logEntry.textContent = message;
logContent.appendChild(logEntry);
// 自动滚动到底部
logContent.scrollTop = logContent.scrollHeight;
// 限制日志条数
while (logContent.children.length > 100) {
logContent.removeChild(logContent.firstChild);
}
}
// 复制到剪贴板
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showNotification('已复制到剪贴板', 'success');
}).catch(() => {
showNotification('复制失败', 'error');
});
}
// 显示通知
function showNotification(message, type = 'info') {
// 创建通知元素
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 8px;
color: white;
font-weight: 600;
z-index: 1000;
animation: slideInRight 0.3s ease;
`;
// 根据类型设置颜色
const colors = {
success: '#28a745',
error: '#dc3545',
warning: '#ffc107',
info: '#17a2b8'
};
notification.style.backgroundColor = colors[type] || colors.info;
notification.textContent = message;
document.body.appendChild(notification);
// 3秒后自动移除
setTimeout(() => {
notification.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
// 验证配置
function validateConfig() {
return config.qq_email.username &&
config.qq_email.password &&
config.cloudflare.api_token &&
config.cloudflare.zone_id &&
config.cloudflare.domain;
}
// 模拟API调用
function simulateApiCall(delay = 500) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.95) { // 95%成功率
resolve();
} else {
reject(new Error('网络错误'));
}
}, delay);
});
}