mailu branch failed
This commit is contained in:
commit
2999e562c7
|
|
@ -0,0 +1,4 @@
|
||||||
|
/backend/node_modules
|
||||||
|
/frontend/node_modules
|
||||||
|
/backend/package-lock.json
|
||||||
|
/frontend/package-lock.json
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
1.在终端执行命令时使用适合Windows 的命令
|
||||||
|
|
||||||
|
2.始终使用中文回答
|
||||||
|
|
||||||
|
3.对于项目代码的中打印语句代码(println语句、console语句等等)和注释需要保留,不要随意改动他们
|
||||||
|
|
||||||
|
4.对于前端的npm等命令你输出到屏幕即可(给出命令即可),我会在其他终端自己执行
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Email Unlimit Project (临时邮件项目)
|
||||||
|
|
||||||
|
## 最终架构 (单一 Docker Compose + 宿主机 Nginx)
|
||||||
|
|
||||||
|
本项目采用生产环境推荐的架构:所有服务(包括您的应用和完整的 Mailu 邮件套件)都由一个 `docker-compose.full.yml` 文件统一管理,而入口的反向代理则由 **宿主机上的 Nginx** 负责。
|
||||||
|
|
||||||
|
- **宿主机 Nginx:** 作为项目的唯一公共入口 (`main.shenjianl.cn`)。它负责:
|
||||||
|
1. 处理所有外部流量和 SSL 加密(推荐)。
|
||||||
|
2. 将对域名根路径 (`/`) 的访问反向代理到 **前端 Docker 容器** (`localhost:5181`)。
|
||||||
|
3. 将对 `/api` 路径的访问反向代理到 **后端 Docker 容器** (`localhost:5182`)。
|
||||||
|
4. 将对 `/mailu` 路径的访问反向代理到 **Mailu Admin UI** (`localhost:80`)。
|
||||||
|
|
||||||
|
- **Docker 容器 (由 `docker-compose.full.yml` 管理):**
|
||||||
|
- **您的应用:** `frontend`, `backend`, `mysql`。
|
||||||
|
- **Mailu 套件:** `front` (Mailu Nginx), `admin`, `smtp`, `imap`, `redis` 等全套服务。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 如何运行
|
||||||
|
|
||||||
|
### 步骤 1: 准备工作
|
||||||
|
|
||||||
|
1. **安装 Docker**: 确保您的系统中已安装 Docker 和 Docker Compose。
|
||||||
|
2. **配置 Mailu**: 打开 `mailu.env` 文件,**务必修改** `SECRET_KEY` 和 `ADMIN_PASSWORD` 为安全的值。域名和数据库配置已预填。
|
||||||
|
3. **配置 DNS**: 根据 `info.md` 中的 DNS 记录示例,在您的域名提供商处完成 SPF、DKIM 和 DMARC 的配置。
|
||||||
|
|
||||||
|
### 步骤 2: 配置宿主机 Nginx
|
||||||
|
|
||||||
|
将下面的配置块添加到您宿主机的 Nginx 中。这份配置统一处理了对您的应用和 Mailu 管理后台的访问。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# /etc/nginx/conf.d/main.shenjianl.cn.conf
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name main.shenjianl.cn;
|
||||||
|
|
||||||
|
# --- 推荐配置 SSL ---
|
||||||
|
# listen 443 ssl http2;
|
||||||
|
# server_name main.shenjianl.cn;
|
||||||
|
# ssl_certificate /path/to/your/fullchain.pem;
|
||||||
|
# ssl_certificate_key /path/to/your/privkey.pem;
|
||||||
|
# --------------------
|
||||||
|
|
||||||
|
# 反向代理到前端 Vue.js 容器
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:5181;
|
||||||
|
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;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 反向代理到后端 API 容器
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://localhost:5182;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 反向代理到 Mailu Admin UI 和 Webmail
|
||||||
|
# 注意:Mailu 自己的 Nginx (front) 在 80 端口上运行
|
||||||
|
location /mailu {
|
||||||
|
proxy_pass http://localhost:80;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**配置完成后,请重载或重启您的 Nginx 服务。**
|
||||||
|
|
||||||
|
### 步骤 3: 启动 Docker 服务
|
||||||
|
|
||||||
|
使用我们最终生成的 `docker-compose.full.yml` 文件来启动所有服务。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在项目根目录执行
|
||||||
|
docker-compose -f docker-compose.full.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 访问<E8AEBF><E997AE><EFBFBD>址
|
||||||
|
|
||||||
|
- **主应用入口**: `http://main.shenjianl.cn`
|
||||||
|
- **Mailu 管理后台**: `http://main.shenjianl.cn/mailu`
|
||||||
|
- **Mailu Webmail**: `http://main.shenjianl.cn/mailu/webmail`
|
||||||
|
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 5182
|
||||||
|
|
||||||
|
CMD [ "node", "app.js" ]
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
|
const db = require('./db');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const port = 5182;
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// API to get messages for a recipient
|
||||||
|
app.get('/api/messages', async (req, res) => {
|
||||||
|
const { recipient } = req.query;
|
||||||
|
if (!recipient) {
|
||||||
|
return res.status(400).send('Recipient is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [rows] = await db.execute(
|
||||||
|
'SELECT id, sender, subject, body, received_at FROM emails WHERE recipient = ? ORDER BY received_at DESC',
|
||||||
|
[recipient]
|
||||||
|
);
|
||||||
|
res.json(rows);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch emails:', error);
|
||||||
|
res.status(500).send('Failed to fetch emails');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Backend server listening at http://localhost:${port}`);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
const mysql = require('mysql2');
|
||||||
|
|
||||||
|
const pool = mysql.createPool({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
queueLimit: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = pool.promise();
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS emails (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
recipient VARCHAR(255) NOT NULL,
|
||||||
|
sender VARCHAR(255) NOT NULL,
|
||||||
|
subject VARCHAR(512),
|
||||||
|
body TEXT,
|
||||||
|
received_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
raw MEDIUMBLOB
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS email_attachments (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
email_id BIGINT NOT NULL,
|
||||||
|
filename VARCHAR(255),
|
||||||
|
content_type VARCHAR(128),
|
||||||
|
content LONGBLOB,
|
||||||
|
FOREIGN KEY (email_id) REFERENCES emails(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Backend for email service",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"mysql2": "^3.14.2",
|
||||||
|
"mailparser": "^3.7.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"get-stream": "^9.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const { simpleParser } = require('mailparser');
|
||||||
|
const db = require('./db');
|
||||||
|
|
||||||
|
// Using require for get-stream v5 as it's CommonJS
|
||||||
|
const getStream = require('get-stream');
|
||||||
|
|
||||||
|
async function saveEmail(rawEmail) {
|
||||||
|
try {
|
||||||
|
const parsed = await simpleParser(rawEmail);
|
||||||
|
|
||||||
|
// Ensure 'to' and 'from' objects exist before accessing 'text'
|
||||||
|
const recipient = parsed.to ? parsed.to.text : 'undisclosed-recipients';
|
||||||
|
const sender = parsed.from ? parsed.from.text : 'unknown-sender';
|
||||||
|
const subject = parsed.subject;
|
||||||
|
const body = parsed.text || (parsed.html || '');
|
||||||
|
|
||||||
|
const [result] = await db.execute(
|
||||||
|
'INSERT INTO emails (recipient, sender, subject, body, raw) VALUES (?, ?, ?, ?, ?)',
|
||||||
|
[recipient, sender, subject, body, rawEmail]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Email saved with ID: ${result.insertId}`);
|
||||||
|
|
||||||
|
if (parsed.attachments && parsed.attachments.length > 0) {
|
||||||
|
for (const attachment of parsed.attachments) {
|
||||||
|
await db.execute(
|
||||||
|
'INSERT INTO email_attachments (email_id, filename, content_type, content) VALUES (?, ?, ?, ?)',
|
||||||
|
[result.insertId, attachment.filename, attachment.contentType, attachment.content]
|
||||||
|
);
|
||||||
|
console.log(`Attachment ${attachment.filename} saved.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save email:', error);
|
||||||
|
// Exit with an error code to signal failure to the calling process (e.g., Mailu)
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const rawEmail = await getStream(process.stdin);
|
||||||
|
if (rawEmail && rawEmail.length > 0) {
|
||||||
|
await saveEmail(rawEmail);
|
||||||
|
} else {
|
||||||
|
console.log('Received empty input, no email to save.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading from stdin:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# docker-compose.full.yml
|
||||||
|
# 最终合并版本:包含您的应用 (frontend, backend) 和完整的 Mailu 服务套件。
|
||||||
|
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# 您的应用服务
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
container_name: mail_backend
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
DB_HOST: "43.143.145.172"
|
||||||
|
DB_USER: "root"
|
||||||
|
DB_PASSWORD: "kyff145972"
|
||||||
|
DB_NAME: "maildb"
|
||||||
|
ports:
|
||||||
|
- "5182:5182"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
container_name: mail_frontend
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "5181:8080"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# Mailu 官方服务套件
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- "mailu_redis:/data"
|
||||||
|
|
||||||
|
front:
|
||||||
|
image: ghcr.io/mailu/nginx:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_certs:/certs"
|
||||||
|
- "mailu_overrides_nginx:/overrides"
|
||||||
|
|
||||||
|
resolver:
|
||||||
|
image: ghcr.io/mailu/unbound:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
dns:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
# --- 用于调试的临时修改 ---
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command: -c "sleep 3600"
|
||||||
|
|
||||||
|
admin:
|
||||||
|
image: ghcr.io/mailu/admin:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_data:/data"
|
||||||
|
- "mailu_dkim:/dkim"
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
imap:
|
||||||
|
image: ghcr.io/mailu/dovecot:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_mail:/mail"
|
||||||
|
- "mailu_overrides_dovecot:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
image: ghcr.io/mailu/postfix:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_overrides_postfix:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
- resolver
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
image: ghcr.io/mailu/rspamd:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_filter:/var/lib/rspamd"
|
||||||
|
- "mailu_overrides_rspamd:/etc/rspamd/override.d"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mailu_data:
|
||||||
|
mailu_certs:
|
||||||
|
mailu_dkim:
|
||||||
|
mailu_filter:
|
||||||
|
mailu_mail:
|
||||||
|
mailu_redis:
|
||||||
|
mailu_overrides_nginx:
|
||||||
|
mailu_overrides_dovecot:
|
||||||
|
mailu_overrides_postfix:
|
||||||
|
mailu_overrides_rspamd:
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
# docker-compose.full.yml
|
||||||
|
# 最终合并版本:包含您的应用 (frontend, backend) 和完整的 Mailu 服务套件。
|
||||||
|
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# 您的应用服务
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
container_name: mail_backend
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
DB_HOST: "43.143.145.172"
|
||||||
|
DB_USER: "root"
|
||||||
|
DB_PASSWORD: "kyff145972"
|
||||||
|
DB_NAME: "maildb"
|
||||||
|
ports:
|
||||||
|
- "5182:5182"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
container_name: mail_frontend
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "5181:8080"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# Mailu 官方服务套件
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- "mailu_redis:/data"
|
||||||
|
|
||||||
|
front:
|
||||||
|
image: ghcr.io/mailu/nginx:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_certs:/certs"
|
||||||
|
- "mailu_overrides_nginx:/overrides"
|
||||||
|
|
||||||
|
resolver:
|
||||||
|
image: ghcr.io/mailu/unbound:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
dns:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
|
||||||
|
admin:
|
||||||
|
image: ghcr.io/mailu/admin:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_data:/data"
|
||||||
|
- "mailu_dkim:/dkim"
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
imap:
|
||||||
|
image: ghcr.io/mailu/dovecot:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_mail:/mail"
|
||||||
|
- "mailu_overrides_dovecot:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
image: ghcr.io/mailu/postfix:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_overrides_postfix:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
- resolver
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
image: ghcr.io/mailu/rspamd:2.0
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
hostname: mail.shenjianl.cn
|
||||||
|
volumes:
|
||||||
|
- "mailu_filter:/var/lib/rspamd"
|
||||||
|
- "mailu_overrides_rspamd:/etc/rspamd/override.d"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mailu_data:
|
||||||
|
mailu_certs:
|
||||||
|
mailu_dkim:
|
||||||
|
mailu_filter:
|
||||||
|
mailu_mail:
|
||||||
|
mailu_redis:
|
||||||
|
mailu_overrides_nginx:
|
||||||
|
mailu_overrides_dovecot:
|
||||||
|
mailu_overrides_postfix:
|
||||||
|
mailu_overrides_rspamd:
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy all other files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Run the app
|
||||||
|
CMD ["npm", "run", "serve"]
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.5.18",
|
||||||
|
"axios": "^1.11.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-service": "~5.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title>临时邮件</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-container">
|
||||||
|
<h1>临时邮箱</h1>
|
||||||
|
<div class="email-generator">
|
||||||
|
<input v-model="randomEmail" type="text" readonly @click="copyToClipboard" title="点击复制"/>
|
||||||
|
<button @click="generateRandomEmail">生成新地址</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inbox">
|
||||||
|
<h2>收件箱 ({{ currentEmail }})</h2>
|
||||||
|
<button @click="fetchMessages" :disabled="loading">
|
||||||
|
{{ loading ? '刷新中...' : '刷新邮件' }}
|
||||||
|
</button>
|
||||||
|
<div v-if="error" class="error">{{ error }}</div>
|
||||||
|
<ul v-if="messages.length > 0" class="message-list">
|
||||||
|
<li v-for="msg in messages" :key="msg.id" @click="selectMessage(msg)" :class="{ selected: selectedMessage && selectedMessage.id === msg.id }">
|
||||||
|
<div class="sender"><strong>发件人:</strong> {{ msg.sender }}</div>
|
||||||
|
<div class="subject"><strong>主题:</strong> {{ msg.subject }}</div>
|
||||||
|
<div class="time">{{ new Date(msg.received_at).toLocaleString() }}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else class="no-messages">
|
||||||
|
<p>{{ loading ? '正在加载...' : '收件箱是空的。' }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="selectedMessage" class="message-view">
|
||||||
|
<h3>{{ selectedMessage.subject }}</h3>
|
||||||
|
<p><strong>发件人:</strong> {{ selectedMessage.sender }}</p>
|
||||||
|
<p><strong>收件人:</strong> {{ currentEmail }}</p>
|
||||||
|
<p><strong>时间:</strong> {{ new Date(selectedMessage.received_at).toLocaleString() }}</p>
|
||||||
|
<hr>
|
||||||
|
<div v-html="selectedMessage.body" class="email-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const API_URL = '/api';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
randomEmail: '',
|
||||||
|
currentEmail: '',
|
||||||
|
messages: [],
|
||||||
|
selectedMessage: null,
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
domain: 'shenjianl.cn', // 从 info.md 获取
|
||||||
|
refreshInterval: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
generateRandomString(length) {
|
||||||
|
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
generateRandomEmail() {
|
||||||
|
const name = this.generateRandomString(8);
|
||||||
|
this.randomEmail = `${name}@${this.domain}`;
|
||||||
|
this.currentEmail = this.randomEmail;
|
||||||
|
this.messages = [];
|
||||||
|
this.selectedMessage = null;
|
||||||
|
this.fetchMessages();
|
||||||
|
},
|
||||||
|
async fetchMessages() {
|
||||||
|
if (!this.currentEmail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
this.error = null;
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${API_URL}/messages`, {
|
||||||
|
params: { recipient: this.currentEmail }
|
||||||
|
});
|
||||||
|
this.messages = response.data;
|
||||||
|
// 如果有新邮件,且当前选中了邮件,则更新选中的邮件内容
|
||||||
|
if (this.selectedMessage) {
|
||||||
|
const updatedSelected = this.messages.find(m => m.id === this.selectedMessage.id);
|
||||||
|
if (updatedSelected) {
|
||||||
|
this.selectedMessage = updatedSelected;
|
||||||
|
} else {
|
||||||
|
this.selectedMessage = null; // 如果邮件被删除,则取消选中
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.error = '无法加载邮件。请检查后端服务是否运行。';
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectMessage(message) {
|
||||||
|
this.selectedMessage = message;
|
||||||
|
},
|
||||||
|
copyToClipboard() {
|
||||||
|
navigator.clipboard.writeText(this.randomEmail).then(() => {
|
||||||
|
alert('邮箱地址已复制到剪贴板');
|
||||||
|
}, (err) => {
|
||||||
|
console.error('Could not copy text: ', err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startAutoRefresh() {
|
||||||
|
this.refreshInterval = setInterval(this.fetchMessages, 15000); // 每15秒刷新一次
|
||||||
|
},
|
||||||
|
stopAutoRefresh() {
|
||||||
|
if (this.refreshInterval) {
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.generateRandomEmail();
|
||||||
|
this.startAutoRefresh();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.stopAutoRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #f4f7f9;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-generator {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-generator input {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
background: #f9f9f9;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 15px;
|
||||||
|
border: none;
|
||||||
|
background-color: #42b983;
|
||||||
|
color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #36a374;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li {
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li:hover {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li.selected {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender, .subject, .time {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-messages {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-view {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fdfdfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-body {
|
||||||
|
margin-top: 15px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #e74c3c;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true
|
||||||
|
// devServer 代理配置已移除,所有代理均由外部 Nginx 处理。
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,349 @@
|
||||||
|
|
||||||
|
前端:**Vue**
|
||||||
|
后端:**Node.js + Express**
|
||||||
|
数据库:**MySQL**
|
||||||
|
邮件服务:**Mailu**
|
||||||
|
|
||||||
|
我帮你重新整理并生成**完整且清晰**的架构图、流程图、技术栈表 + 项目结构:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 **一、架构图(新版:Vue + Express)**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ 用户 │
|
||||||
|
│ ┌──────────────────────┐ │
|
||||||
|
│ │ mail.shenjianl.cn 前端网站 │ ←─────┐
|
||||||
|
│ │ (Vue) │ │
|
||||||
|
│ └──────────────────────┘ │
|
||||||
|
└────────────┬─────────────┘
|
||||||
|
│ HTTP (REST API)
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Node.js 后端 (Express) │
|
||||||
|
│ - 生成随机邮箱地址 │
|
||||||
|
│ - 查询收件箱 │
|
||||||
|
│ - 邮件接收解析写数据库 │
|
||||||
|
└────────────┬─────────────┘
|
||||||
|
│ MySQL
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ MySQL │
|
||||||
|
│ - emails 表 │
|
||||||
|
│ - email_attachments 表 │
|
||||||
|
└────────────┬─────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ 邮件服务器(Mailu) │
|
||||||
|
│ - SMTP 接收外部邮件 │
|
||||||
|
│ - Catch-all 所有收件人 │
|
||||||
|
│ - 调用 Node.js 脚本入库 │
|
||||||
|
└────────────┬─────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ DNS │
|
||||||
|
│ - MX: mail.shenjianl.cn │
|
||||||
|
│ - A: mail.shenjianl.cn → IP │
|
||||||
|
└────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 **二、流程图:接收 & 查看流程**
|
||||||
|
|
||||||
|
```
|
||||||
|
【外部发件人】
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
发邮件到 abc123@shenjianl.cn
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
DNS MX 查找 mail.shenjianl.cn
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Mailu 收到邮件 (SMTP)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
调用 Node.js 脚本 (saveEmail.js)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
simpleParser 解析邮件内容
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
写入 MySQL:
|
||||||
|
recipient, sender, subject, body, received_at
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
用户访问网页:
|
||||||
|
mail.shenjianl.cn
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Vue 前端请求后端 API:
|
||||||
|
GET /api/messages?recipient=abc123@shenjianl.cn
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
后端查询 MySQL 返回邮件列表
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
前端展示收件箱
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙ **三、技术栈清单(新版)**
|
||||||
|
|
||||||
|
| 层级 | 功能 | 技术选型 |
|
||||||
|
| -------- | -------------- | ----------------------------------- |
|
||||||
|
| 域名 & DNS | MX/A 解析 | 阿里云 DNS / Cloudflare |
|
||||||
|
| 邮件服务器 | 接收所有外部邮件 | Mailu(基于 Postfix/Dovecot) |
|
||||||
|
| 邮件解析 | 提取收件人/正文/主题/附件 | Node.js (mailparser) |
|
||||||
|
| 后端 API | 生成邮箱 / 查询收件箱 | Node.js + Express |
|
||||||
|
| 数据库 | 存储邮件信息 | MySQL |
|
||||||
|
| 前端 | 用户界面 / 展示收件箱 | Vue.js |
|
||||||
|
| 部署 & 管理 | 容器化 / 部署 | Docker + docker-compose |
|
||||||
|
| 安全 | HTTPS / 防滥用 | Let's Encrypt + Nginx 反代 + IP限流/黑名单 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠 **四、项目结构建议(新版)**
|
||||||
|
|
||||||
|
```
|
||||||
|
📦 project-root
|
||||||
|
├── frontend/ # Vue 前端
|
||||||
|
│ └── (组件/页面/接口)
|
||||||
|
├── backend/ # Node.js 后端
|
||||||
|
│ ├── app.js # Express 主程序
|
||||||
|
│ ├── saveEmail.js # 接收邮件脚本:解析并写入数据库
|
||||||
|
│ └── db.js # 数据库连接
|
||||||
|
├── docker-compose.yml # Mailu + Node.js + MySQL
|
||||||
|
├── mailu.env # Mailu 配置
|
||||||
|
├── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ **总结:**
|
||||||
|
|
||||||
|
* 前端:Vue 只负责界面和展示
|
||||||
|
* 后端:Express 提供 API + 接收邮件写 MySQL
|
||||||
|
* 邮件服务器:Mailu catch-all 接收所有地址
|
||||||
|
* DNS:配置 MX / A 指向服务器
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
好的!
|
||||||
|
下面我给你**直接写好**适配新版架构的:
|
||||||
|
✅ `docker-compose.yml`(包含:Vue 前端 + Node.js Express 后端 + MySQL + Mailu 邮件服务器)
|
||||||
|
✅ MySQL 建表 SQL
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ **1️⃣ docker-compose.yml**
|
||||||
|
|
||||||
|
> ⚠️ 注:
|
||||||
|
|
||||||
|
* Vue 前端用 `npm run serve` 或 `npm run build` 后放 Nginx 也行,这里为了简单直接跑开发服务
|
||||||
|
* Mailu 是官方 Docker 邮件套件,需要 `mailu.env` 配置
|
||||||
|
* 建议生产用 HTTPS + Nginx,下面只是开发演示架构
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8
|
||||||
|
container_name: mail_mysql
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: myrootpassword
|
||||||
|
MYSQL_DATABASE: maildb
|
||||||
|
MYSQL_USER: mailuser
|
||||||
|
MYSQL_PASSWORD: mailpassword
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
container_name: mail_backend
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
DB_HOST: mysql
|
||||||
|
DB_USER: mailuser
|
||||||
|
DB_PASSWORD: mailpassword
|
||||||
|
DB_NAME: maildb
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
container_name: mail_frontend
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
mailu:
|
||||||
|
image: mailu/mailu:master
|
||||||
|
container_name: mail_server
|
||||||
|
restart: always
|
||||||
|
env_file: mailu.env
|
||||||
|
ports:
|
||||||
|
- "25:25" # SMTP
|
||||||
|
- "465:465" # SMTPS
|
||||||
|
- "143:143" # IMAP
|
||||||
|
- "993:993" # IMAPS
|
||||||
|
- "8081:80" # Mailu admin web UI(可选)
|
||||||
|
volumes:
|
||||||
|
- mailu_data:/data
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
mailu_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 **2️⃣ 建表 SQL**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE emails (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
recipient VARCHAR(255) NOT NULL,
|
||||||
|
sender VARCHAR(255) NOT NULL,
|
||||||
|
subject VARCHAR(512),
|
||||||
|
body TEXT,
|
||||||
|
received_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
raw MEDIUMBLOB
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE email_attachments (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
email_id BIGINT NOT NULL,
|
||||||
|
filename VARCHAR(255),
|
||||||
|
content_type VARCHAR(128),
|
||||||
|
content LONGBLOB,
|
||||||
|
FOREIGN KEY (email_id) REFERENCES emails(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 **3️⃣ mailu.env**(示例配置)
|
||||||
|
|
||||||
|
```env
|
||||||
|
DOMAIN=shenjianl.cn
|
||||||
|
HOSTNAMES=mail.shenjianl.cn
|
||||||
|
SECRET_KEY=change-this-to-a-very-random-secret
|
||||||
|
ADMIN=admin
|
||||||
|
ADMIN_PASSWORD=adminpassword
|
||||||
|
DB_FLAVOR=mysql
|
||||||
|
DB_HOST=mysql
|
||||||
|
DB_USER=mailuser
|
||||||
|
DB_PASSWORD=mailpassword
|
||||||
|
DB_NAME=maildb
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ **4️⃣ 项目结构(推荐)**
|
||||||
|
|
||||||
|
```
|
||||||
|
📦 project-root
|
||||||
|
├── frontend/ # Vue 前端
|
||||||
|
│ └── (页面/组件)
|
||||||
|
├── backend/ # Node.js Express 后端
|
||||||
|
│ ├── app.js # 提供 API
|
||||||
|
│ ├── saveEmail.js # 接收邮件脚本
|
||||||
|
│ └── db.js # 数据库连接
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── mailu.env
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✨ **这样就完成了:**
|
||||||
|
|
||||||
|
* Vue 前端对接后端 API
|
||||||
|
* 后端(Express)接收请求、存储/查询邮件
|
||||||
|
* Mailu 负责接收 SMTP 邮件并调用脚本写数据库
|
||||||
|
* MySQL 存储邮件数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
以下是针对你域名 `shenjianl.cn` 的**简单版 SPF、DKIM、DMARC DNS TXT 记录示范**,你可以直接复制,去你买域名的DNS管理后台添加。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. SPF 记录
|
||||||
|
|
||||||
|
* **类型**:TXT
|
||||||
|
* **主机名**:`@` (代表根域名 shenjianl.cn)
|
||||||
|
* **值(内容)**:
|
||||||
|
|
||||||
|
```
|
||||||
|
v=spf1 mx ip4:你的服务器公网IP -all
|
||||||
|
```
|
||||||
|
|
||||||
|
> 把 `你的服务器公网IP` 换成你实际服务器的外网IP地址。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. DKIM 记录(示例)
|
||||||
|
|
||||||
|
假设你的邮件服务器给你生成了一个 selector,叫做 `default`,那么:
|
||||||
|
|
||||||
|
* **类型**:TXT
|
||||||
|
* **主机名**:`default._domainkey`
|
||||||
|
* **值(内容)**:
|
||||||
|
|
||||||
|
```
|
||||||
|
v=DKIM1; k=rsa; p=这里填你的公钥内容
|
||||||
|
```
|
||||||
|
|
||||||
|
> 注意:`p=` 后面是一个很长的字符串,由邮件服务器生成的公钥。
|
||||||
|
> 你可以让 Mailu 生成 DKIM 密钥,然后复制粘贴到这里。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. DMARC 记录
|
||||||
|
|
||||||
|
* **类型**:TXT
|
||||||
|
* **主机名**:`_dmarc`
|
||||||
|
* **值(内容)**:
|
||||||
|
|
||||||
|
```
|
||||||
|
v=DMARC1; p=quarantine; rua=mailto:admin@shenjianl.cn; pct=100
|
||||||
|
```
|
||||||
|
|
||||||
|
解释:
|
||||||
|
|
||||||
|
* `p=quarantine`:不合规邮件放到垃圾箱
|
||||||
|
* `rua=mailto:admin@shenjianl.cn`:每隔一段时间会收到报告(你可以换成自己的邮箱)
|
||||||
|
* `pct=100`:所有邮件都应用该规则
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 总结
|
||||||
|
|
||||||
|
| 类型 | 主机名 | 内容示例 |
|
||||||
|
| --- | ------------------- | ---------------------------------------------------------------- |
|
||||||
|
| TXT | @ | `v=spf1 mx ip4:你的服务器公网IP -all` |
|
||||||
|
| TXT | default.\_domainkey | `v=DKIM1; k=rsa; p=你的公钥` |
|
||||||
|
| TXT | \_dmarc | `v=DMARC1; p=quarantine; rua=mailto:admin@shenjianl.cn; pct=100` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
如果你还没有 DKIM 公钥或者不确定怎么生成,可以告诉我,我帮你写生成步骤。
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
SECRET_KEY=d4f7a1b3c2e987f05a6d3b4c8e1f2097
|
||||||
|
DOMAIN=shenjianl.cn
|
||||||
|
HOSTNAMES=mail.shenjianl.cn
|
||||||
|
POSTMASTER=admin
|
||||||
|
|
||||||
|
DB_FLAVOR=mysql
|
||||||
|
DB_HOST=43.143.145.172
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=kyff145972
|
||||||
|
DB_NAME=maildb
|
||||||
|
|
||||||
|
WEB_ADMIN=/mailu
|
||||||
|
WEB_WEBMAIL=/mailu/webmail
|
||||||
|
|
||||||
|
ENABLE_UNPRIVILEGED_USER=false
|
||||||
|
PASSWORD_SCHEME=BLF-CRYPT
|
||||||
|
ENABLE_ADMIN=true
|
||||||
|
ENABLE_WEBMAIL=true
|
||||||
|
ENABLE_WEBDAV=false
|
||||||
|
ENABLE_ANTIVIRUS=false
|
||||||
|
ENABLE_ANTISPAM=true
|
||||||
|
DEFAULT_QUOTA=2G
|
||||||
|
ADMIN_PASSWORD=admin
|
||||||
|
|
||||||
|
TLS_FLAVOR=notls
|
||||||
|
MESSAGE_SIZE_LIMIT=20000000
|
||||||
Loading…
Reference in New Issue