|
|
<?php
|
|
|
// === HTTP 头部设置 ===
|
|
|
header("Access-Control-Allow-Origin: *");
|
|
|
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
|
|
header("Access-Control-Allow-Headers: Content-Type, Authorization");
|
|
|
header('Content-Type: application/json');
|
|
|
// 处理浏览器的预检请求 (OPTIONS)
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
|
exit(0);
|
|
|
}
|
|
|
|
|
|
// 引入依赖文件
|
|
|
require_once __DIR__ . '/model/DB.php';
|
|
|
require_once __DIR__ . '/service/AuthService.php';
|
|
|
require_once __DIR__ . '/service/SocketClinet.php';
|
|
|
|
|
|
// 获取请求参数
|
|
|
// action: 决定调用哪个功能 (如 login, send_mail)
|
|
|
$action = $_GET['action'] ?? '';
|
|
|
// input: 获取 POST 请求的 JSON Body
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
|
|
|
|
|
// 获取当前操作的用户名(由前端传过来,用于标识身份)
|
|
|
$currentUser = $input['username'] ?? ($_GET['username'] ?? '');
|
|
|
|
|
|
try {
|
|
|
// === 路由分发 (Routing) ===
|
|
|
switch ($action) {
|
|
|
|
|
|
/* ================== 基础认证模块 ================== */
|
|
|
|
|
|
case 'login':
|
|
|
$username = $input['username'] ?? '';
|
|
|
$password = $input['password'] ?? '';
|
|
|
|
|
|
if (!$username || !$password) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '请输入账号密码']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 调用 AuthService 进行校验
|
|
|
$data = AuthService::login($username, $password);
|
|
|
if (!$data) {
|
|
|
echo json_encode(['code' => 401, 'msg' => '账号或密码错误']);
|
|
|
} else {
|
|
|
// 登录成功
|
|
|
echo json_encode(['code' => 200, 'data' => $data]);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 'register':
|
|
|
// 简单的注册逻辑:查重 -> 插入
|
|
|
$username = $input['username'] ?? '';
|
|
|
$password = $input['password'] ?? '';
|
|
|
|
|
|
if (!$username || !$password) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '参数不完整']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$pdo = DB::get();
|
|
|
// 检查用户名是否重复
|
|
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
|
|
|
$stmt->execute([$username]);
|
|
|
|
|
|
if ($stmt->fetch()) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '用户已存在']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 插入新用户,密码使用 MD5 加密
|
|
|
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
|
|
$stmt->execute([$username, md5($password)]);
|
|
|
|
|
|
echo json_encode(['code' => 200, 'msg' => '注册成功']);
|
|
|
break;
|
|
|
|
|
|
/* ================== 邮件业务模块 ================== */
|
|
|
|
|
|
case 'list_mails':
|
|
|
// 它实际上是在调用 SocketClient 去爬取 POP3 服务器的数据返回给前端。
|
|
|
if (!$currentUser) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '缺少 username']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$pwdForPop3 = $input['password'] ?? '';
|
|
|
if (!$pwdForPop3) {
|
|
|
echo json_encode(['code'=>401,'msg'=>'缺少密码']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
// 调用 Socket 客户端去收信
|
|
|
$rows = SocketClient::fetchByPOP3($currentUser, $pwdForPop3);
|
|
|
|
|
|
// 数据清洗:为列表页生成简略预览 (Preview)
|
|
|
foreach ($rows as &$r) {
|
|
|
$body = $r['content'];
|
|
|
// 如果有双换行,通常双换行后面是正文
|
|
|
if(strpos($body, "\r\n\r\n") !== false) {
|
|
|
$body = explode("\r\n\r\n", $body, 2)[1];
|
|
|
}
|
|
|
// 截取前50个字符作为摘要
|
|
|
$r['preview'] = mb_substr(strip_tags($body), 0, 50) . '...';
|
|
|
}
|
|
|
|
|
|
echo json_encode(['code' => 200, 'data' => $rows]);
|
|
|
} catch (Exception $e) {
|
|
|
echo json_encode(['code' => 500, 'msg' => 'POP3错误: ' . $e->getMessage()]);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 'send_mail':
|
|
|
if (!$currentUser) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '未登录']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$to = $input['to'] ?? '';
|
|
|
$subject = $input['subject'] ?? '';
|
|
|
$body = $input['content'] ?? '';
|
|
|
|
|
|
try {
|
|
|
// 调用 Socket 客户端投递邮件
|
|
|
SocketClient::sendBySMTP($currentUser, $to, $subject, $body);
|
|
|
echo json_encode(['code' => 200, 'msg' => 'SMTP投递成功']);
|
|
|
} catch (Exception $e) {
|
|
|
echo json_encode(['code' => 500, 'msg' => 'SMTP错误: ' . $e->getMessage()]);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 'delete_mail':
|
|
|
// 调用 POP3 DELE 命令
|
|
|
$pwdForPop3 = $input['password'] ?? '';
|
|
|
$id = $input['id'] ?? 0;
|
|
|
|
|
|
try {
|
|
|
SocketClient::deleteByPOP3($currentUser, $pwdForPop3, $id);
|
|
|
echo json_encode(['code' => 200, 'msg' => 'POP3删除成功']);
|
|
|
} catch (Exception $e) {
|
|
|
echo json_encode(['code' => 500, 'msg' => $e->getMessage()]);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 'mark_read':
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("UPDATE mails SET is_read = 1 WHERE id = ?");
|
|
|
$stmt->execute([$input['id'] ?? 0]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '已标记已读']);
|
|
|
break;
|
|
|
|
|
|
case 'toggle_star':
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("UPDATE mails SET is_starred = NOT is_starred WHERE id = ?");
|
|
|
$stmt->execute([$input['id'] ?? 0]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '星标已更新']);
|
|
|
break;
|
|
|
|
|
|
/* ================== 管理员模块 ================== */
|
|
|
|
|
|
case 'list_users':
|
|
|
// 获取用户列表,用于管理面板
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->query("SELECT id, username, is_admin, is_disabled, used_size, quota_size FROM users");
|
|
|
echo json_encode(['code' => 200, 'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)]);
|
|
|
break;
|
|
|
|
|
|
case 'add_user':
|
|
|
// 管理员添加新用户
|
|
|
$pdo = DB::get();
|
|
|
// 查重
|
|
|
$check = $pdo->prepare("SELECT id FROM users WHERE username=?");
|
|
|
$check->execute([$input['target_username']]);
|
|
|
if($check->fetch()) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '用户已存在']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$stmt = $pdo->prepare("INSERT INTO users (username, password, is_admin) VALUES (?, ?, ?)");
|
|
|
$stmt->execute([
|
|
|
$input['target_username'],
|
|
|
md5($input['target_password']),
|
|
|
$input['is_admin'] ?? 0
|
|
|
]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '用户已添加']);
|
|
|
break;
|
|
|
|
|
|
case 'del_user':
|
|
|
// 管理员删除用户
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
|
|
|
$stmt->execute([$input['uid'] ?? 0]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '用户已删除']);
|
|
|
break;
|
|
|
|
|
|
case 'broadcast':
|
|
|
// 逻辑:遍历所有用户,通过 SocketClient 循环发送 SMTP 邮件
|
|
|
$pdo = DB::get();
|
|
|
$users = $pdo
|
|
|
->query("SELECT username FROM users")
|
|
|
->fetchAll(PDO::FETCH_COLUMN);
|
|
|
|
|
|
$subject = $input['subject'] ?? '系统通知';
|
|
|
$body = $input['content'] ?? 'System Broadcast';
|
|
|
$from = 'admin@system';
|
|
|
|
|
|
foreach ($users as $u) {
|
|
|
try {
|
|
|
if ($u === $from) continue; // 不发给自己
|
|
|
|
|
|
// 复用 sendBySMTP 方法
|
|
|
SocketClient::sendBySMTP(
|
|
|
$from,
|
|
|
$u,
|
|
|
$subject,
|
|
|
$body
|
|
|
);
|
|
|
} catch (Exception $e) {
|
|
|
error_log("Broadcast to {$u} failed: " . $e->getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
echo json_encode([
|
|
|
'code' => 200,
|
|
|
'msg' => '广播完成'
|
|
|
]);
|
|
|
break;
|
|
|
|
|
|
|
|
|
case 'get_logs':
|
|
|
// 获取服务器日志
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->query("SELECT * FROM server_logs ORDER BY id DESC LIMIT 50");
|
|
|
echo json_encode(['code' => 200, 'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)]);
|
|
|
break;
|
|
|
|
|
|
case 'change_password':
|
|
|
// 修改密码
|
|
|
if (!$currentUser) { echo json_encode(['code'=>401,'msg'=>'未登录']); break; }
|
|
|
|
|
|
$newPass = $input['new_pass'] ?? '';
|
|
|
if (!$newPass) { echo json_encode(['code'=>400,'msg'=>'密码不能为空']); break; }
|
|
|
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE username = ?");
|
|
|
$stmt->execute([md5($newPass), $currentUser]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '密码已修改']);
|
|
|
break;
|
|
|
|
|
|
case 'toggle_status':
|
|
|
// 禁用/解封用户
|
|
|
$uid = $input['uid'] ?? 0;
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("UPDATE users SET is_disabled = NOT is_disabled WHERE id = ?");
|
|
|
$stmt->execute([$uid]);
|
|
|
echo json_encode(['code' => 200, 'msg' => '状态已更新']);
|
|
|
break;
|
|
|
|
|
|
case 'list_ip_filters':
|
|
|
// 列出封禁的ip
|
|
|
$pdo = DB::get();
|
|
|
|
|
|
$stmt = $pdo->query("
|
|
|
SELECT
|
|
|
id,
|
|
|
ip_address,
|
|
|
action,
|
|
|
created_at
|
|
|
FROM ip_filters
|
|
|
ORDER BY id DESC
|
|
|
");
|
|
|
|
|
|
echo json_encode([
|
|
|
'code' => 200,
|
|
|
'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)
|
|
|
]);
|
|
|
break;
|
|
|
|
|
|
case 'add_ip_filter':
|
|
|
// 添加封禁ip
|
|
|
$ip = trim($input['ip_address'] ?? '');
|
|
|
|
|
|
if (!$ip) {
|
|
|
echo json_encode(['code' => 400, 'msg' => 'IP 不能为空']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 简单 IP 格式校验(IPv4 / IPv6 都支持)
|
|
|
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
|
|
|
echo json_encode(['code' => 400, 'msg' => 'IP 格式不合法']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$pdo = DB::get();
|
|
|
|
|
|
// 防止重复封禁
|
|
|
$check = $pdo->prepare("
|
|
|
SELECT id
|
|
|
FROM ip_filters
|
|
|
WHERE ip_address = ? AND action = 'BLOCK'
|
|
|
");
|
|
|
$check->execute([$ip]);
|
|
|
|
|
|
if ($check->fetch()) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '该 IP 已被封禁']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$stmt = $pdo->prepare("
|
|
|
INSERT INTO ip_filters (ip_address, action)
|
|
|
VALUES (?, 'BLOCK')
|
|
|
");
|
|
|
$stmt->execute([$ip]);
|
|
|
|
|
|
echo json_encode([
|
|
|
'code' => 200,
|
|
|
'msg' => 'IP 已成功封禁'
|
|
|
]);
|
|
|
break;
|
|
|
|
|
|
|
|
|
case 'del_ip_filter':
|
|
|
// 删除被封禁的ip
|
|
|
$id = intval($input['id'] ?? 0);
|
|
|
|
|
|
if (!$id) {
|
|
|
echo json_encode(['code' => 400, 'msg' => '参数错误']);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
$pdo = DB::get();
|
|
|
$stmt = $pdo->prepare("DELETE FROM ip_filters WHERE id = ?");
|
|
|
$stmt->execute([$id]);
|
|
|
|
|
|
echo json_encode([
|
|
|
'code' => 200,
|
|
|
'msg' => 'IP 已解封'
|
|
|
]);
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
echo json_encode(['code' => 404, 'msg' => '接口不存在']);
|
|
|
}
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
// 全局异常捕获:确保无论发生什么错误,都返回 JSON 格式,防止前端解析崩溃
|
|
|
echo json_encode(['code' => 500, 'msg' => $e->getMessage()]);
|
|
|
} |