1.0服务器端完成

pull/1/head
zwq 4 months ago
parent fe37b27513
commit 9b44622099

@ -30,7 +30,14 @@ docker-compose up -d
sleep 15
```
### 3. 查看数据库phpMyAdmin
### 3. 初始化管理功能数据库表(首次使用)
```bash
# 执行管理功能相关的数据库表创建
docker-compose exec mysql mysql -umail_user -puser123 mail_server < scripts/create_admin_tables.sql
```
### 4. 查看数据库phpMyAdmin
- 访问http://localhost:8088
- 登录信息:
@ -38,7 +45,7 @@ sleep 15
- 用户名:`root`
- 密码:`root123`
### 4. 测试账号
### 5. 测试账号
- 管理员:`admin@test.com` / `123456`
- 普通用户:`user1@test.com` / `123456`
@ -48,6 +55,7 @@ sleep 15
- **25** - SMTP服务器发送邮件
- **110** - POP3服务器接收邮件
- **3308** - MySQL数据库
- **8080** - Web管理后台
- **8088** - phpMyAdmin管理界面
## 启动服务器
@ -66,6 +74,96 @@ sudo php scripts/start_pop3.php
**注意**两个服务器需要分别在两个终端运行都需要sudo权限因为使用25和110端口
## Web管理后台
### 启动Web服务器
**方式1推荐从public目录启动**
```bash
cd /mnt/d/mailserver/mailserver/public
php -S localhost:8080
```
**方式2从项目根目录启动**
```bash
cd /mnt/d/mailserver/mailserver
php -S localhost:8080 -t public
```
### 访问管理后台
- 访问http://localhost:8080
- 登录账号:
- 管理员:`admin@test.com` / `123456`
- 普通用户:`user1@test.com` / `123456`
### 功能模块
#### 1. 用户注册
- **页面**`register.php`
- **功能**:新用户注册,邮箱域名限制为 @test.com
#### 2. 用户管理(管理员)
- **页面**`users.php`
- **功能**
- 创建新用户(设置密码、管理员权限、激活状态)
- 编辑用户信息(修改密码、权限、状态)
- 删除用户账号
- 查看用户列表
#### 3. 邮件管理
- **页面**`emails.php`
- **功能**
- 查看邮件(管理员查看全部,普通用户查看自己的收件箱)
- 查看邮件详情
- 标记邮件为已读
- 删除邮件
- 分页浏览
#### 4. 群发邮件(管理员)
- **页面**`broadcast.php`
- **功能**
- 发送给所有用户
- 发送给指定用户列表
- 自定义邮件主题和内容
#### 5. 过滤规则
- **页面**`filters.php`
- **功能**
- 创建邮箱过滤规则(阻止/允许特定邮箱)
- 创建IP地址过滤规则阻止/允许特定IP
- 启用/禁用过滤规则
- 删除过滤规则
#### 6. 系统设置(管理员)
- **页面**`settings.php`
- **功能**
- 设置SMTP端口默认25
- 设置POP3端口默认110
- 设置服务器域名默认test.com
- 设置用户邮箱大小限制
- 设置日志存储路径和最大大小
- 修改管理员密码
#### 7. 服务管理(管理员)
- **页面**`services.php`
- **功能**
- 查看SMTP服务状态
- 查看POP3服务状态
- 启动/停止服务(状态管理)
#### 8. 日志管理
- **页面**`logs.php`
- **功能**
- 查看所有日志
- 按类型过滤SMTP/POP3
- 查看日志统计信息
- 清除日志(管理员)
#### 9. 帮助
- **页面**`help.php`
- **功能**:提供系统使用帮助文档
## 测试方法
### 测试SMTP发送邮件
@ -117,70 +215,39 @@ RETR 1
QUIT
```
## Web管理后台
### 测试Web管理后台
### 启动Web服务器
1. **用户注册测试**
- 访问http://localhost:8080/register.php
- 注册新用户:`newuser@test.com` / `123456`
- 预期:注册成功,跳转到登录页
#### 测试步骤
2. **用户管理测试(管理员)**
- 登录管理员账号
- 访问http://localhost:8080/users.php
- 创建、编辑、删除用户
3. **群发邮件测试(管理员)**
- 访问http://localhost:8080/broadcast.php
- 选择"发送给所有用户"或"发送给指定用户"
- 填写主题和内容,发送
- 预期:显示成功发送数量
4. **系统设置测试(管理员)**
- 访问http://localhost:8080/settings.php
- 修改端口、域名、邮箱大小等设置
- 修改管理员密码
5. **过滤规则测试**
- 访问http://localhost:8080/filters.php
- 创建邮箱过滤规则和IP过滤规则
- 测试启用/禁用功能
6. **日志管理测试**
- 访问http://localhost:8080/logs.php
- 查看日志列表,按类型过滤
- 测试清除日志功能(管理员)
**1. 启动 Web 服务器(如果还没启动):**
```
cd /mnt/d/mailserver/mailserverphp -S localhost:8080 -t public
```
**2. 测试退出登录功能:**
```
访问 http://localhost:8080/index.php
使用 admin@test.com / 123456 登录
点击右上角"退出"链接
预期:跳转到登录页面,会话已清除
```
**3. 测试用户管理页面(管理员功能):**
```
登录管理员账号 admin@test.com
访问 http://localhost:8080/users.php 或点击菜单"用户管理"
测试创建用户:
邮箱testuser2@test.com
密码123456
勾选"管理员"和"激活"
点击"创建用户"
预期:用户创建成功,出现在列表中
测试编辑用户:
点击某个用户的"编辑"按钮
修改密码、管理员状态、激活状态
点击"保存"
预期:用户信息更新成功
测试删除用户:
点击某个用户的"删除"按钮
确认删除
预期:用户从列表中消失(不能删除自己)
```
**4. 测试邮件管理页面:**
```
访问 http://localhost:8080/emails.php 或点击菜单"邮件管理"
管理员:显示所有邮件
普通用户:只显示自己的收件箱
测试查看邮件:
点击邮件主题或"查看"按钮
预期:弹出模态框显示邮件详情
测试标记已读:
点击未读邮件的"标记已读"
预期:邮件状态变为"已读"
测试删除邮件:
点击"删除"按钮
确认删除
预期:邮件从列表中消失
测试分页如果有超过20封邮件
点击页码或"上一页"/"下一页"
预期:正确切换页面
```
**5. 测试权限控制:**
```
使用普通用户 user1@test.com / 123456 登录
尝试访问 http://localhost:8080/users.php
预期:显示"权限不足:只有管理员可以访问此页面"
访问 http://localhost:8080/emails.php
预期:只显示该用户的收件箱邮件
```
## 查看数据
### 方法1phpMyAdmin推荐
@ -201,6 +268,8 @@ docker-compose exec mysql mysql -umail_user -puser123 mail_server -e "SELECT id,
docker-compose down -v
docker-compose up -d
sleep 15
# 重新执行初始化脚本
docker-compose exec mysql mysql -umail_user -puser123 mail_server < scripts/create_admin_tables.sql
```
## 常见问题
@ -253,42 +322,139 @@ nc localhost 25
```
mailserver/
├── scripts/ # 启动脚本和SQL
│ ├── start_smtp.php # 启动SMTP服务器
│ ├── start_pop3.php # 启动POP3服务器
│ └── create_tables.sql # 数据库初始化脚本
├── src/ # 源代码
│ ├── protocol/ # SMTP/POP3协议实现
├── scripts/ # 启动脚本和SQL
│ ├── start_smtp.php # 启动SMTP服务器
│ ├── start_pop3.php # 启动POP3服务器
│ ├── create_tables.sql # 数据库初始化脚本
│ └── create_admin_tables.sql # 管理功能数据库表
├── src/ # 源代码
│ ├── protocol/ # SMTP/POP3协议实现
│ │ ├── SmtpServer.php
│ │ └── Pop3Server.php
│ ├── storage/ # 数据存储层
│ └── admin/ # 管理后台逻辑
├── public/ # Web管理界面
│ └── index.php # 主页面(登录+仪表盘)
├── config/ # 配置文件
│ ├── database.php # 数据库配置
│ └── constants.php # 常量定义
└── docker-compose.yml # Docker配置
│ ├── storage/ # 数据存储层
│ │ ├── Database.php
│ │ ├── UserRepository.php
│ │ ├── EmailRepository.php
│ │ ├── SystemSettingsRepository.php
│ │ ├── FilterRepository.php
│ │ ├── ServiceRepository.php
│ │ └── MailboxRepository.php
│ ├── admin/ # 管理后台逻辑
│ │ └── BroadcastService.php
│ └── utils/ # 工具类
│ ├── Security.php
│ └── Validator.php
├── public/ # Web管理界面
│ ├── index.php # 主页面(登录+仪表盘)
│ ├── register.php # 用户注册
│ ├── logout.php # 退出登录
│ ├── users.php # 用户管理
│ ├── emails.php # 邮件管理
│ ├── broadcast.php # 群发邮件
│ ├── filters.php # 过滤规则
│ ├── settings.php # 系统设置
│ ├── services.php # 服务管理
│ ├── logs.php # 日志管理
│ └── help.php # 帮助
├── config/ # 配置文件
│ ├── database.php # 数据库配置
│ └── constants.php # 常量定义
└── docker-compose.yml # Docker配置
```
## 功能完成情况
### ✅ 服务器端功能(已完成)
根据课程设计说明书要求,服务器端功能已全部实现:
1. **✅ 邮箱管理**
- 设置用户邮箱大小限制
- 查看用户邮箱使用情况
2. **✅ 客户管理**
- 创建新客户账号和密码
- 设置用户权限(管理员/普通用户)
- 启用/禁用用户
- 删除客户账号
- 编辑用户信息
3. **✅ 服务起停**
- SMTP服务状态管理
- POP3服务状态管理
- 服务启动/停止控制
4. **✅ 系统设置**
- SMTP端口设置默认25
- POP3端口设置默认110
- 服务器域名设置默认test.com
- 管理员密码修改
- 邮件过滤(账号过滤)
- IP地址过滤
5. **✅ 日志管理**
- SMTP日志查看
- POP3日志查看
- 日志清除功能
- 日志存储位置设置
- 日志文件大小管理
6. **✅ 日常管理**
- 群发邮件功能(发送给所有用户或指定用户)
7. **✅ 帮助**
- 系统使用帮助文档
### ❌ 移动客户端功能(未完成)
根据课程设计说明书要求Android移动客户端尚未实现
1. **❌ 邮件的操作**
- 邮件的发送
- 邮件的接收
- 邮件的删除
2. **❌ 用户管理**
- 用户修改自己邮箱的账户密码
- 新用户注册功能
3. **❌ 管理员管理**
- 管理员远程登录
- 客户端用户管理(创建、删除、授权、消权、禁用)
## 简要操作指南
### 初始化项目
```bash
# 1. 启动数据库
cd /mnt/d/mailserver/mailserver
docker-compose up -d
sleep 15
# 2. 初始化管理功能数据库表
docker-compose exec mysql mysql -umail_user -puser123 mail_server < scripts/create_admin_tables.sql
# 3. 启动Web服务器
php -S localhost:8080 -t public
```
## 功能列表
### 日常使用
### 已实现功能
```bash
# 启动SMTP服务器终端1
sudo php scripts/start_smtp.php
- ✅ SMTP邮件发送服务器
- ✅ POP3邮件接收服务器
- ✅ 用户密码验证
- ✅ Web管理后台登录、仪表盘
- ✅ 数据库存储MySQL
# 启动POP3服务器终端2
sudo php scripts/start_pop3.php
# 启动Web管理后台终端3
php -S localhost:8080 -t public
```
### 待实现功能
### 访问地址
- ⏳ 用户注册功能
- ⏳ 用户管理页面users.php
- ⏳ 邮件管理页面emails.php
- ⏳ 管理员群发邮件
- ⏳ 服务器参数设置
- ⏳ 邮件过滤功能
- ⏳ IP地址过滤功能
- ⏳ 系统日志记录和查看
- **Web管理后台**http://localhost:8080
- **phpMyAdmin**http://localhost:8088
- **SMTP服务器**localhost:25
- **POP3服务器**localhost:110

@ -13,7 +13,7 @@ services:
- "3308:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./scripts/create_tables.sql:/docker-entrypoint-initdb.d/init.sql
- ./scripts/create_all_tables.sql:/docker-entrypoint-initdb.d/init.sql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci

@ -0,0 +1,193 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/storage/EmailRepository.php';
require_once __DIR__ . '/../src/admin/BroadcastService.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// 检查管理员权限
if (!$_SESSION['is_admin']) {
die('权限不足:只有管理员可以访问此页面');
}
$broadcastService = new BroadcastService();
$userRepo = new UserRepository();
$message = '';
$error = '';
// 处理群发邮件
if (isset($_POST['send_broadcast'])) {
$subject = trim($_POST['subject'] ?? '');
$body = trim($_POST['body'] ?? '');
$broadcastType = $_POST['broadcast_type'] ?? 'all';
$recipients = trim($_POST['recipients'] ?? '');
if (empty($subject)) {
$error = "主题不能为空";
} elseif (empty($body)) {
$error = "邮件内容不能为空";
} else {
$senderEmail = $_SESSION['username'];
try {
if ($broadcastType === 'all') {
// 群发给所有用户
$result = $broadcastService->broadcastToAll($senderEmail, $subject, $body);
} else {
// 群发给指定用户
$recipientList = array_filter(array_map('trim', explode(',', $recipients)));
if (empty($recipientList)) {
$error = "请指定收件人";
} else {
$result = $broadcastService->broadcastToUsers($senderEmail, $recipientList, $subject, $body);
}
}
if (isset($result)) {
if ($result['success'] > 0) {
$message = "群发成功!成功发送 {$result['success']} 封邮件";
if ($result['failed'] > 0) {
$message .= ",失败 {$result['failed']} 封";
}
if (!empty($result['errors'])) {
$error = "部分失败:" . implode('<br>', array_slice($result['errors'], 0, 5));
if (count($result['errors']) > 5) {
$error .= "<br>... 还有 " . (count($result['errors']) - 5) . " 个错误";
}
}
} else {
$error = "群发失败:" . implode('<br>', $result['errors']);
}
}
} catch (Exception $e) {
$error = "群发失败: " . $e->getMessage();
}
}
}
// 获取所有用户列表(用于选择收件人)
$allUsers = $userRepo->getAll();
?>
<!DOCTYPE html>
<html>
<head>
<title>群发邮件 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 800px; }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.form-group { margin-bottom: 20px; }
.form-group label { display: block; margin-bottom: 8px; font-weight: 500; }
.form-group input, .form-group textarea, .form-group select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.form-group textarea { min-height: 200px; font-family: monospace; }
.form-group small { color: #666; font-size: 12px; }
.btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; }
.btn-primary { background: #007bff; color: white; }
.btn-primary:hover { background: #0056b3; }
.radio-group { display: flex; gap: 20px; margin-bottom: 15px; }
.radio-group label { display: flex; align-items: center; cursor: pointer; }
.radio-group input[type="radio"] { width: auto; margin-right: 5px; }
.user-list { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; border-radius: 4px; background: #f8f9fa; }
.user-list-item { padding: 5px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<a href="emails.php">邮件管理</a>
<a href="broadcast.php">群发邮件</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<a href="settings.php">系统设置</a>
</div>
<div class="container">
<h2>群发邮件</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<form method="POST">
<div class="form-group">
<label>发送方式</label>
<div class="radio-group">
<label>
<input type="radio" name="broadcast_type" value="all" checked onchange="toggleRecipients()">
发送给所有用户
</label>
<label>
<input type="radio" name="broadcast_type" value="selected" onchange="toggleRecipients()">
发送给指定用户
</label>
</div>
</div>
<div class="form-group" id="recipients-group" style="display: none;">
<label>收件人(多个邮箱用逗号分隔)</label>
<input type="text" name="recipients" placeholder="user1@test.com, user2@test.com">
<small>请输入邮箱地址,多个邮箱用逗号分隔</small>
<div class="user-list">
<strong>可用用户列表:</strong>
<?php foreach ($allUsers as $user): ?>
<?php if ($user['is_active'] && $user['username'] !== $_SESSION['username']): ?>
<div class="user-list-item"><?php echo htmlspecialchars($user['username']); ?></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<div class="form-group">
<label>邮件主题 *</label>
<input type="text" name="subject" required placeholder="请输入邮件主题" value="<?php echo htmlspecialchars($_POST['subject'] ?? ''); ?>">
</div>
<div class="form-group">
<label>邮件内容 *</label>
<textarea name="body" required placeholder="请输入邮件内容"><?php echo htmlspecialchars($_POST['body'] ?? ''); ?></textarea>
</div>
<button type="submit" name="send_broadcast" class="btn btn-primary">发送群发邮件</button>
</form>
</div>
<script>
function toggleRecipients() {
const broadcastType = document.querySelector('input[name="broadcast_type"]:checked').value;
const recipientsGroup = document.getElementById('recipients-group');
if (broadcastType === 'selected') {
recipientsGroup.style.display = 'block';
} else {
recipientsGroup.style.display = 'none';
}
}
</script>
</body>
</html>

@ -0,0 +1,243 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/FilterRepository.php';
require_once __DIR__ . '/../src/utils/Validator.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$filterRepo = new FilterRepository();
$message = '';
$error = '';
// 处理创建过滤规则
if (isset($_POST['create_filter'])) {
$ruleType = $_POST['rule_type'] ?? '';
$ruleValue = trim($_POST['rule_value'] ?? '');
$action = $_POST['action'] ?? 'block';
$description = trim($_POST['description'] ?? '');
if (empty($ruleValue)) {
$error = "规则值不能为空";
} else {
if ($ruleType === 'email') {
if (!Validator::validateEmail($ruleValue)) {
$error = "邮箱格式无效";
}
} elseif ($ruleType === 'ip') {
if (!Validator::validateIP($ruleValue)) {
$error = "IP地址格式无效";
}
} else {
$error = "规则类型无效";
}
if (empty($error)) {
try {
if ($filterRepo->create($ruleType, $ruleValue, $action, $description)) {
$message = "过滤规则创建成功";
} else {
$error = "创建失败,可能已存在相同规则";
}
} catch (Exception $e) {
$error = "创建失败: " . $e->getMessage();
}
}
}
}
// 处理删除规则
if (isset($_GET['delete'])) {
$id = (int)$_GET['delete'];
if ($filterRepo->delete($id)) {
$message = "规则删除成功";
} else {
$error = "删除失败";
}
}
// 处理切换规则状态
if (isset($_GET['toggle'])) {
$id = (int)$_GET['toggle'];
$rule = $filterRepo->getAll();
$currentRule = null;
foreach ($rule as $r) {
if ($r['id'] == $id) {
$currentRule = $r;
break;
}
}
if ($currentRule) {
$newStatus = !$currentRule['is_active'];
if ($filterRepo->updateStatus($id, $newStatus)) {
$message = "规则状态已更新";
} else {
$error = "更新失败";
}
}
}
// 获取所有规则
$rules = $filterRepo->getAll();
?>
<!DOCTYPE html>
<html>
<head>
<title>过滤规则 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; }
.form-group input, .form-group select, .form-group textarea { width: 100%; max-width: 500px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.form-inline { display: flex; gap: 10px; align-items: flex-end; }
.form-inline .form-group { flex: 1; margin-bottom: 0; }
.btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-primary { background: #007bff; color: white; }
.btn-danger { background: #dc3545; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-warning { background: #ffc107; color: #000; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background: #f8f9fa; }
.badge { padding: 4px 8px; border-radius: 3px; font-size: 12px; font-weight: 500; }
.badge-email { background: #17a2b8; color: white; }
.badge-ip { background: #6c757d; color: white; }
.badge-block { background: #dc3545; color: white; }
.badge-allow { background: #28a745; color: white; }
.badge-active { background: #28a745; color: white; }
.badge-inactive { background: #6c757d; color: white; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="users.php">用户管理</a>
<?php endif; ?>
<a href="emails.php">邮件管理</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="settings.php">系统设置</a>
<?php endif; ?>
</div>
<div class="container">
<h2>过滤规则管理</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- 创建过滤规则 -->
<h3>创建过滤规则</h3>
<form method="POST" class="form-inline">
<div class="form-group">
<label>规则类型</label>
<select name="rule_type" required>
<option value="email">邮箱过滤</option>
<option value="ip">IP地址过滤</option>
</select>
</div>
<div class="form-group">
<label>规则值</label>
<input type="text" name="rule_value" placeholder="邮箱或IP地址" required>
</div>
<div class="form-group">
<label>动作</label>
<select name="action" required>
<option value="block">阻止</option>
<option value="allow">允许</option>
</select>
</div>
<div class="form-group">
<label>描述</label>
<input type="text" name="description" placeholder="规则描述(可选)">
</div>
<div class="form-group">
<button type="submit" name="create_filter" class="btn btn-primary">创建规则</button>
</div>
</form>
<!-- 规则列表 -->
<h3>过滤规则列表 (<?php echo count($rules); ?>)</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>类型</th>
<th>规则值</th>
<th>动作</th>
<th>描述</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($rules)): ?>
<tr>
<td colspan="8" style="text-align: center; padding: 40px;">暂无过滤规则</td>
</tr>
<?php else: ?>
<?php foreach ($rules as $rule): ?>
<tr>
<td><?php echo $rule['id']; ?></td>
<td>
<span class="badge badge-<?php echo $rule['rule_type']; ?>">
<?php echo $rule['rule_type'] === 'email' ? '邮箱' : 'IP'; ?>
</span>
</td>
<td><?php echo htmlspecialchars($rule['rule_value']); ?></td>
<td>
<span class="badge badge-<?php echo $rule['action']; ?>">
<?php echo $rule['action'] === 'block' ? '阻止' : '允许'; ?>
</span>
</td>
<td><?php echo htmlspecialchars($rule['description'] ?? '-'); ?></td>
<td>
<span class="badge badge-<?php echo $rule['is_active'] ? 'active' : 'inactive'; ?>">
<?php echo $rule['is_active'] ? '激活' : '禁用'; ?>
</span>
</td>
<td><?php echo $rule['created_at']; ?></td>
<td>
<a href="?toggle=<?php echo $rule['id']; ?>" class="btn btn-warning">
<?php echo $rule['is_active'] ? '禁用' : '启用'; ?>
</a>
<a href="?delete=<?php echo $rule['id']; ?>" class="btn btn-danger" onclick="return confirm('确定要删除此规则吗?');">删除</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</body>
</html>

@ -0,0 +1,196 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>帮助 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 900px; }
.section { margin-bottom: 30px; }
.section h3 { color: #007bff; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
.section h4 { color: #333; margin-top: 20px; }
.code-block { background: #f8f9fa; padding: 15px; border-radius: 5px; border-left: 4px solid #007bff; margin: 10px 0; font-family: monospace; }
ul, ol { line-height: 1.8; }
.highlight { background: #fff3cd; padding: 2px 4px; border-radius: 3px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="users.php">用户管理</a>
<?php endif; ?>
<a href="emails.php">邮件管理</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="settings.php">系统设置</a>
<?php endif; ?>
<a href="help.php">帮助</a>
</div>
<div class="container">
<h2>使用帮助</h2>
<div class="section">
<h3>系统概述</h3>
<p>这是一个基于POP3和SMTP协议的邮件服务器管理系统支持用户注册、邮件收发、系统管理等功能。</p>
</div>
<div class="section">
<h3>功能模块</h3>
<h4>1. 用户管理(管理员功能)</h4>
<ul>
<li><strong>创建用户:</strong>可以创建新的用户账号,设置密码、管理员权限和激活状态</li>
<li><strong>编辑用户:</strong>可以修改用户密码、权限和状态</li>
<li><strong>删除用户:</strong>可以删除用户账号(不能删除自己)</li>
<li><strong>用户列表:</strong>查看所有注册用户及其状态</li>
</ul>
<h4>2. 邮件管理</h4>
<ul>
<li><strong>查看邮件:</strong>管理员可以查看所有邮件,普通用户只能查看自己的收件箱</li>
<li><strong>标记已读:</strong>将未读邮件标记为已读</li>
<li><strong>删除邮件:</strong>删除不需要的邮件(软删除)</li>
<li><strong>邮件详情:</strong>点击邮件主题查看完整内容</li>
</ul>
<h4>3. 群发邮件(管理员功能)</h4>
<ul>
<li><strong>发送给所有用户:</strong>可以一次性向所有激活用户发送通知邮件</li>
<li><strong>发送给指定用户:</strong>可以选择特定用户进行群发</li>
<li><strong>邮件内容:</strong>支持自定义主题和内容</li>
</ul>
<h4>4. 过滤规则</h4>
<ul>
<li><strong>邮箱过滤:</strong>可以阻止或允许特定邮箱地址</li>
<li><strong>IP过滤</strong>可以阻止或允许特定IP地址</li>
<li><strong>规则管理:</strong>可以启用、禁用或删除过滤规则</li>
</ul>
<h4>5. 系统设置(管理员功能)</h4>
<ul>
<li><strong>端口设置:</strong>配置SMTP端口默认25和POP3端口默认110</li>
<li><strong>域名设置:</strong>设置邮件服务器域名默认test.com</li>
<li><strong>邮箱管理:</strong>设置用户邮箱大小限制</li>
<li><strong>日志设置:</strong>配置日志存储路径和最大大小</li>
<li><strong>密码修改:</strong>管理员可以修改自己的密码</li>
</ul>
<h4>6. 服务管理(管理员功能)</h4>
<ul>
<li><strong>SMTP服务</strong>查看和管理SMTP服务状态</li>
<li><strong>POP3服务</strong>查看和管理POP3服务状态</li>
<li><strong>服务起停:</strong>启动或停止邮件服务</li>
</ul>
<h4>7. 日志管理</h4>
<ul>
<li><strong>查看日志:</strong>查看SMTP和POP3服务器日志</li>
<li><strong>日志过滤:</strong>按类型过滤日志(全部/SMTP/POP3</li>
<li><strong>清除日志:</strong>管理员可以清除日志记录</li>
</ul>
</div>
<div class="section">
<h3>启动服务器</h3>
<p>要启动邮件服务器,需要在命令行执行以下命令:</p>
<div class="code-block">
# 启动SMTP服务器需要sudo权限<br>
sudo php scripts/start_smtp.php<br><br>
# 启动POP3服务器需要sudo权限<br>
sudo php scripts/start_pop3.php
</div>
<p><span class="highlight">注意:</span>两个服务器需要分别在两个终端运行。</p>
</div>
<div class="section">
<h3>测试邮件服务器</h3>
<h4>测试SMTP发送邮件</h4>
<div class="code-block">
telnet localhost 25<br>
HELO test<br>
MAIL FROM: &lt;user1@test.com&gt;<br>
RCPT TO: &lt;admin@test.com&gt;<br>
DATA<br>
Subject: 测试邮件<br>
From: user1@test.com<br>
To: admin@test.com<br><br>
这是一封测试邮件!<br>
.<br>
QUIT
</div>
<h4>测试POP3接收邮件</h4>
<div class="code-block">
telnet localhost 110<br>
USER admin@test.com<br>
PASS 123456<br>
STAT<br>
LIST<br>
RETR 1<br>
QUIT
</div>
</div>
<div class="section">
<h3>常见问题</h3>
<h4>Q: 端口被占用怎么办?</h4>
<p>A: 检查端口占用情况:</p>
<div class="code-block">
sudo netstat -tlnp | grep 25 # 检查SMTP端口<br>
sudo netstat -tlnp | grep 110 # 检查POP3端口
</div>
<h4>Q: 数据库连接失败?</h4>
<p>A: 确保Docker容器正在运行</p>
<div class="code-block">
docker-compose ps<br>
docker-compose up -d mysql
</div>
<h4>Q: 如何重置数据库?</h4>
<p>A: 执行以下命令:</p>
<div class="code-block">
docker-compose down -v<br>
docker-compose up -d<br>
sleep 15
</div>
</div>
<div class="section">
<h3>默认账号</h3>
<ul>
<li><strong>管理员:</strong>admin@test.com / 123456</li>
<li><strong>普通用户:</strong>user1@test.com / 123456</li>
</ul>
</div>
</div>
</body>
</html>

@ -119,13 +119,20 @@ requireAuth();
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<?php if ($_SESSION['is_admin']) { ?>
<a href="users.php">用户管理</a>
<?php } ?>
<a href="emails.php">邮件管理</a>
<?php if ($_SESSION['is_admin']) { ?>
<a href="broadcast.php">群发邮件</a>
<?php } ?>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<?php if ($_SESSION['is_admin']) { ?>
<a href="services.php">服务管理</a>
<a href="settings.php">系统设置</a>
<?php } ?>
<a href="help.php">帮助</a>
</div>
<div class="stats">

@ -0,0 +1,256 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/SystemSettingsRepository.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$db = Database::getInstance();
$settingsRepo = new SystemSettingsRepository();
$message = '';
$error = '';
// 处理清除日志
if (isset($_GET['clear_logs'])) {
$logType = $_GET['clear_logs'] ?? '';
if ($logType === 'all') {
$stmt = $db->prepare("DELETE FROM server_logs");
$stmt->execute();
$message = "所有日志已清除";
} elseif ($logType === 'smtp') {
$stmt = $db->prepare("DELETE FROM server_logs WHERE log_type = 'SMTP'");
$stmt->execute();
$message = "SMTP日志已清除";
} elseif ($logType === 'pop3') {
$stmt = $db->prepare("DELETE FROM server_logs WHERE log_type = 'POP3'");
$stmt->execute();
$message = "POP3日志已清除";
}
}
// 获取日志统计
$stmt = $db->query("SELECT COUNT(*) as count FROM server_logs");
$totalLogs = $stmt->fetch()['count'];
$stmt = $db->query("SELECT COUNT(*) as count FROM server_logs WHERE log_type = 'SMTP'");
$smtpLogs = $stmt->fetch()['count'];
$stmt = $db->query("SELECT COUNT(*) as count FROM server_logs WHERE log_type = 'POP3'");
$pop3Logs = $stmt->fetch()['count'];
// 分页参数
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$perPage = 50;
$offset = ($page - 1) * $perPage;
// 过滤参数
$filterType = $_GET['type'] ?? 'all';
// 获取日志列表
if ($filterType === 'smtp') {
$stmt = $db->prepare("
SELECT l.*, u.username
FROM server_logs l
LEFT JOIN users u ON l.user_id = u.id
WHERE l.log_type = 'SMTP'
ORDER BY l.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->execute([$perPage, $offset]);
$countStmt = $db->prepare("SELECT COUNT(*) as count FROM server_logs WHERE log_type = 'SMTP'");
$countStmt->execute();
$totalLogs = $countStmt->fetch()['count'];
} elseif ($filterType === 'pop3') {
$stmt = $db->prepare("
SELECT l.*, u.username
FROM server_logs l
LEFT JOIN users u ON l.user_id = u.id
WHERE l.log_type = 'POP3'
ORDER BY l.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->execute([$perPage, $offset]);
$countStmt = $db->prepare("SELECT COUNT(*) as count FROM server_logs WHERE log_type = 'POP3'");
$countStmt->execute();
$totalLogs = $countStmt->fetch()['count'];
} else {
$stmt = $db->prepare("
SELECT l.*, u.username
FROM server_logs l
LEFT JOIN users u ON l.user_id = u.id
ORDER BY l.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->execute([$perPage, $offset]);
}
$logs = $stmt->fetchAll();
$totalPages = ceil($totalLogs / $perPage);
// 获取日志设置
$logPath = $settingsRepo->get('log_path', '/var/log/mailserver');
$logMaxSize = $settingsRepo->get('log_max_size', 10485760);
?>
<!DOCTYPE html>
<html>
<head>
<title>系统日志 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 20px; }
.stat-box { border: 1px solid #ddd; padding: 15px; text-align: center; border-radius: 5px; }
.stat-box h3 { margin: 0; font-size: 24px; color: #007bff; }
.stat-box p { margin: 5px 0 0 0; color: #666; }
.filters { margin-bottom: 20px; }
.filters a { display: inline-block; padding: 8px 16px; margin-right: 10px; text-decoration: none; border: 1px solid #ddd; border-radius: 4px; }
.filters a.active { background: #007bff; color: white; border-color: #007bff; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background: #f8f9fa; }
.btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-danger { background: #dc3545; color: white; }
.pagination { margin-top: 20px; text-align: center; }
.pagination a { display: inline-block; padding: 8px 12px; margin: 0 4px; text-decoration: none; border: 1px solid #ddd; border-radius: 4px; }
.pagination a:hover { background: #f8f9fa; }
.pagination .current { background: #007bff; color: white; border-color: #007bff; }
.log-info { background: #f8f9fa; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="users.php">用户管理</a>
<?php endif; ?>
<a href="emails.php">邮件管理</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="settings.php">系统设置</a>
<?php endif; ?>
</div>
<div class="container">
<h2>系统日志管理</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- 日志统计 -->
<div class="stats">
<div class="stat-box">
<h3><?php echo $totalLogs; ?></h3>
<p>总日志数</p>
</div>
<div class="stat-box">
<h3><?php echo $smtpLogs; ?></h3>
<p>SMTP日志</p>
</div>
<div class="stat-box">
<h3><?php echo $pop3Logs; ?></h3>
<p>POP3日志</p>
</div>
</div>
<!-- 日志信息 -->
<div class="log-info">
<strong>日志存储路径:</strong><?php echo htmlspecialchars($logPath); ?><br>
<strong>日志文件最大大小:</strong><?php echo round($logMaxSize / 1048576, 2); ?> MB
</div>
<!-- 过滤和操作 -->
<div class="filters">
<a href="?type=all" class="<?php echo $filterType === 'all' ? 'active' : ''; ?>">全部</a>
<a href="?type=smtp" class="<?php echo $filterType === 'smtp' ? 'active' : ''; ?>">SMTP日志</a>
<a href="?type=pop3" class="<?php echo $filterType === 'pop3' ? 'active' : ''; ?>">POP3日志</a>
<?php if ($_SESSION['is_admin'] ?? false): ?>
<a href="?clear_logs=smtp" class="btn btn-danger" onclick="return confirm('确定要清除SMTP日志吗');">清除SMTP日志</a>
<a href="?clear_logs=pop3" class="btn btn-danger" onclick="return confirm('确定要清除POP3日志吗');">清除POP3日志</a>
<a href="?clear_logs=all" class="btn btn-danger" onclick="return confirm('确定要清除所有日志吗?');">清除所有日志</a>
<?php endif; ?>
</div>
<!-- 日志列表 -->
<table>
<thead>
<tr>
<th>ID</th>
<th>类型</th>
<th>消息</th>
<th>用户</th>
<th>IP地址</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<?php if (empty($logs)): ?>
<tr>
<td colspan="6" style="text-align: center; padding: 40px;">暂无日志</td>
</tr>
<?php else: ?>
<?php foreach ($logs as $log): ?>
<tr>
<td><?php echo $log['id']; ?></td>
<td><?php echo htmlspecialchars($log['log_type'] ?? '-'); ?></td>
<td><?php echo htmlspecialchars($log['message'] ?? '-'); ?></td>
<td><?php echo htmlspecialchars($log['username'] ?? '-'); ?></td>
<td><?php echo htmlspecialchars($log['client_ip'] ?? '-'); ?></td>
<td><?php echo $log['created_at']; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<!-- 分页 -->
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php if ($page > 1): ?>
<a href="?page=<?php echo $page - 1; ?>&type=<?php echo $filterType; ?>">上一页</a>
<?php endif; ?>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<?php if ($i == $page): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="?page=<?php echo $i; ?>&type=<?php echo $filterType; ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($page < $totalPages): ?>
<a href="?page=<?php echo $page + 1; ?>&type=<?php echo $filterType; ?>">下一页</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</body>
</html>

@ -0,0 +1,166 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/ServiceRepository.php';
require_once __DIR__ . '/../src/storage/SystemSettingsRepository.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// 检查管理员权限
if (!$_SESSION['is_admin']) {
die('权限不足:只有管理员可以访问此页面');
}
$serviceRepo = new ServiceRepository();
$settingsRepo = new SystemSettingsRepository();
$message = '';
$error = '';
// 处理服务起停
if (isset($_GET['action'])) {
$serviceName = $_GET['service'] ?? '';
$action = $_GET['action'] ?? '';
if ($serviceName === 'smtp' || $serviceName === 'pop3') {
if ($action === 'start') {
// 启动服务(实际应该通过系统服务管理,这里只是更新状态)
$pid = null; // 实际应该获取进程ID
$serviceRepo->updateStatus($serviceName, true, $pid);
$message = strtoupper($serviceName) . "服务已启动";
} elseif ($action === 'stop') {
// 停止服务
$status = $serviceRepo->getStatus($serviceName);
if ($status && $status['pid']) {
// 尝试终止进程
@exec("kill {$status['pid']} 2>/dev/null");
}
$serviceRepo->updateStatus($serviceName, false, null);
$message = strtoupper($serviceName) . "服务已停止";
}
}
}
// 获取服务状态
$smtpStatus = $serviceRepo->getStatus('smtp');
$pop3Status = $serviceRepo->getStatus('pop3');
$smtpRunning = $serviceRepo->isRunning('smtp');
$pop3Running = $serviceRepo->isRunning('pop3');
// 获取端口设置
$smtpPort = $settingsRepo->get('smtp_port', 25);
$pop3Port = $settingsRepo->get('pop3_port', 110);
?>
<!DOCTYPE html>
<html>
<head>
<title>服务管理 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.service-box { border: 1px solid #ddd; padding: 20px; margin-bottom: 20px; border-radius: 5px; }
.service-box h3 { margin-top: 0; }
.status { display: inline-block; padding: 6px 12px; border-radius: 4px; font-weight: 500; margin-right: 10px; }
.status-running { background: #28a745; color: white; }
.status-stopped { background: #dc3545; color: white; }
.btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-success { background: #28a745; color: white; }
.btn-danger { background: #dc3545; color: white; }
.info { color: #666; font-size: 14px; margin-top: 10px; }
.note { background: #fff3cd; border: 1px solid #ffc107; padding: 15px; border-radius: 5px; margin-top: 20px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<a href="emails.php">邮件管理</a>
<a href="broadcast.php">群发邮件</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<a href="services.php">服务管理</a>
<a href="settings.php">系统设置</a>
</div>
<div class="container">
<h2>服务管理</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- SMTP服务 -->
<div class="service-box">
<h3>SMTP服务邮件发送</h3>
<p>
<span class="status status-<?php echo $smtpRunning ? 'running' : 'stopped'; ?>">
<?php echo $smtpRunning ? '运行中' : '已停止'; ?>
</span>
<?php if ($smtpRunning): ?>
<a href="?service=smtp&action=stop" class="btn btn-danger" onclick="return confirm('确定要停止SMTP服务吗');">停止服务</a>
<?php else: ?>
<a href="?service=smtp&action=start" class="btn btn-success" onclick="return confirm('确定要启动SMTP服务吗');">启动服务</a>
<?php endif; ?>
</p>
<div class="info">
<strong>端口:</strong><?php echo $smtpPort; ?><br>
<strong>进程ID</strong><?php echo $smtpStatus['pid'] ?? '-'; ?><br>
<strong>最后启动:</strong><?php echo $smtpStatus['last_started_at'] ?? '-'; ?><br>
<strong>最后停止:</strong><?php echo $smtpStatus['last_stopped_at'] ?? '-'; ?>
</div>
</div>
<!-- POP3服务 -->
<div class="service-box">
<h3>POP3服务邮件接收</h3>
<p>
<span class="status status-<?php echo $pop3Running ? 'running' : 'stopped'; ?>">
<?php echo $pop3Running ? '运行中' : '已停止'; ?>
</span>
<?php if ($pop3Running): ?>
<a href="?service=pop3&action=stop" class="btn btn-danger" onclick="return confirm('确定要停止POP3服务吗');">停止服务</a>
<?php else: ?>
<a href="?service=pop3&action=start" class="btn btn-success" onclick="return confirm('确定要启动POP3服务吗');">启动服务</a>
<?php endif; ?>
</p>
<div class="info">
<strong>端口:</strong><?php echo $pop3Port; ?><br>
<strong>进程ID</strong><?php echo $pop3Status['pid'] ?? '-'; ?><br>
<strong>最后启动:</strong><?php echo $pop3Status['last_started_at'] ?? '-'; ?><br>
<strong>最后停止:</strong><?php echo $pop3Status['last_stopped_at'] ?? '-'; ?>
</div>
</div>
<div class="note">
<strong>注意:</strong>此页面仅用于管理服务状态。实际启动服务需要使用命令行:
<ul>
<li>SMTP服务<code>sudo php scripts/start_smtp.php</code></li>
<li>POP3服务<code>sudo php scripts/start_pop3.php</code></li>
</ul>
</div>
</div>
</body>
</html>

@ -0,0 +1,320 @@
<?php
/**
* 系统设置页面
*
* @uses SystemSettingsRepository
* @uses UserRepository
* @uses MailboxRepository
*/
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/SystemSettingsRepository.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/storage/MailboxRepository.php';
require_once __DIR__ . '/../src/utils/Validator.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// 检查管理员权限
if (!$_SESSION['is_admin']) {
die('权限不足:只有管理员可以访问此页面');
}
/** @var SystemSettingsRepository */
$settingsRepo = new SystemSettingsRepository();
/** @var UserRepository */
$userRepo = new UserRepository();
/** @var MailboxRepository */
$mailboxRepo = new MailboxRepository();
$message = '';
$error = '';
// 处理系统设置更新
if (isset($_POST['update_settings'])) {
// SMTP端口
if (isset($_POST['smtp_port'])) {
$port = (int)$_POST['smtp_port'];
if (Validator::validatePort($port)) {
$settingsRepo->set('smtp_port', $port);
} else {
$error = "SMTP端口无效1-65535";
}
}
// POP3端口
if (isset($_POST['pop3_port'])) {
$port = (int)$_POST['pop3_port'];
if (Validator::validatePort($port)) {
$settingsRepo->set('pop3_port', $port);
} else {
$error = "POP3端口无效1-65535";
}
}
// 域名
if (isset($_POST['domain'])) {
$domain = trim($_POST['domain']);
if (!empty($domain)) {
$settingsRepo->set('domain', $domain);
}
}
// 默认邮箱大小限制
if (isset($_POST['mailbox_size_limit'])) {
$size = (int)$_POST['mailbox_size_limit'];
if ($size > 0) {
$settingsRepo->set('mailbox_size_limit', $size);
}
}
// 日志路径
if (isset($_POST['log_path'])) {
$settingsRepo->set('log_path', trim($_POST['log_path']));
}
// 日志最大大小
if (isset($_POST['log_max_size'])) {
$size = (int)$_POST['log_max_size'];
if ($size > 0) {
$settingsRepo->set('log_max_size', $size);
}
}
if (empty($error)) {
$message = "系统设置已更新";
}
}
// 处理管理员密码修改
if (isset($_POST['change_admin_password'])) {
$oldPassword = $_POST['old_password'] ?? '';
$newPassword = $_POST['new_password'] ?? '';
$confirmPassword = $_POST['confirm_password'] ?? '';
$user = $userRepo->findById($_SESSION['user_id']);
if (!Security::verifyPassword($oldPassword, $user['password_hash'])) {
$error = "原密码错误";
} else {
$passwordValidation = Validator::validatePassword($newPassword, 6);
if (!$passwordValidation['valid']) {
$error = implode('<br>', $passwordValidation['errors']);
} elseif ($newPassword !== $confirmPassword) {
$error = "两次输入的密码不一致";
} else {
if ($userRepo->update($_SESSION['user_id'], ['password' => $newPassword])) {
$message = "管理员密码已更新";
} else {
$error = "密码更新失败";
}
}
}
}
// 处理用户邮箱大小设置
if (isset($_POST['set_mailbox_size'])) {
$userId = (int)$_POST['user_id'];
$sizeBytes = (int)$_POST['mailbox_size'];
if ($sizeBytes > 0) {
if ($mailboxRepo->setSizeLimit($userId, $sizeBytes)) {
$message = "邮箱大小限制已更新";
} else {
$error = "更新失败";
}
} else {
$error = "邮箱大小必须大于0";
}
}
// 获取当前设置
$settings = $settingsRepo->getAll();
$users = $userRepo->getAll();
?>
<!DOCTYPE html>
<html>
<head>
<title>系统设置 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.section { margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #ddd; }
.section:last-child { border-bottom: none; }
.section h3 { margin-top: 0; color: #333; }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; }
.form-group input, .form-group select { width: 100%; max-width: 400px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.form-group small { color: #666; font-size: 12px; }
.btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; }
.btn-primary { background: #007bff; color: white; }
.btn-success { background: #28a745; color: white; }
table { width: 100%; border-collapse: collapse; margin-top: 15px; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
th { background: #f8f9fa; }
.size-input { width: 150px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<a href="emails.php">邮件管理</a>
<a href="broadcast.php">群发邮件</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<a href="settings.php">系统设置</a>
</div>
<div class="container">
<h2>系统设置</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- 服务器端口设置 -->
<div class="section">
<h3>服务器端口设置</h3>
<form method="POST">
<div class="form-group">
<label>SMTP端口默认25</label>
<input type="number" name="smtp_port" value="<?php echo htmlspecialchars($settings['smtp_port'] ?? '25'); ?>" min="1" max="65535" required>
<small>SMTP服务器监听端口</small>
</div>
<div class="form-group">
<label>POP3端口默认110</label>
<input type="number" name="pop3_port" value="<?php echo htmlspecialchars($settings['pop3_port'] ?? '110'); ?>" min="1" max="65535" required>
<small>POP3服务器监听端口</small>
</div>
<button type="submit" name="update_settings" class="btn btn-primary">保存端口设置</button>
</form>
</div>
<!-- 域名设置 -->
<div class="section">
<h3>域名设置</h3>
<form method="POST">
<div class="form-group">
<label>服务器域名默认test.com</label>
<input type="text" name="domain" value="<?php echo htmlspecialchars($settings['domain'] ?? 'test.com'); ?>" required>
<small>邮件服务器域名,用户邮箱必须使用此域名</small>
</div>
<button type="submit" name="update_settings" class="btn btn-primary">保存域名设置</button>
</form>
</div>
<!-- 邮箱管理 -->
<div class="section">
<h3>邮箱大小管理</h3>
<form method="POST">
<div class="form-group">
<label>默认邮箱大小限制(字节)</label>
<input type="number" name="mailbox_size_limit" value="<?php echo htmlspecialchars($settings['mailbox_size_limit'] ?? '104857600'); ?>" min="1" required>
<small>默认值104857600 (100MB)</small>
</div>
<button type="submit" name="update_settings" class="btn btn-primary">保存默认大小</button>
</form>
<h4>用户邮箱大小设置</h4>
<table>
<thead>
<tr>
<th>用户</th>
<th>当前使用</th>
<th>限制大小</th>
<th>使用率</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<?php
$usage = $mailboxRepo->getUsage($user['id']);
$usedMB = round($usage['used'] / 1048576, 2);
$limitMB = round($usage['limit'] / 1048576, 2);
?>
<tr>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td><?php echo $usedMB; ?> MB</td>
<td><?php echo $limitMB; ?> MB</td>
<td><?php echo $usage['percentage']; ?>%</td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="user_id" value="<?php echo $user['id']; ?>">
<input type="number" name="mailbox_size" value="<?php echo $usage['limit']; ?>" class="size-input" min="1" required>
<button type="submit" name="set_mailbox_size" class="btn btn-success">设置</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- 日志设置 -->
<div class="section">
<h3>日志设置</h3>
<form method="POST">
<div class="form-group">
<label>日志文件存储路径</label>
<input type="text" name="log_path" value="<?php echo htmlspecialchars($settings['log_path'] ?? '/var/log/mailserver'); ?>" required>
<small>日志文件存储的目录路径</small>
</div>
<div class="form-group">
<label>日志文件最大大小(字节)</label>
<input type="number" name="log_max_size" value="<?php echo htmlspecialchars($settings['log_max_size'] ?? '10485760'); ?>" min="1" required>
<small>默认值10485760 (10MB)</small>
</div>
<button type="submit" name="update_settings" class="btn btn-primary">保存日志设置</button>
</form>
</div>
<!-- 管理员密码修改 -->
<div class="section">
<h3>修改管理员密码</h3>
<form method="POST">
<div class="form-group">
<label>原密码</label>
<input type="password" name="old_password" required>
</div>
<div class="form-group">
<label>新密码</label>
<input type="password" name="new_password" required minlength="6">
<small>密码长度至少6个字符</small>
</div>
<div class="form-group">
<label>确认新密码</label>
<input type="password" name="confirm_password" required minlength="6">
</div>
<button type="submit" name="change_admin_password" class="btn btn-primary">修改密码</button>
</form>
</div>
</div>
</body>
</html>

@ -0,0 +1,135 @@
-- ============================================
-- 邮件服务器完整数据库初始化脚本
-- ============================================
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS mail_server
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE mail_server;
-- 创建用户并授权(关键步骤!)
CREATE USER IF NOT EXISTS 'mail_user'@'%' IDENTIFIED BY 'user123';
GRANT ALL PRIVILEGES ON mail_server.* TO 'mail_user'@'%';
FLUSH PRIVILEGES;
-- ============================================
-- 核心功能表
-- ============================================
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
is_admin TINYINT(1) DEFAULT 0,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 邮件表兼容两种存储方式SimpleSmtpServer用sender/recipientSmtpHandler用sender_id/recipient_id
CREATE TABLE IF NOT EXISTS emails (
id INT AUTO_INCREMENT PRIMARY KEY,
sender VARCHAR(100),
recipient VARCHAR(100),
sender_id INT,
recipient_id INT,
subject VARCHAR(200),
body TEXT,
headers TEXT,
size_bytes INT DEFAULT 0,
is_deleted TINYINT(1) DEFAULT 0,
is_read TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (recipient_id) REFERENCES users(id) ON DELETE SET NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 系统日志表
CREATE TABLE IF NOT EXISTS server_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
log_type VARCHAR(50),
message TEXT,
client_ip VARCHAR(45),
user_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- ============================================
-- 管理功能表
-- ============================================
-- 系统设置表
CREATE TABLE IF NOT EXISTS system_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(100) UNIQUE NOT NULL,
setting_value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 过滤规则表
CREATE TABLE IF NOT EXISTS filter_rules (
id INT AUTO_INCREMENT PRIMARY KEY,
rule_type ENUM('email', 'ip') NOT NULL,
rule_value VARCHAR(255) NOT NULL,
action ENUM('block', 'allow') DEFAULT 'block',
description TEXT,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_rule (rule_type, rule_value)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 服务状态表
CREATE TABLE IF NOT EXISTS service_status (
id INT AUTO_INCREMENT PRIMARY KEY,
service_name VARCHAR(50) UNIQUE NOT NULL,
is_running TINYINT(1) DEFAULT 0,
pid INT,
last_started_at TIMESTAMP NULL,
last_stopped_at TIMESTAMP NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 用户邮箱大小限制表
CREATE TABLE IF NOT EXISTS user_mailbox_limits (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
size_limit_bytes BIGINT DEFAULT 104857600, -- 100MB
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE KEY unique_user (user_id)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- ============================================
-- 插入默认数据
-- ============================================
-- 插入测试用户密码都是123456
INSERT INTO users (username, password_hash, is_admin, is_active) VALUES
('admin@test.com', '$2y$10$jB21V61k9aLAyp5.5qBpV.L70Aq6.XrtJrvlNI28bOXeJboLBJwoq', 1, 1),
('user1@test.com', '$2y$10$jB21V61k9aLAyp5.5qBpV.L70Aq6.XrtJrvlNI28bOXeJboLBJwoq', 0, 1)
ON DUPLICATE KEY UPDATE username = VALUES(username);
-- 插入测试邮件
INSERT INTO emails (sender, recipient, subject, body) VALUES
('admin@test.com', 'user1@test.com', '欢迎使用邮件系统', '这是第一封测试邮件'),
('user1@test.com', 'admin@test.com', '回复测试', '我收到了,谢谢!')
ON DUPLICATE KEY UPDATE id = id;
-- 插入系统默认设置
INSERT INTO system_settings (setting_key, setting_value) VALUES
('smtp_port', '25'),
('pop3_port', '110'),
('domain', 'test.com'),
('mailbox_size_limit', '104857600'), -- 100MB
('smtp_enabled', '1'),
('pop3_enabled', '1'),
('log_path', '/var/log/mailserver'),
('log_max_size', '10485760') -- 10MB
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value);
-- 插入服务状态初始记录
INSERT INTO service_status (service_name, is_running) VALUES
('smtp', 0),
('pop3', 0)
ON DUPLICATE KEY UPDATE service_name = VALUES(service_name);

@ -1,60 +0,0 @@
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS mail_server
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE mail_server;
-- 创建用户并授权(关键步骤!)
CREATE USER IF NOT EXISTS 'mail_user'@'%' IDENTIFIED BY 'user123';
GRANT ALL PRIVILEGES ON mail_server.* TO 'mail_user'@'%';
FLUSH PRIVILEGES;
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
is_admin TINYINT(1) DEFAULT 0,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 邮件表兼容两种存储方式SimpleSmtpServer用sender/recipientSmtpHandler用sender_id/recipient_id
CREATE TABLE emails (
id INT AUTO_INCREMENT PRIMARY KEY,
sender VARCHAR(100),
recipient VARCHAR(100),
sender_id INT,
recipient_id INT,
subject VARCHAR(200),
body TEXT,
headers TEXT,
size_bytes INT DEFAULT 0,
is_deleted TINYINT(1) DEFAULT 0,
is_read TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (recipient_id) REFERENCES users(id) ON DELETE SET NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 系统日志表
CREATE TABLE server_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
log_type VARCHAR(50),
message TEXT,
client_ip VARCHAR(45),
user_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB;
-- 插入测试用户
-- 密码都是123456
INSERT INTO users (username, password_hash, is_admin, is_active) VALUES
('admin@test.com', '$2y$10$jB21V61k9aLAyp5.5qBpV.L70Aq6.XrtJrvlNI28bOXeJboLBJwoq', 1, 1),
('user1@test.com', '$2y$10$jB21V61k9aLAyp5.5qBpV.L70Aq6.XrtJrvlNI28bOXeJboLBJwoq', 0, 1);
-- 插入测试邮件
INSERT INTO emails (sender, recipient, subject, body) VALUES
('admin@test.com', 'user1@test.com', '欢迎使用邮件系统', '这是第一封测试邮件'),
('user1@test.com', 'admin@test.com', '回复测试', '我收到了,谢谢!');

@ -0,0 +1,150 @@
<?php
require_once __DIR__ . '/../storage/Database.php';
require_once __DIR__ . '/../storage/UserRepository.php';
require_once __DIR__ . '/../storage/EmailRepository.php';
/**
* 群发邮件服务
*/
class BroadcastService {
private $db;
private $userRepo;
private $emailRepo;
public function __construct() {
$this->db = Database::getInstance();
$this->userRepo = new UserRepository();
$this->emailRepo = new EmailRepository();
}
/**
* 群发邮件给所有用户
* @param string $senderEmail 发件人邮箱
* @param string $subject 主题
* @param string $body 内容
* @return array ['success' => int, 'failed' => int, 'errors' => array]
*/
public function broadcastToAll($senderEmail, $subject, $body) {
$users = $this->userRepo->getAll();
$sender = $this->userRepo->findByUsername($senderEmail);
if (!$sender) {
return ['success' => 0, 'failed' => 0, 'errors' => ['发件人不存在']];
}
$success = 0;
$failed = 0;
$errors = [];
foreach ($users as $user) {
// 跳过发件人自己
if ($user['username'] === $senderEmail) {
continue;
}
// 跳过禁用的用户
if (!$user['is_active']) {
continue;
}
try {
$this->sendEmail($sender['id'], $user['id'], $subject, $body);
$success++;
} catch (Exception $e) {
$failed++;
$errors[] = "发送给 {$user['username']} 失败: " . $e->getMessage();
}
}
return [
'success' => $success,
'failed' => $failed,
'errors' => $errors
];
}
/**
* 群发邮件给指定用户列表
* @param string $senderEmail 发件人邮箱
* @param array $recipientEmails 收件人邮箱列表
* @param string $subject 主题
* @param string $body 内容
* @return array ['success' => int, 'failed' => int, 'errors' => array]
*/
public function broadcastToUsers($senderEmail, $recipientEmails, $subject, $body) {
$sender = $this->userRepo->findByUsername($senderEmail);
if (!$sender) {
return ['success' => 0, 'failed' => 0, 'errors' => ['发件人不存在']];
}
$success = 0;
$failed = 0;
$errors = [];
foreach ($recipientEmails as $email) {
$recipient = $this->userRepo->findByUsername(trim($email));
if (!$recipient) {
$failed++;
$errors[] = "用户 {$email} 不存在";
continue;
}
if (!$recipient['is_active']) {
$failed++;
$errors[] = "用户 {$email} 已被禁用";
continue;
}
try {
$this->sendEmail($sender['id'], $recipient['id'], $subject, $body);
$success++;
} catch (Exception $e) {
$failed++;
$errors[] = "发送给 {$email} 失败: " . $e->getMessage();
}
}
return [
'success' => $success,
'failed' => $failed,
'errors' => $errors
];
}
/**
* 发送邮件
* @param int $senderId 发件人ID
* @param int $recipientId 收件人ID
* @param string $subject 主题
* @param string $body 内容
* @return bool 是否成功
*/
private function sendEmail($senderId, $recipientId, $subject, $body) {
$sender = $this->userRepo->findById($senderId);
$recipient = $this->userRepo->findById($recipientId);
if (!$sender || !$recipient) {
throw new Exception("发件人或收件人不存在");
}
$sizeBytes = strlen($subject) + strlen($body);
$stmt = $this->db->prepare("
INSERT INTO emails (sender_id, recipient_id, sender, recipient, subject, body, size_bytes, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
");
return $stmt->execute([
$senderId,
$recipientId,
$sender['username'],
$recipient['username'],
$subject,
$body,
$sizeBytes
]);
}
}

@ -0,0 +1,114 @@
<?php
require_once __DIR__ . '/Database.php';
/**
* 过滤规则数据访问层
*/
class FilterRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 创建过滤规则
* @param string $type 规则类型 ('email' 或 'ip')
* @param string $value 规则值
* @param string $action 动作 ('block' 或 'allow')
* @param string $description 描述
* @return bool 是否成功
*/
public function create($type, $value, $action = 'block', $description = '') {
$stmt = $this->db->prepare("
INSERT INTO filter_rules (rule_type, rule_value, action, description, is_active)
VALUES (?, ?, ?, ?, 1)
");
return $stmt->execute([$type, $value, $action, $description]);
}
/**
* 获取所有过滤规则
* @return array 规则列表
*/
public function getAll() {
$stmt = $this->db->query("
SELECT * FROM filter_rules
ORDER BY rule_type, created_at DESC
");
return $stmt->fetchAll();
}
/**
* 获取激活的过滤规则
* @return array 规则列表
*/
public function getActive() {
$stmt = $this->db->query("
SELECT * FROM filter_rules
WHERE is_active = 1
ORDER BY rule_type, created_at DESC
");
return $stmt->fetchAll();
}
/**
* 检查邮箱是否被过滤
* @param string $email 邮箱地址
* @return bool 是否被阻止
*/
public function isEmailBlocked($email) {
$stmt = $this->db->prepare("
SELECT action FROM filter_rules
WHERE rule_type = 'email'
AND is_active = 1
AND (rule_value = ? OR rule_value LIKE ?)
ORDER BY action DESC
LIMIT 1
");
$stmt->execute([$email, $email]);
$result = $stmt->fetch();
return $result && $result['action'] === 'block';
}
/**
* 检查IP是否被过滤
* @param string $ip IP地址
* @return bool 是否被阻止
*/
public function isIPBlocked($ip) {
$stmt = $this->db->prepare("
SELECT action FROM filter_rules
WHERE rule_type = 'ip'
AND is_active = 1
AND rule_value = ?
ORDER BY action DESC
LIMIT 1
");
$stmt->execute([$ip]);
$result = $stmt->fetch();
return $result && $result['action'] === 'block';
}
/**
* 删除规则
* @param int $id 规则ID
* @return bool 是否成功
*/
public function delete($id) {
$stmt = $this->db->prepare("DELETE FROM filter_rules WHERE id = ?");
return $stmt->execute([$id]);
}
/**
* 更新规则状态
* @param int $id 规则ID
* @param bool $isActive 是否激活
* @return bool 是否成功
*/
public function updateStatus($id, $isActive) {
$stmt = $this->db->prepare("UPDATE filter_rules SET is_active = ? WHERE id = ?");
return $stmt->execute([$isActive ? 1 : 0, $id]);
}
}

@ -0,0 +1,84 @@
<?php
require_once __DIR__ . '/Database.php';
/**
* 邮箱管理数据访问层
*
* @package storage
*/
class MailboxRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 获取用户邮箱大小限制
* @param int $userId 用户ID
* @return int 大小限制(字节)
*/
public function getSizeLimit($userId) {
$stmt = $this->db->prepare("
SELECT size_limit_bytes FROM user_mailbox_limits WHERE user_id = ?
");
$stmt->execute([$userId]);
$result = $stmt->fetch();
if ($result) {
return (int)$result['size_limit_bytes'];
}
// 返回默认值
return 104857600; // 100MB
}
/**
* 设置用户邮箱大小限制
* @param int $userId 用户ID
* @param int $sizeBytes 大小限制(字节)
* @return bool 是否成功
*/
public function setSizeLimit($userId, $sizeBytes) {
$stmt = $this->db->prepare("
INSERT INTO user_mailbox_limits (user_id, size_limit_bytes)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE size_limit_bytes = VALUES(size_limit_bytes)
");
return $stmt->execute([$userId, $sizeBytes]);
}
/**
* 获取用户当前邮箱使用大小
* @param int $userId 用户ID
* @return int 已使用大小(字节)
*/
public function getUsedSize($userId) {
$stmt = $this->db->prepare("
SELECT COALESCE(SUM(size_bytes), 0) as total_size
FROM emails
WHERE recipient_id = ? AND is_deleted = 0
");
$stmt->execute([$userId]);
$result = $stmt->fetch();
return (int)($result['total_size'] ?? 0);
}
/**
* 获取用户邮箱使用情况
* @param int $userId 用户ID
* @return array ['limit' => int, 'used' => int, 'percentage' => float]
*/
public function getUsage($userId) {
$limit = $this->getSizeLimit($userId);
$used = $this->getUsedSize($userId);
$percentage = $limit > 0 ? ($used / $limit) * 100 : 0;
return [
'limit' => $limit,
'used' => $used,
'percentage' => round($percentage, 2)
];
}
}

@ -0,0 +1,79 @@
<?php
require_once __DIR__ . '/Database.php';
/**
* 服务状态数据访问层
*/
class ServiceRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 获取服务状态
* @param string $serviceName 服务名称 ('smtp' 或 'pop3')
* @return array|null 服务状态或null
*/
public function getStatus($serviceName) {
$stmt = $this->db->prepare("SELECT * FROM service_status WHERE service_name = ?");
$stmt->execute([$serviceName]);
return $stmt->fetch();
}
/**
* 更新服务状态
* @param string $serviceName 服务名称
* @param bool $isRunning 是否运行
* @param int|null $pid 进程ID
* @return bool 是否成功
*/
public function updateStatus($serviceName, $isRunning, $pid = null) {
$stmt = $this->db->prepare("
INSERT INTO service_status (service_name, is_running, pid, last_started_at, last_stopped_at)
VALUES (?, ?, ?, NOW(), NULL)
ON DUPLICATE KEY UPDATE
is_running = VALUES(is_running),
pid = VALUES(pid),
last_started_at = IF(VALUES(is_running) = 1, NOW(), last_started_at),
last_stopped_at = IF(VALUES(is_running) = 0, NOW(), last_stopped_at)
");
return $stmt->execute([$serviceName, $isRunning ? 1 : 0, $pid]);
}
/**
* 获取所有服务状态
* @return array 服务状态列表
*/
public function getAllStatus() {
$stmt = $this->db->query("SELECT * FROM service_status ORDER BY service_name");
return $stmt->fetchAll();
}
/**
* 检查服务是否运行
* @param string $serviceName 服务名称
* @return bool 是否运行
*/
public function isRunning($serviceName) {
$status = $this->getStatus($serviceName);
if (!$status) {
return false;
}
// 检查进程是否真的在运行
if ($status['pid'] && $status['is_running']) {
// 检查进程是否存在
$result = shell_exec("ps -p {$status['pid']} -o pid= 2>/dev/null");
if (empty(trim($result))) {
// 进程不存在,更新状态
$this->updateStatus($serviceName, false, null);
return false;
}
}
return (bool)$status['is_running'];
}
}

@ -0,0 +1,56 @@
<?php
require_once __DIR__ . '/Database.php';
/**
* 系统设置数据访问层
*/
class SystemSettingsRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 获取设置值
* @param string $key 设置键
* @param mixed $default 默认值
* @return mixed 设置值
*/
public function get($key, $default = null) {
$stmt = $this->db->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?");
$stmt->execute([$key]);
$result = $stmt->fetch();
return $result ? $result['setting_value'] : $default;
}
/**
* 设置值
* @param string $key 设置键
* @param mixed $value 设置值
* @return bool 是否成功
*/
public function set($key, $value) {
$stmt = $this->db->prepare("
INSERT INTO system_settings (setting_key, setting_value)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)
");
return $stmt->execute([$key, $value]);
}
/**
* 获取所有设置
* @return array 所有设置
*/
public function getAll() {
$stmt = $this->db->query("SELECT setting_key, setting_value FROM system_settings");
$results = $stmt->fetchAll();
$settings = [];
foreach ($results as $row) {
$settings[$row['setting_key']] = $row['setting_value'];
}
return $settings;
}
}
Loading…
Cancel
Save