VPSHUB/index.php

707 lines
32 KiB
PHP
Raw Permalink 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';
require_once __DIR__ . '/aliyun.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 = $_POST['host_id'] ?? '';
$configId = intval($_POST['config_id'] ?? 0);
if ($action && !empty($hostId) && $configId > 0) {
$result = null;
Logger::info("[操作请求] action={$action}, host_id={$hostId}, config_id={$configId}", 'index.php');
// 获取配置信息以确定平台类型
$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);
}
} elseif ($config['site_type'] === 'aliyun') {
// 阿里云ECS操作
if ($action === 'reboot') {
$result = aliyunReboot($configId, $hostId);
} elseif ($action === 'on') {
$result = aliyunPowerOn($configId, $hostId);
} elseif ($action === 'off') {
$result = aliyunPowerOff($configId, $hostId);
}
} elseif ($config['site_type'] === 'aliyun_swas') {
// 阿里云轻量应用服务器操作
if ($action === 'reboot') {
$result = aliyunSwasReboot($configId, $hostId);
} elseif ($action === 'on') {
$result = aliyunSwasPowerOn($configId, $hostId);
} elseif ($action === 'off') {
$result = aliyunSwasPowerOff($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_single_config') {
// 单独刷新某个配置
$configId = intval($_POST['config_id'] ?? 0);
if ($configId > 0) {
$configDb = getVpsDB();
$config = $configDb->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if ($config) {
$success = false;
$typeName = '';
if ($config['site_type'] === 'mofang') {
require_once __DIR__ . '/mofangidc.php';
$success = refreshVpsListForConfig($configId);
$typeName = '魔方平台';
} elseif ($config['site_type'] === 'tencent') {
require_once __DIR__ . '/tencentcloud.php';
// 同时刷新CVM和轻量
$cvmSuccess = refreshTencentCvmListForConfig($configId);
$lhSuccess = refreshTencentLighthouseListForConfig($configId);
$success = $cvmSuccess || $lhSuccess;
$typeName = '腾讯云';
} elseif ($config['site_type'] === 'aliyun') {
require_once __DIR__ . '/aliyun.php';
$success = refreshAliyunEcsListForConfig($configId);
$typeName = '阿里云ECS';
} elseif ($config['site_type'] === 'aliyun_swas') {
require_once __DIR__ . '/aliyun.php';
$success = refreshAliyunSwasListForConfig($configId);
$typeName = '阿里云轻量';
}
if ($success) {
$message = '<div class="success-message"><h3>✅ 刷新成功</h3><p>已刷新配置「' . htmlspecialchars($config['api_label']) . '」(' . $typeName . ')的VPS列表</p></div>';
} else {
$message = '<div class="error-message"><h3>❌ 刷新失败</h3><p>配置「' . htmlspecialchars($config['api_label']) . '」刷新失败请检查API凭证和网络连接</p></div>';
}
} else {
$message = '<div class="error-message">❌ 配置不存在</div>';
}
} else {
$message = '<div class="error-message">❌ 无效的配置ID</div>';
}
} elseif ($action === 'refresh_vps_list') {
// 手动刷新VPS列表全部
$count = 0;
// 刷新魔方平台的VPS列表
$mofangCount = refreshAllVpsLists();
$count += $mofangCount;
// 刷新腾讯云的CVM和轻量应用服务器列表
$tencentCount = refreshAllTencentVpsLists();
$count += $tencentCount;
// 刷新阿里云的ECS列表
$aliyunCount = refreshAllAliyunVpsLists();
$count += $aliyunCount;
$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');
} elseif ($config['site_type'] === 'aliyun') {
// 阿里云ECS实例已在refreshAllAliyunVpsLists中更新了详细信息这里不需要额外处理
Logger::info("[refresh_vps_list] 阿里云配置 {$config['id']} 已刷新", 'index.php');
} elseif ($config['site_type'] === 'aliyun_swas') {
// 阿里云轻量应用服务器实例已在refreshAllAliyunVpsLists中更新了详细信息这里不需要额外处理
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;
}
// 构建api_label到config_id的映射用于单独刷新
$apiLabelToConfigId = [];
foreach ($configs as $config) {
$apiLabelToConfigId[$config['api_label']] = $config['id'];
}
// 统计信息
$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;
}
.config-title {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 消息自动消失动画 */
.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;
cursor: help;
}
.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;" onsubmit="return confirmRefreshAll()">
<button type="submit" name="action" value="refresh_vps_list" class="btn-refresh">
🔄 刷新全部列表
</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">
<span>
<?php echo htmlspecialchars($apiLabel); ?>
- <?php echo count($labelVps); ?> 台VPS
</span>
<form method="POST" style="display: inline; margin-left: 15px;" onsubmit="return confirm('确认刷新此配置的VPS列表')">
<input type="hidden" name="config_id" value="<?php echo $apiLabelToConfigId[$apiLabel] ?? 0; ?>">
<button type="submit" name="action" value="refresh_single_config" class="btn-refresh" style="padding: 5px 12px; font-size: 13px;">
🔄 刷新此配置
</button>
</form>
</div>
<?php if (empty($labelVps)): ?>
<div class="empty-state" style="background: white; padding: 40px;">
<p>此配置下暂无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';
}
// 处理长ID显示
$vpsId = $vps['vps_id'];
$displayId = $vpsId;
$showTooltip = false;
if (strlen($vpsId) > 12) {
$displayId = substr($vpsId, 0, 10) . '...';
$showTooltip = true;
}
?>
<div class="vps-card">
<div class="card-header">
<div class="vps-id" <?php if ($showTooltip): ?>title="<?php echo htmlspecialchars($vpsId); ?>"<?php endif; ?>>#<?php echo htmlspecialchars($displayId); ?></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 } elseif ($siteType === 'aliyun' || $siteType === 'aliyun_swas') {
// 阿里云ECS或轻量普通重启
?>
<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秒后开始淡出
});
});
// 刷新全部列表确认
function confirmRefreshAll() {
return confirm('⚠️ 警告:资源消耗占用较高\n\n' +
'刷新全部列表会同时请求所有配置的API可能会导致\n' +
'• 页面卡顿或响应缓慢\n' +
'• API超时导致报错中断\n' +
'• 部分配置刷新失败\n\n' +
'建议优先使用分离式刷新功能(点击每个配置右上角的🔄按钮)\n\n' +
'确定要继续刷新全部列表吗?');
}
</script>
</body>
</html>