541 lines
21 KiB
PHP
541 lines
21 KiB
PHP
<?php
|
||
require_once __DIR__ . '/app/db_helper.php';
|
||
require_once __DIR__ . '/mofangidc.php';
|
||
|
||
// 存储API_PASS
|
||
$tokenFile = __DIR__ . '/app/pass.php';
|
||
|
||
// 检查是否已设置API_PASS
|
||
if (!file_exists($tokenFile)) {
|
||
header('Location: config_add.php');
|
||
exit;
|
||
}
|
||
|
||
// 加载API_PASS
|
||
include $tokenFile;
|
||
|
||
// 验证pass参数
|
||
$pass = isset($_GET['pass']) ? $_GET['pass'] : '';
|
||
|
||
if (!defined('API_PASS') || $pass !== API_PASS) {
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode(['status' => 403, 'msg' => '访问密码错误或者未输入密码,请拼接?pass=API_PASS后再尝试访问'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
exit;
|
||
}
|
||
|
||
// 处理操作请求
|
||
$message = '';
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$action = $_POST['action'] ?? '';
|
||
$hostId = intval($_POST['host_id'] ?? 0);
|
||
$configId = intval($_POST['config_id'] ?? 0);
|
||
|
||
if ($action && $hostId > 0 && $configId > 0) {
|
||
$result = null;
|
||
|
||
if ($action === 'reboot') {
|
||
$result = mofangHardReboot($configId, $hostId);
|
||
} elseif ($action === 'on') {
|
||
$result = mofangPowerOn($configId, $hostId);
|
||
} elseif ($action === 'off') {
|
||
$result = mofangPowerOff($configId, $hostId);
|
||
}
|
||
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
$message = '<div class="success-message"><h3>✅ 操作成功</h3><p>VPS #' . $hostId . ' 已成功执行操作</p></div>';
|
||
} else {
|
||
$errorMsg = $result['msg'] ?? '未知错误';
|
||
$message = '<div class="error-message"><h3>❌ 操作失败</h3><p>' . htmlspecialchars($errorMsg) . '</p></div>';
|
||
}
|
||
} elseif ($action === 'refresh_vps_list') {
|
||
// 手动刷新VPS列表
|
||
$count = refreshAllVpsLists();
|
||
$message = '<div class="success-message"><h3>✅ 刷新完成</h3><p>已成功刷新 ' . $count . ' 个配置的VPS列表</p></div>';
|
||
|
||
// 获取每个VPS的详细信息并更新数据库
|
||
$listDb = getVpsListDB();
|
||
$configDb = getVpsDB();
|
||
$configs = $configDb->query('SELECT * FROM configs');
|
||
|
||
foreach ($configs as $config) {
|
||
if ($config['site_type'] !== 'mofang') {
|
||
continue; // 目前只支持魔方平台
|
||
}
|
||
|
||
// 获取该配置下的所有VPS
|
||
$vpsItems = $listDb->query('SELECT vps_id FROM vps_list WHERE config_id = ?', [$config['id']]);
|
||
|
||
foreach ($vpsItems as $vpsItem) {
|
||
$vpsId = $vpsItem['vps_id'];
|
||
|
||
// 获取VPS详细信息
|
||
$details = mofangGetVpsDetails($config['id'], $vpsId);
|
||
|
||
if ($details && isset($details['status']) && $details['status'] === 200 && isset($details['data']['host'])) {
|
||
$host = $details['data']['host'];
|
||
|
||
// 解析CPU和内存信息
|
||
$cpuCores = null;
|
||
$memorySize = null;
|
||
|
||
if (isset($host['config_option']) && is_array($host['config_option'])) {
|
||
foreach ($host['config_option'] as $option) {
|
||
if ($option['key'] === 'cpu' && isset($option['value'])) {
|
||
// 提取数字,例如 "16核" -> 16
|
||
preg_match('/(\d+)/', $option['value'], $matches);
|
||
if (!empty($matches)) {
|
||
$cpuCores = intval($matches[1]);
|
||
}
|
||
}
|
||
if ($option['key'] === 'memory' && isset($option['value'])) {
|
||
$memorySize = $option['value']; // 例如 "16G"
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新数据库
|
||
$listDb->execute(
|
||
'UPDATE vps_list SET cpu_cores = ?, memory_size = ?, last_check = CURRENT_TIMESTAMP WHERE config_id = ? AND vps_id = ?',
|
||
[$cpuCores, $memorySize, $config['id'], $vpsId]
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
$message .= '<div class="success-message">✅ 已更新VPS详细信息(CPU、内存等)</div>';
|
||
}
|
||
}
|
||
|
||
// 获取所有配置
|
||
$db = getVpsDB();
|
||
$configs = $db->query('SELECT * FROM configs ORDER BY id');
|
||
|
||
// 构建配置ID到配置的映射
|
||
$configMap = [];
|
||
foreach ($configs as $config) {
|
||
$configMap[$config['id']] = $config;
|
||
}
|
||
|
||
// 获取所有VPS列表(只从vpslist.db查询)
|
||
$listDb = getVpsListDB();
|
||
$vpsList = $listDb->query('SELECT * FROM vps_list ORDER BY config_id, vps_id');
|
||
|
||
// 为每个VPS添加site_type和site_url信息
|
||
foreach ($vpsList as &$vps) {
|
||
$configId = $vps['config_id'];
|
||
if (isset($configMap[$configId])) {
|
||
$vps['site_type'] = $configMap[$configId]['site_type'];
|
||
// 修正site_url:移除末尾的/v1或/v1/
|
||
$siteUrl = $configMap[$configId]['site_url'];
|
||
$siteUrl = rtrim($siteUrl, '/'); // 移除末尾的 /
|
||
if (substr($siteUrl, -3) === '/v1') {
|
||
$siteUrl = substr($siteUrl, 0, -3); // 移除 /v1
|
||
}
|
||
$vps['site_url'] = $siteUrl;
|
||
} else {
|
||
$vps['site_type'] = 'unknown';
|
||
$vps['site_url'] = '';
|
||
}
|
||
}
|
||
unset($vps); // 解除引用
|
||
|
||
// 调试信息(临时)
|
||
error_log("Configs count: " . count($configs));
|
||
error_log("VPS List count: " . count($vpsList));
|
||
if (!empty($vpsList)) {
|
||
error_log("First VPS: " . json_encode($vpsList[0]));
|
||
}
|
||
|
||
// 按配置分组
|
||
$vpsByConfig = [];
|
||
foreach ($vpsList as $vps) {
|
||
$configId = $vps['config_id'];
|
||
if (!isset($vpsByConfig[$configId])) {
|
||
$vpsByConfig[$configId] = [];
|
||
}
|
||
$vpsByConfig[$configId][] = $vps;
|
||
}
|
||
|
||
// 统计信息
|
||
$totalCount = count($vpsList);
|
||
$runningCount = 0;
|
||
$offCount = 0;
|
||
|
||
foreach ($vpsList as $vps) {
|
||
if ($vps['status'] === 'on') {
|
||
$runningCount++;
|
||
} elseif ($vps['status'] === 'off') {
|
||
$offCount++;
|
||
}
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<link rel="shortcut icon" href="./static/favicon.ico" type="image/x-icon">
|
||
<link href="./static/style.css" rel="stylesheet" type="text/css">
|
||
<title>VPS管理面板 - VPS Hub</title>
|
||
<style>
|
||
.toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding: 15px;
|
||
background: white;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
.btn-refresh {
|
||
background-color: #007bff;
|
||
color: white;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
}
|
||
.btn-refresh:hover {
|
||
background-color: #0056b3;
|
||
}
|
||
/* 消息自动消失动画 */
|
||
.success-message, .error-message {
|
||
transition: opacity 0.5s ease-out;
|
||
}
|
||
.fade-out {
|
||
opacity: 0;
|
||
}
|
||
.config-section {
|
||
margin-bottom: 30px;
|
||
}
|
||
.config-title {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 8px 8px 0 0;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
}
|
||
.vps-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||
gap: 20px;
|
||
padding: 20px;
|
||
background: #f8f9fa;
|
||
border-radius: 0 0 8px 8px;
|
||
}
|
||
.vps-card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
overflow: hidden;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
.vps-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||
}
|
||
.card-header {
|
||
padding: 15px;
|
||
border-bottom: 1px solid #eee;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.vps-id {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
.status-badge {
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
color: white;
|
||
}
|
||
.card-body {
|
||
padding: 15px;
|
||
}
|
||
.info-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
.info-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.info-label {
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
.info-value {
|
||
color: #333;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
.power-status {
|
||
padding: 10px 15px;
|
||
background: #f8f9fa;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.power-indicator {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
}
|
||
.power-on {
|
||
background-color: #28a745;
|
||
box-shadow: 0 0 8px #28a745;
|
||
}
|
||
.power-off {
|
||
background-color: #dc3545;
|
||
}
|
||
.power-unknown {
|
||
background-color: #6c757d;
|
||
}
|
||
.power-text {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
.card-actions {
|
||
padding: 15px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 10px;
|
||
}
|
||
.btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: all 0.2s;
|
||
}
|
||
.btn-success {
|
||
background-color: #28a745;
|
||
color: white;
|
||
}
|
||
.btn-success:hover {
|
||
background-color: #218838;
|
||
}
|
||
.btn-danger {
|
||
background-color: #dc3545;
|
||
color: white;
|
||
}
|
||
.btn-danger:hover {
|
||
background-color: #c82333;
|
||
}
|
||
.btn-warning {
|
||
background-color: #ffc107;
|
||
color: #333;
|
||
grid-column: span 2;
|
||
}
|
||
.btn-warning:hover {
|
||
background-color: #e0a800;
|
||
}
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: #999;
|
||
}
|
||
.empty-state h3 {
|
||
font-size: 24px;
|
||
margin-bottom: 10px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>🖥️ VPS Hub 管理面板</h1>
|
||
<p>实时查看和管理您的云服务器</p>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<?php echo $message; ?>
|
||
|
||
<!-- 工具栏 -->
|
||
<div class="toolbar">
|
||
<form method="POST" style="display: inline;">
|
||
<button type="submit" name="action" value="refresh_vps_list" class="btn-refresh">
|
||
🔄 手动刷新VPS列表
|
||
</button>
|
||
</form>
|
||
|
||
<a href="config_add.php?pass=<?php echo urlencode($pass); ?>" class="btn btn-success">
|
||
➕ 添加配置源
|
||
</a>
|
||
</div>
|
||
|
||
<!-- 统计信息 -->
|
||
<div class="stats-bar">
|
||
<div class="stats-item">
|
||
总计:<strong><?php echo $totalCount; ?></strong> 台服务器
|
||
</div>
|
||
<div class="stats-item">
|
||
运行中:<strong><?php echo $runningCount; ?></strong> 台
|
||
</div>
|
||
<div class="stats-item">
|
||
已关机:<strong><?php echo $offCount; ?></strong> 台
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (empty($configs)): ?>
|
||
<div class="empty-state">
|
||
<h3>📭 暂无配置</h3>
|
||
<p>请先添加VPS配置才能开始管理</p>
|
||
<a href="config_add.php?pass=<?php echo urlencode($pass); ?>" class="btn btn-success" style="margin-top: 20px;">
|
||
➕ 添加第一个配置
|
||
</a>
|
||
</div>
|
||
<?php else: ?>
|
||
<!-- 按配置分组显示VPS -->
|
||
<?php foreach ($configs as $config):
|
||
$configVps = $vpsByConfig[$config['id']] ?? [];
|
||
|
||
$typeMap = [
|
||
'mofang' => '魔方平台',
|
||
'aliyun' => '阿里云',
|
||
'tencent' => '腾讯云'
|
||
];
|
||
$typeName = $typeMap[$config['site_type']] ?? $config['site_type'];
|
||
?>
|
||
<div class="config-section">
|
||
<div class="config-title">
|
||
<?php echo htmlspecialchars($config['api_label']); ?>
|
||
(<?php echo htmlspecialchars($typeName); ?>)
|
||
- <?php echo count($configVps); ?> 台VPS
|
||
</div>
|
||
|
||
<?php if (empty($configVps)): ?>
|
||
<div class="empty-state" style="background: white; padding: 40px;">
|
||
<p>此配置下暂无VPS,请点击"手动刷新VPS列表"获取</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="vps-grid">
|
||
<?php foreach ($configVps as $vps):
|
||
$statusClass = 'power-unknown';
|
||
$statusText = '未知';
|
||
$statusColor = '#999';
|
||
|
||
if ($vps['status'] === 'on') {
|
||
$statusClass = 'power-on';
|
||
$statusText = '运行中';
|
||
$statusColor = '#28a745';
|
||
} elseif ($vps['status'] === 'off') {
|
||
$statusClass = 'power-off';
|
||
$statusText = '已关机';
|
||
$statusColor = '#dc3545';
|
||
}
|
||
?>
|
||
<div class="vps-card">
|
||
<div class="card-header">
|
||
<div class="vps-id">#<?php echo $vps['vps_id']; ?></div>
|
||
<span class="status-badge" style="background-color: <?php echo $statusColor; ?>">
|
||
<?php echo $statusText; ?>
|
||
</span>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
|
||
<?php if ($vps['ip_address']): ?>
|
||
<div class="info-row">
|
||
<span class="info-label">IP地址</span>
|
||
<span class="info-value"><?php echo htmlspecialchars($vps['ip_address']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vps['product_name']): ?>
|
||
<div class="info-row">
|
||
<span class="info-label">产品名称</span>
|
||
<span class="info-value"><?php echo htmlspecialchars($vps['product_name']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vps['cpu_cores'] || $vps['memory_size']): ?>
|
||
<div class="info-row">
|
||
<span class="info-label">CPU/内存</span>
|
||
<span class="info-value">
|
||
<?php
|
||
$parts = [];
|
||
if ($vps['cpu_cores']) {
|
||
$parts[] = $vps['cpu_cores'] . ' 核';
|
||
}
|
||
if ($vps['memory_size']) {
|
||
$parts[] = htmlspecialchars($vps['memory_size']);
|
||
}
|
||
echo implode(' / ', $parts);
|
||
?>
|
||
</span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vps['disk_size'] || $vps['bandwidth'] || $vps['os_type']): ?>
|
||
<div class="info-row">
|
||
<span class="info-label">磁盘/带宽/系统</span>
|
||
<span class="info-value"><?php echo htmlspecialchars($vps['disk_size']) . ' / ' . htmlspecialchars($vps['bandwidth']) . ' / ' . htmlspecialchars($vps['os_type']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="power-status">
|
||
<span class="power-indicator <?php echo $statusClass; ?>"></span>
|
||
<span class="power-text"><?php echo $statusText; ?></span>
|
||
</div>
|
||
|
||
<form method="POST" style="margin: 0;">
|
||
<input type="hidden" name="host_id" value="<?php echo $vps['vps_id']; ?>">
|
||
<input type="hidden" name="config_id" value="<?php echo $vps['config_id']; ?>">
|
||
<div class="card-actions">
|
||
<button type="submit" name="action" value="on" class="btn btn-success" onclick="return confirm('确认开机?')">
|
||
⚡ 开机
|
||
</button>
|
||
<button type="submit" name="action" value="off" class="btn btn-danger" onclick="return confirm('确认关机?')">
|
||
🔴 关机
|
||
</button>
|
||
<button type="submit" name="action" value="reboot" class="btn btn-warning" onclick="return confirm('确认硬重启?')">
|
||
🔄 硬重启
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<center><div class="copyright">
|
||
© 2026 Gaming Master Cybersecurity |
|
||
<a href="https://git.masonliu.com/MasonLiu/VPSHUB" target="_blank">MasonLiu In Gitea</a>
|
||
</div></center>
|
||
</div>
|
||
|
||
<script>
|
||
// 操作成功提示5秒后自动消失
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const messages = document.querySelectorAll('.success-message, .error-message');
|
||
|
||
messages.forEach(function(message) {
|
||
setTimeout(function() {
|
||
message.classList.add('fade-out');
|
||
|
||
// 等待动画完成后移除元素
|
||
setTimeout(function() {
|
||
message.style.display = 'none';
|
||
}, 500);
|
||
}, 5000); // 5秒后开始淡出
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|