VPSHUB/index.php

586 lines
25 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';
require_once __DIR__ . '/tencentcloud.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;
// 获取配置信息以确定平台类型
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if ($config) {
if ($config['site_type'] === 'mofang') {
if ($action === 'reboot') {
$result = mofangHardReboot($configId, $hostId);
} elseif ($action === 'on') {
$result = mofangPowerOn($configId, $hostId);
} elseif ($action === 'off') {
$result = mofangPowerOff($configId, $hostId);
}
} elseif ($config['site_type'] === 'tencent') {
// 腾讯云智能操作自动识别CVM或轻量服务器
if ($action === 'reboot') {
$result = tencentSmartReboot($configId, $hostId);
} elseif ($action === 'on') {
$result = tencentSmartPowerOn($configId, $hostId);
} elseif ($action === 'off') {
$result = tencentSmartPowerOff($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['error'] ?? $result['msg'] ?? '未知错误';
$message = '<div class="error-message"><h3>❌ 操作失败</h3><p>' . htmlspecialchars($errorMsg) . '</p></div>';
}
} elseif ($action === 'refresh_vps_list') {
// 手动刷新VPS列表
$count = 0;
// 刷新魔方平台的VPS列表
$mofangCount = refreshAllVpsLists();
$count += $mofangCount;
// 刷新腾讯云的CVM和轻量应用服务器列表
$tencentCount = refreshAllTencentVpsLists();
$count += $tencentCount;
$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') {
// 获取该配置下的所有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'])) {
preg_match('/(\d+)/', $option['value'], $matches);
if (!empty($matches)) {
$cpuCores = intval($matches[1]);
}
}
if ($option['key'] === 'memory' && isset($option['value'])) {
$memorySize = $option['value'];
}
}
}
// 更新数据库
$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]
);
}
}
} elseif ($config['site_type'] === 'tencent') {
// 腾讯云实例已在refreshAllTencentVpsLists中更新了详细信息这里不需要额外处理
Logger::info("[refresh_vps_list] 腾讯云配置 {$config['id']} 已刷新", 'index.php');
}
}
$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添加api_label
foreach ($vpsList as &$vps) {
$configId = $vps['config_id'];
$vps['api_label'] = $configMap[$configId]['api_label'] ?? '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>
<?php
// 根据平台类型显示不同的重启按钮
$configId = $vps['config_id'];
$siteType = isset($configMap[$configId]) ? $configMap[$configId]['site_type'] : '';
if ($siteType === 'tencent') {
// 腾讯云:普通重启
?>
<button type="submit" name="action" value="reboot" class="btn btn-warning" onclick="return confirm('确认重启?')">
🔄 重启
</button>
<?php } else {
// 魔方等其他平台:硬重启
?>
<button type="submit" name="action" value="reboot" class="btn btn-warning" onclick="return confirm('确认硬重启?')">
🔄 硬重启
</button>
<?php } ?>
</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>