-
多语言支持:简体中文、繁体中文、英文、意大利语、西班牙语。
-
邮箱有效期:默认 10 分钟,支持手动延长。
-
复制功能:一键复制邮箱地址。
-
HTTPS 支持:通过 Nginx 和 Let’s Encrypt 实现。
-
域名支持:
-
主域名(如 email.yourdomain.com)用于访问程序。
-
固定域名(如 yourdomain1.com)供用户选择。
-
任意域名通过 MX 记录接收邮件。
-
-
进程常驻:使用 PM2 保持后台运行。
-
操作系统:Linux(推荐 Ubuntu)
-
软件:
-
Node.js(建议 v16 或以上)
-
Nginx
-
Certbot(用于 HTTPS)
-
PM2(用于进程管理)
-
-
硬件:一台具有公网 IP 的服务器
-
域名:至少一个主域名和几个固定域名
temp-email/
├── index.js # 主程序
├── package.json # 依赖配置文件
├── emails/ # 邮件存储目录
├── public/
│ ├── index.html # 前端页面
│ └── languages/ # 语言文件
│ ├── en.json
│ ├── zh-CN.json
│ ├── zh-TW.json
│ ├── it.json
│ └── es.json
├── cert/ # HTTPS 证书目录
│ ├── server.key
│ └── server.crt
sudo apt update
sudo apt install nodejs npm
node -v # 确认版本,例如 v16.x.x
sudo apt install nginx
sudo systemctl enable nginx
sudo systemctl start nginx
sudo apt install certbot python3-certbot-nginx
npm install -g pm2
curl ifconfig.me # 记录公网 IP,例如 123.45.67.89
mkdir temp-email
cd temp-email
const { SMTPServer } = require('smtp-server');
const express = require('express');
const { simpleParser } = require('mailparser');
const path = require('path');
const fs = require('fs');
const https = require('https');
// 配置
const PORT_WEB = 3000;
const PORT_SMTP = 25;
const MAIN_DOMAIN = 'email.yourdomain.com'; // 替换为主域名
const FIXED_DOMAINS = ['yourdomain1.com', 'yourdomain2.com', 'yourdomain3.com']; // 替换为固定域名
const EMAIL_TTL = 24 * 60 * 60 * 1000; // 文件存储 24 小时
const EMAIL_LIFETIME = 10 * 60 * 1000; // 邮箱默认 10 分钟有效期
const EMAIL_DIR = path.join(__dirname, 'emails');
if (!fs.existsSync(EMAIL_DIR)) {
fs.mkdirSync(EMAIL_DIR);
}
const emailLifetimes = new Map();
function generateEmail(prefix, domain) {
const randomStr = prefix || Math.random().toString(36).substring(2, 10);
const cleanPrefix = randomStr.replace(/[^a-zA-Z0-9]/g, '');
if (!cleanPrefix) throw new Error('Invalid prefix');
return `${cleanPrefix}@${domain}`;
}
function loadEmails(email) {
const filePath = path.join(EMAIL_DIR, `${email.replace('@', '_')}.json`);
if (fs.existsSync(filePath)) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
return [];
}
function saveEmails(email, emails) {
const filePath = path.join(EMAIL_DIR, `${email.replace('@', '_')}.json`);
fs.writeFileSync(filePath, JSON.stringify(emails, null, 2));
}
function cleanupEmails() {
const now = Date.now();
fs.readdirSync(EMAIL_DIR).forEach(file => {
const email = file.replace('_', '@').replace('.json', '');
const emails = loadEmails(email);
const filtered = emails.filter(e => now - e.timestamp < EMAIL_TTL);
if (filtered.length > 0) {
saveEmails(email, filtered);
} else {
fs.unlinkSync(path.join(EMAIL_DIR, file));
}
});
}
setInterval(cleanupEmails, 60 * 1000);
const smtpServer = new SMTPServer({
authOptional: true,
onRcptTo(address, session, callback) {
callback(null);
},
onData(stream, session, callback) {
let emailData = '';
stream.on('data', chunk => (emailData += chunk));
stream.on('end', async () => {
try {
const parsed = await simpleParser(emailData);
const to = session.envelope.rcptTo[0].address;
const expiration = emailLifetimes.get(to);
if (expiration && Date.now() > expiration) {
return callback(new Error('Email address expired'));
}
const emailEntry = {
from: parsed.from?.value[0]?.address || 'unknown@example.com',
fromName: parsed.from?.value[0]?.name || '',
subject: parsed.subject || 'No Subject',
body: parsed.text || parsed.html || 'No Content',
timestamp: Date.now(),
receivedAt: new Date().toLocaleString()
};
const emails = loadEmails(to);
emails.push(emailEntry);
saveEmails(to, emails);
callback(null);
} catch (err) {
console.error('Error parsing email:', err);
callback(err);
}
});
}
});
smtpServer.listen(PORT_SMTP, () => {
console.log(`SMTP Server running on port ${PORT_SMTP}`);
});
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/api/main-domain', (req, res) => {
res.json({ domain: MAIN_DOMAIN });
});
app.get('/api/domains', (req, res) => {
res.json(FIXED_DOMAINS);
});
app.get('/api/new-email', (req, res) => {
const domain = req.query.domain || FIXED_DOMAINS[0]; // 如果没有提供 domain,使用默认域名
const prefix = req.query.prefix || '';
console.log(`Received domain: ${domain}`); // 调试日志
try {
const email = generateEmail(prefix, domain);
emailLifetimes.set(email, Date.now() + EMAIL_LIFETIME);
res.json({ email, expiresAt: emailLifetimes.get(email) });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.get('/api/emails/:email', (req, res) => {
const email = req.params.email;
const emails = loadEmails(email);
const expiration = emailLifetimes.get(email) || 0;
res.json({
emails: emails.filter(e => Date.now() - e.timestamp < EMAIL_TTL),
expiresAt: expiration
});
});
app.get('/api/extend/:email', (req, res) => {
const email = req.params.email;
if (emailLifetimes.has(email)) {
const newExpiration = (emailLifetimes.get(email) || Date.now()) + EMAIL_LIFETIME;
emailLifetimes.set(email, newExpiration);
res.json({ expiresAt: newExpiration });
} else {
res.status(404).json({ error: 'Email not found or already expired' });
}
});
const useHttps = fs.existsSync(path.join(__dirname, 'cert', 'server.key')) &&
fs.existsSync(path.join(__dirname, 'cert', 'server.crt'));
if (useHttps) {
const options = {
key: fs.readFileSync(path.join(__dirname, 'cert', 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'cert', 'server.crt'))
};
https.createServer(options, app).listen(PORT_WEB, 'localhost', () => {
console.log(`HTTPS Server running on https://localhost:${PORT_WEB}`);
});
} else {
app.listen(PORT_WEB, 'localhost', () => {
console.log(`HTTP Server running on http://localhost:${PORT_WEB}`);
});
}
{
"name": "temp-email",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2",
"smtp-server": "^3.11.0",
"mailparser": "^3.6.5",
"https": "^1.0.0",
"fs": "^0.0.1-security"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Temp Email</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
background-color: #f4f6f9;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
max-width: 900px;
width: 100%;
background: white;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
padding: 25px;
text-align: center;
position: relative;
}
h1 {
font-size: 26px;
color: #1565c0;
margin-bottom: 20px;
}
.intro, .tutorial {
font-size: 14px;
color: #666;
margin-top: 20px;
text-align: left;
padding: 10px;
background: #f9f9f9;
border-radius: 6px;
}
.intro h2, .tutorial h2 {
font-size: 18px;
color: #1976d2;
margin-bottom: 10px;
}
.email-section {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
padding-bottom: 20px;
border-bottom: 1px solid #ddd;
}
.email-box {
flex-grow: 1;
max-width: 500px;
padding: 12px;
background: #e3f2fd;
border-radius: 8px;
font-size: 18px;
font-weight: bold;
color: #0d47a1;
border: 1px solid #90caf9;
text-align: center;
}
.timer {
font-size: 14px;
color: #e63946;
margin-top: 5px;
}
.controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin-top: 15px;
}
button, select, input {
padding: 12px;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s ease;
}
button {
background-color: #1976d2;
color: white;
}
button:hover {
background-color: #1565c0;
}
input, select {
background: #fff;
border: 1px solid #ccc;
width: 200px;
}
#lang-select {
position: absolute;
top: 25px;
right: 25px;
background: #fff;
border: 1px solid #ccc;
padding: 8px;
}
#inbox-container {
margin-top: 20px;
max-height: 400px;
overflow-y: auto;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background: #fafafa;
}
.email-item {
padding: 15px;
border-bottom: 1px solid #ddd;
background: white;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 15px;
}
.email-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.email-from {
font-size: 14px;
color: #666;
}
.email-time {
font-size: 12px;
color: #999;
}
.email-subject {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 5px 0;
}
.email-body {
font-size: 14px;
color: #444;
white-space: pre-wrap;
}
</style>
</head>
<body>
<div class="container">
<h1 id="title">Temporary Email Service</h1>
<select id="lang-select" onchange="changeLanguage(this.value)">
<option value="en">English</option>
<option value="zh-CN">简体中文</option>
<option value="zh-TW">繁體中文</option>
<option value="it">Italiano</option>
<option value="es">Español</option>
</select>
<div class="email-section">
<div class="email-box" id="email-box">
<span id="email"></span>
<div class="timer" id="timer"></div>
</div>
<button onclick="copyEmail()" id="copy-btn">Copy</button>
<button onclick="extendEmail()" id="extend-btn">Extend 10 Minutes</button>
</div>
<div class="controls">
<input id="prefix-input" type="text" placeholder="Custom Prefix (Optional)">
<select id="domain-select">
<option value="" id="domain-select-default"></option>
</select>
<input id="domain-input" type="text" placeholder="Or Enter Custom Domain">
<button onclick="getNewEmail()" id="generate-btn">Generate New Email</button>
<button onclick="fetchEmails()" id="refresh-btn">Refresh</button>
</div>
<div id="inbox-container">
<div id="inbox"></div>
</div>
<div class="intro">
<h2 id="intro-title"></h2>
<p id="intro-content"></p>
</div>
<div class="tutorial">
<h2 id="tutorial-title"></h2>
<div id="tutorial-content"></div>
</div>
</div>
<script>
let currentEmail = '';
let translations = {};
let currentLang = 'en';
let expirationTime = 0;
let mainDomain = '';
const supportedLangs = ['en', 'zh-CN', 'zh-TW', 'it', 'es'];
function detectBrowserLanguage() {
const browserLang = navigator.language || navigator.userLanguage;
console.log('Detected browser language:', browserLang); // 调试:输出浏览器语言
if (!browserLang) return 'en';
const langLower = browserLang.toLowerCase();
if (langLower.startsWith('zh-cn')) return 'zh-CN';
if (langLower.startsWith('zh-tw') || langLower.startsWith('zh-hk')) return 'zh-TW';
if (langLower.startsWith('zh')) return 'zh-CN'; // 默认简体中文
if (langLower.startsWith('it')) return 'it';
if (langLower.startsWith('es')) return 'es';
return 'en'; // 默认回退到英文
}
async function loadTranslations(lang) {
try {
const response = await fetch(`/languages/${lang}.json`);
if (!response.ok) throw new Error(`Failed to load ${lang}.json`);
translations[lang] = await response.json();
console.log(`Loaded translations for ${lang}`); // 调试:确认加载成功
} catch (err) {
console.error(`Error loading ${lang}.json: ${err}`);
translations[lang] = translations['en']; // 回退到英文
}
}
async function initLanguages() {
// 先加载所有语言文件
await Promise.all(supportedLangs.map(lang => loadTranslations(lang)));
// 检测浏览器语言并设置
currentLang = detectBrowserLanguage();
console.log('Initial language set to:', currentLang); // 调试:输出初始语言
document.getElementById('lang-select').value = currentLang;
updateUI();
}
initLanguages();
async function loadMainDomain() {
const response = await fetch('/api/main-domain');
const data = await response.json();
mainDomain = data.domain;
document.title = `Temp Email - ${mainDomain}`;
}
loadMainDomain();
async function loadDomains() {
const response = await fetch('/api/domains');
const domains = await response.json();
const select = document.getElementById('domain-select');
const defaultOption = document.getElementById('domain-select-default');
defaultOption.textContent = translations[currentLang].domainSelect;
domains.forEach(domain => {
const option = document.createElement('option');
option.value = domain;
option.textContent = domain;
select.appendChild(option);
});
select.value = '';
getNewEmail();
}
loadDomains();
function changeLanguage(lang) {
currentLang = lang || document.getElementById('lang-select').value;
if (!supportedLangs.includes(currentLang)) {
currentLang = 'en';
}
console.log('Language changed to:', currentLang); // 调试:输出切换后的语言
document.getElementById('lang-select').value = currentLang;
if (translations[currentLang]) updateUI();
}
function updateUI() {
const t = translations[currentLang] || translations['en'];
document.getElementById('title').textContent = t.title;
document.getElementById('generate-btn').textContent = t.generateBtn;
document.getElementById('refresh-btn').textContent = t.refreshBtn;
document.getElementById('extend-btn').textContent = t.extendBtn;
document.getElementById('copy-btn').textContent = t.copyBtn;
document.getElementById('prefix-input').placeholder = t.prefixPlaceholder;
document.getElementById('domain-input').placeholder = t.domainPlaceholder;
document.getElementById('intro-title').textContent = t.introTitle;
document.getElementById('intro-content').textContent = t.introContent;
document.getElementById('tutorial-title').textContent = t.tutorialTitle;
document.getElementById('tutorial-content').innerHTML = t.tutorialContent;
document.getElementById('domain-select-default').textContent = t.domainSelect;
fetchEmails();
}
async function getNewEmail() {
try {
let domain = document.getElementById('domain-input').value.trim();
if (!domain) {
domain = document.getElementById('domain-select').value || '';
}
const prefix = document.getElementById('prefix-input').value.trim();
const url = `/api/new-email?domain=${encodeURIComponent(domain)}${prefix ? `&prefix=${encodeURIComponent(prefix)}` : ''}`;
const response = await fetch(url);
const data = await response.json();
if (data.error) throw new Error(data.error);
currentEmail = data.email;
expirationTime = data.expiresAt;
document.getElementById('email').textContent = currentEmail;
updateTimer();
fetchEmails();
} catch (err) {
console.error('Error:', err);
alert((translations[currentLang] || translations['en']).error + ': ' + err.message);
}
}
async function extendEmail() {
if (!currentEmail) return;
try {
const response = await fetch(`/api/extend/${encodeURIComponent(currentEmail)}`);
const data = await response.json();
if (data.error) throw new Error(data.error);
expirationTime = data.expiresAt;
updateTimer();
} catch (err) {
console.error('Error extending email:', err);
alert((translations[currentLang] || translations['en']).error + ': ' + err.message);
}
}
function copyEmail() {
if (!currentEmail) {
alert((translations[currentLang] || translations['en']).noEmail);
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(currentEmail)
.then(() => {
alert((translations[currentLang] || translations['en']).copied);
})
.catch(err => {
console.error('Clipboard API failed:', err);
fallbackCopyEmail();
});
} else {
fallbackCopyEmail();
}
}
function fallbackCopyEmail() {
const tempInput = document.createElement('input');
tempInput.value = currentEmail;
document.body.appendChild(tempInput);
tempInput.select();
try {
document.execCommand('copy');
alert((translations[currentLang] || translations['en']).copied);
} catch (err) {
console.error('Fallback copy failed:', err);
alert((translations[currentLang] || translations['en']).copyFailed);
}
document.body.removeChild(tempInput);
}
function updateTimer() {
const now = Date.now();
const timeLeft = expirationTime - now;
const timer = document.getElementById('timer');
if (timeLeft > 0) {
const minutes = Math.floor(timeLeft / 60000);
const seconds = Math.floor((timeLeft % 60000) / 1000);
timer.textContent = `${translations[currentLang].timeLeft}: ${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
} else {
timer.textContent = translations[currentLang].expired;
currentEmail = '';
document.getElementById('email').textContent = '';
}
}
setInterval(updateTimer, 1000);
async function fetchEmails() {
if (!currentEmail || Date.now() > expirationTime) return;
try {
const response = await fetch(`/api/emails/${encodeURIComponent(currentEmail)}`);
const data = await response.json();
const inbox = document.getElementById('inbox');
const t = translations[currentLang] || translations['en'];
inbox.innerHTML = data.emails.map(e => `
<div class="email-item">
<div class="email-header">
<span class="email-from">${t.from}: ${e.from}${e.fromName ? ` (${e.fromName})` : ''}</span>
<span class="email-time">${t.received}: ${e.receivedAt}</span>
</div>
<div class="email-subject">${e.subject}</div>
<div class="email-body">${e.body}</div>
</div>
`).join('');
expirationTime = data.expiresAt;
} catch (err) {
console.error('Error fetching emails:', err);
}
}
setInterval(fetchEmails, 5000);
</script>
</body>
</html>
2.5 创建语言文件
{
"title": "临时邮箱服务",
"generateBtn": "生成新邮箱",
"refreshBtn": "刷新",
"prefixPlaceholder": "自定义前缀(可选)",
"domainPlaceholder": "或输入自定义域名",
"emailLabel": "您的邮箱:",
"from": "发件人",
"received": "接收时间",
"error": "错误",
"timeLeft": "剩余时间",
"expired": "已过期",
"extendBtn": "延长10分钟",
"copyBtn": "复制",
"copied": "已复制",
"noEmail": "没有邮箱可复制",
"copyFailed": "复制失败"
}
{
"title": "Temporary Email Service",
"generateBtn": "Generate New Email",
"refreshBtn": "Refresh",
"prefixPlaceholder": "Custom prefix (optional)",
"domainPlaceholder": "Or enter custom domain",
"emailLabel": "Your email:",
"from": "From",
"received": "Received",
"error": "Error",
"timeLeft": "Time left",
"expired": "Expired",
"extendBtn": "Extend 10 minutes",
"copyBtn": "Copy",
"copied": "Copied",
"noEmail": "No email to copy",
"copyFailed": "Copy failed"
}
{
"title": "臨時郵箱服務",
"generateBtn": "生成新郵箱",
"refreshBtn": "刷新",
"prefixPlaceholder": "自定義前綴(可選)",
"domainPlaceholder": "或輸入自定義域名",
"emailLabel": "您的郵箱:",
"from": "發件人",
"received": "接收時間",
"error": "錯誤",
"timeLeft": "剩餘時間",
"expired": "已過期",
"extendBtn": "延長10分鐘",
"copyBtn": "複製",
"copied": "已複製",
"noEmail": "沒有郵箱可複製",
"copyFailed": "複製失敗"
}
{
"title": "Servizio Email Temporaneo",
"generateBtn": "Genera Nuova Email",
"refreshBtn": "Aggiorna",
"prefixPlaceholder": "Prefisso personalizzato (opzionale)",
"domainPlaceholder": "O inserisci dominio personalizzato",
"emailLabel": "La tua email:",
"from": "Da",
"received": "Ricevuto",
"error": "Errore",
"timeLeft": "Tempo rimanente",
"expired": "Scaduto",
"extendBtn": "Estendi 10 minuti",
"copyBtn": "Copia",
"copied": "Copiato",
"noEmail": "Nessuna email da copiare",
"copyFailed": "Copia fallita"
}
{
"title": "Servicio de Correo Temporal",
"generateBtn": "Generar Nuevo Correo",
"refreshBtn": "Actualizar",
"prefixPlaceholder": "Prefijo personalizado (opcional)",
"domainPlaceholder": "O ingresa dominio personalizado",
"emailLabel": "Tu correo:",
"from": "De",
"received": "Recibido",
"error": "Error",
"timeLeft": "Tiempo restante",
"expired": "Expirado",
"extendBtn": "Extender 10 minutos",
"copyBtn": "Copiar",
"copied": "Copiado",
"noEmail": "No hay correo para copiar",
"copyFailed": "Copia fallida"
}
const MAIN_DOMAIN = 'email.yourdomain.com'; // 你的主域名
const FIXED_DOMAINS = ['yourdomain1.com', 'yourdomain2.com', 'yourdomain3.com']; // 你的固定域名
-
主域名:
-
A 记录:email.yourdomain.com -> <服务器 IP>
-
示例:
email.yourdomain.com A 123.45.67.89
-
-
固定域名 MX 记录:
-
MX 记录:yourdomain1.com -> <服务器 IP>
-
示例:
@ MX 10 123.45.67.89
-
对每个固定域名重复此步骤。
-
-
其他域名:
-
用户可自行配置 MX 记录指向服务器 IP。
-
-
生成证书:
sudo certbot --nginx -d email.yourdomain.com
-
按照提示完成配置,选择重定向 HTTP 到 HTTPS。
-
-
将证书复制到项目目录(可选,若使用 Node.js HTTPS):
mkdir cert cp /etc/letsencrypt/live/email.yourdomain.com/fullchain.pem cert/server.crt cp /etc/letsencrypt/live/email.yourdomain.com/privkey.pem cert/server.key
sudo nano /etc/nginx/sites-available/email.yourdomain.com
server {
listen 80;
server_name email.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name email.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/email.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/email.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
sudo ln -s /etc/nginx/sites-available/email.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
npm install
sudo pm2 start index.js --name "temp-email"
pm2 startup # 按照提示运行输出的命令
pm2 save
pm2 stop temp-email # 停止
sudo ufw allow 25
sudo ufw allow 80
sudo ufw allow 443
sudo ufw status
-
访问程序:
-
打开浏览器,访问 https://email.yourdomain.com。
-
确认页面加载,下拉菜单显示固定域名。
-
-
生成邮箱:
-
选择固定域名(如 yourdomain1.com)或输入自定义域名(如 customdomain.com)。
-
点击“生成新邮箱”,查看生成的邮箱地址。
-
-
发送邮件:
-
配置自定义域名的 MX 记录指向服务器 IP。
-
发送邮件到生成的邮箱地址,检查收件箱是否收到。
-
-
功能测试:
-
复制:点击“复制”按钮,粘贴到其他地方验证。
-
延长:点击“延长10分钟”,确认倒计时更新。
-
语言:切换语言,确认界面翻译正确。
-
-
DNS 传播:MX 记录生效可能需要几分钟到 48 小时。
-
安全性:开放所有域名可能接收垃圾邮件,可考虑添加黑名单。
-
证书续订:Let’s Encrypt 证书有效期 90 天,运行以下命令测试续订:
sudo certbot renew --dry-run
-
无法访问 HTTPS:
-
检查证书路径:ls -l /etc/letsencrypt/live/email.yourdomain.com/
-
检查 Nginx 日志:sudo tail -f /var/log/nginx/error.log
-
-
邮件未收到:
-
确认 MX 记录:nslookup -type=MX yourdomain1.com
-
检查 SMTP 端口:sudo netstat -tuln | grep 25
-
-
PM2 未运行:
-
查看状态:pm2 list
-
查看日志:pm2 logs temp-email
-
-
添加域名验证:在 onRcptTo 中校验域名,防止滥用。
-
优化存储:使用数据库(如 SQLite)替换文件存储。
-
界面美化:调整 CSS 样式或使用前端框架。
-
ps aux | grep node
lsof -i :3000
kill -9 12345
原创文章,作者:开空网,转载请注明出处:https://www.openull.org/temp-mail.html