VPSHUB/index.php
2026-05-30 15:06:24 +08:00

545 lines
22 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once __DIR__ . '/app/db_helper.php';
require_once __DIR__ . '/app/logger.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到api_label的映射
$configMap = [];
foreach ($configs as $config) {
$configMap[$config['id']] = $config['api_label'];
}
// 获取所有VPS列表从vpslist.db查询
$listDb = getVpsListDB();
$vpsList = $listDb->query('SELECT * FROM vps_list ORDER BY config_id, vps_id');
// 为每个VPS添加api_label
foreach ($vpsList as &$vps) {
$configId = $vps['config_id'];
$vps['api_label'] = $configMap[$configId] ?? 'Unknown';
}
unset($vps);
Logger::info("Total VPS count: " . count($vpsList), 'index.php');
if (!empty($vpsList)) {
Logger::debug("First VPS: " . json_encode($vpsList[0]), 'index.php');
}
// 按api_label分组
$vpsByApiLabel = [];
foreach ($vpsList as $vps) {
$apiLabel = $vps['api_label'];
if (!isset($vpsByApiLabel[$apiLabel])) {
$vpsByApiLabel[$apiLabel] = [];
}
$vpsByApiLabel[$apiLabel][] = $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-process {
background-color: #ffc107;
box-shadow: 0 0 8px #ffc107;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.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: ?>
<!-- 按api_label分组显示VPS -->
<?php foreach ($vpsByApiLabel as $apiLabel => $labelVps): ?>
<div class="config-section">
<div class="config-title">
<?php echo htmlspecialchars($apiLabel); ?>
- <?php echo count($labelVps); ?> 台VPS
</div>
<?php if (empty($labelVps)): ?>
<div class="empty-state" style="background: white; padding: 40px;">
<p>此配置下暂无VPS请点击“手动刷新VPS列表”获取</p>
</div>
<?php else: ?>
<div class="vps-grid">
<?php foreach ($labelVps as $vps):
$statusClass = 'power-unknown';
$statusText = '未知';
$statusColor = '#999';
if ($vps['status'] === 'on' || $vps['status'] === 'running') {
$statusClass = 'power-on';
$statusText = '运行中';
$statusColor = '#28a745';
} elseif ($vps['status'] === 'off' || $vps['status'] === 'stopped') {
$statusClass = 'power-off';
$statusText = '已关机';
$statusColor = '#dc3545';
} elseif ($vps['status'] === 'process' || $vps['status'] === 'pending' || $vps['status'] === 'rebooting') {
$statusClass = 'power-process';
$statusText = '处理中';
$statusColor = '#ffc107';
}
?>
<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; ?>
<?php if ($vps['amount']): ?>
<div class="info-row">
<span class="info-label">价格</span>
<span class="info-value"><?php echo htmlspecialchars($vps['amount']); ?></span>
</div>
<?php endif; ?>
<?php if ($vps['nextduedate']): ?>
<div class="info-row">
<span class="info-label">到期时间</span>
<span class="info-value"><?php echo date('Y-m-d', $vps['nextduedate']); ?></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>