VPSHUB/index.php
2026-05-24 03:13:12 +08:00

494 lines
20 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
// ==================== 全局配置区域 ====================
define('CONFIG_FILE', __DIR__ . '/config.php'); // 配置文件路径
define('TOKEN_CACHE_FILE', __DIR__ . '/token_cache.php'); // Token缓存文件路径
define('TOKEN_EXPIRE_TIME', 3600); // Token过期时间默认1小时
// 加载配置文件
if (file_exists(CONFIG_FILE)) {
require_once CONFIG_FILE;
}
// 检查是否需要显示配置页面
$needConfig = !defined('API_PASS') || !defined('ACCOUNT') || !defined('API_KEY') ||
empty(API_PASS) || empty(ACCOUNT) || empty(API_KEY);
// 处理配置提交
if ($needConfig && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['setup_config'])) {
$apiPass = trim($_POST['api_pass']);
$account = trim($_POST['account']);
$apiKey = trim($_POST['api_key']);
if (empty($apiPass) || empty($account) || empty($apiKey)) {
$configError = '所有字段都不能为空!';
} else {
// 生成配置文件内容
$configContent = "<?php\n";
$configContent .= "// 核云IDC VPS管理面板配置文件\n";
$configContent .= "// 生成时间: " . date('Y-m-d H:i:s') . "\n\n";
$configContent .= "define('API_PASS', '" . addslashes($apiPass) . "'); // API访问密码\n";
$configContent .= "define('ACCOUNT', '" . addslashes($account) . "'); // 核云IDC账号手机号或邮箱\n";
$configContent .= "define('API_KEY', '" . addslashes($apiKey) . "'); // 核云IDC API KEY\n";
$configContent .= "define('BASE_URL', 'https://www.heyunidc.cn/v1'); // API基础URL\n";
// 写入配置文件
if (file_put_contents(CONFIG_FILE, $configContent)) {
// 重新加载配置
require_once CONFIG_FILE;
$needConfig = false;
} else {
$configError = '配置文件写入失败,请检查目录权限!';
}
}
}
// 如果仍需配置,显示配置页面
if ($needConfig) {
header('Content-Type: text/html; charset=utf-8');
?>
<!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/initial.css" rel="stylesheet" type="text/css">
<title>初始配置 - VPS管理面板</title>
</head>
<body>
<div class="config-container">
<div class="config-header">
<h1>🔧 初始配置</h1>
<p>请填写以下信息以开始使用</p>
</div>
<?php if (isset($configError)): ?>
<div class="error-message">
❌ <?php echo htmlspecialchars($configError); ?>
</div>
<?php endif; ?>
<div class="info-box">
💡 提示:这些信息将保存在 <code>config.php</code> 文件中,请妥善保管。<br>
若您需要重置或更改配置,请删除 <code>config.php</code> 文件并重新运行。<br>
配置完成后请以该格式访问服务example.com/?pass=API_PASS
</div>
<form method="POST" onsubmit="return validatePassword()">
<div class="form-group">
<label for="api_pass">访问密码 (API_PASS)</label>
<input type="text" id="api_pass" name="api_pass" required placeholder="设置一个安全的访问密码" pattern="[a-zA-Z0-9]+" title="只允许字母和数字">
<div class="help-text">用于保护管理面板的访问权限<br>可选:字母(a-z,A-Z)和数字(0-9)</div>
</div>
<div class="form-group">
<label for="account">核云IDC账号 (ACCOUNT)</label>
<input type="text" id="account" name="account" required placeholder="手机号或邮箱">
<div class="help-text">您的核云IDC登录账号</div>
</div>
<div class="form-group">
<label for="api_key">API密钥 (API_KEY)</label>
<input type="password" id="api_key" name="api_key" required placeholder="输入API KEY">
<div class="help-text">在核云IDC控制台获取的API密钥</div>
</div>
<button type="submit" name="setup_config" class="btn-submit">
✅ 保存配置并开始使用
</button>
</form>
</div>
<script>
function validatePassword() {
var password = document.getElementById('api_pass').value;
var pattern = /^[a-zA-Z0-9]+$/;
if (!pattern.test(password)) {
alert('格式不正确!\n\n密码仅允许包含\n• 字母 (a-z, A-Z)\n• 数字 (0-9)\n\n请勿使用特殊符号或空格');
return false;
}
if (password.length < 6) {
alert('至少输入6个字符');
return false;
}
return true;
}
</script>
</body>
</html>
<?php
exit;
}
// ==================== Token管理函数 ====================
/**
* 从PHP缓存文件读取Token
*/
function getCachedToken() {
if (!file_exists(TOKEN_CACHE_FILE)) {
return null;
}
// 包含文件获取变量
include TOKEN_CACHE_FILE;
if (!isset($cached_token) || !isset($cached_timestamp)) {
return null;
}
// 检查Token是否过期1小时内有效
$currentTime = time();
$tokenAge = $currentTime - $cached_timestamp;
if ($tokenAge < TOKEN_EXPIRE_TIME) {
return $cached_token;
}
// Token已过期清除缓存
@unlink(TOKEN_CACHE_FILE);
return null;
}
/**
* 保存Token到PHP缓存文件
*/
function saveTokenToCache($token) {
$timestamp = time();
$expireAt = date('Y-m-d H:i:s', $timestamp + TOKEN_EXPIRE_TIME);
$content = "<?php\n";
$content .= "// Token缓存文件 - 自动生成\n";
$content .= "// 生成时间: " . date('Y-m-d H:i:s', $timestamp) . "\n";
$content .= "// 过期时间: {$expireAt}\n";
$content .= "// 警告: 不要手动修改此文件\n\n";
$content .= "\$cached_token = '" . addslashes($token) . "';\n";
$content .= "\$cached_timestamp = {$timestamp};\n";
file_put_contents(TOKEN_CACHE_FILE, $content);
// 设置文件权限(如果可能)
if (function_exists('chmod')) {
@chmod(TOKEN_CACHE_FILE, 0600);
}
}
/**
* 获取登录Token带缓存
*/
function getLoginToken() {
// 首先尝试从缓存获取
$cachedToken = getCachedToken();
if ($cachedToken) {
return $cachedToken;
}
// 缓存不存在或已过期重新获取Token
$url = BASE_URL . '/login_api';
$data = [
'account' => ACCOUNT,
'password' => API_KEY
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
return null;
}
$result = json_decode($response, true);
$token = isset($result['jwt']) ? $result['jwt'] : null;
// 保存新Token到缓存
if ($token) {
saveTokenToCache($token);
}
return $token;
}
// ==================== 功能实现区域 ====================
/**
* 发送API请求
*/
function sendApiRequest($endpoint, $method = 'GET', $data = []) {
$token = getLoginToken();
if (!$token) {
return ['status' => 500, 'msg' => '获取Token失败请检查账号和密码配置'];
}
$url = BASE_URL . $endpoint;
$headers = [
'Authorization: JWT ' . $token,
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
} elseif ($method === 'PUT') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return json_decode($response, true);
}
/**
* 获取VPS列表
*/
function getHostList($page = 1, $limit = 100) {
return sendApiRequest("/hosts?page={$page}&limit={$limit}", 'GET');
}
/**
* 获取VPS状态
*/
function getHostStatus($hostId) {
return sendApiRequest("/hosts/{$hostId}/module/status?type=host", 'GET');
}
/**
* 操作VPS
*/
function operateHost($hostId, $operation) {
return sendApiRequest("/hosts/{$hostId}/module/{$operation}", 'PUT');
}
// ==================== 主逻辑区域 ====================
// 验证pass参数
$pass = isset($_GET['pass']) ? $_GET['pass'] : '';
if ($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;
}
// 处理操作请求
$action = isset($_POST['action']) ? $_POST['action'] : '';
$hostId = isset($_POST['host_id']) ? intval($_POST['host_id']) : 0;
$result = null;
if ($action && $hostId > 0) {
if ($action === 'reboot') {
$result = operateHost($hostId, 'hard_reboot');
} elseif ($action === 'on') {
$result = operateHost($hostId, 'on');
} elseif ($action === 'off') {
$result = operateHost($hostId, 'off');
}
}
// 获取VPS列表和状态
$hosts = [];
$statusMap = [];
$domainstatus = [];
$totalCount = 0;
$errorMsg = '';
$listResult = getHostList();
if (isset($listResult['status']) && $listResult['status'] === 200) {
$data = $listResult['data'];
$hosts = $data['host'];
$domainstatus = $data['domainstatus'];
$totalCount = $data['total'];
// 批量获取所有VPS的状态
foreach ($hosts as $host) {
$statusResult = getHostStatus($host['id']);
if (isset($statusResult['status']) && $statusResult['status'] === 200) {
$statusMap[$host['id']] = $statusResult['data'];
} else {
$statusMap[$host['id']] = ['status' => 'unknown', 'des' => '未知'];
}
}
} else {
$errorMsg = isset($listResult['msg']) ? $listResult['msg'] : '获取VPS列表失败';
}
header('Content-Type: text/html; charset=utf-8');
?>
<!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管理面板</title>
</head>
<body>
<div class="container">
<div class="header">
<h1>🖥️ 核云IDC VPS管理面板</h1>
<p>实时查看和管理您的云服务器</p>
</div>
<div class="content">
<?php if (!empty($errorMsg)): ?>
<div class="error-message">
<h3>❌ 加载失败</h3>
<p><?php echo htmlspecialchars($errorMsg); ?></p>
</div>
<?php else: ?>
<?php if (!empty($result)): ?>
<?php if (isset($result['status']) && $result['status'] === 200): ?>
<div class="success-message">
<h3>✅ 操作成功</h3>
<p>VPS #<?php echo $hostId; ?> 已成功执行操作</p>
</div>
<?php else: ?>
<div class="error-message">
<h3>❌ 操作失败</h3>
<p><?php echo isset($result['msg']) ? htmlspecialchars($result['msg']) : '未知错误'; ?></p>
</div>
<?php endif; ?>
<?php endif; ?>
<?php
$cachedToken = getCachedToken();
if ($cachedToken):
// 从PHP文件读取时间戳
include TOKEN_CACHE_FILE;
$remainingTime = TOKEN_EXPIRE_TIME - (time() - $cached_timestamp);
$remainingMinutes = floor($remainingTime / 60);
?>
<div class="token-info">
✅ <strong>Token缓存生效中</strong> | 剩余有效期:约 <?php echo $remainingMinutes; ?> 分钟
</div>
<?php endif; ?>
<div class="stats-bar">
<div class="stats-item">
总计:<strong><?php echo $totalCount; ?></strong> 台服务器
</div>
<div class="stats-item">
运行中:<strong><?php
$runningCount = 0;
foreach ($statusMap as $status) {
if ($status['status'] === 'on') $runningCount++;
}
echo $runningCount;
?></strong> 台
</div>
<div class="stats-item">
已关机:<strong><?php
$offCount = 0;
foreach ($statusMap as $status) {
if ($status['status'] === 'off') $offCount++;
}
echo $offCount;
?></strong> 台
</div>
</div>
<div class="card-grid">
<?php foreach ($hosts as $host):
$statusKey = $host['domainstatus'];
$statusInfo = isset($domainstatus[$statusKey]) ? $domainstatus[$statusKey] : ['name' => $statusKey, 'color' => '#999'];
$powerStatus = isset($statusMap[$host['id']]) ? $statusMap[$host['id']] : ['status' => 'unknown', 'des' => '未知'];
$regDate = date('Y-m-d', $host['regdate']);
$nextDueDate = date('Y-m-d', $host['nextduedate']);
$powerClass = 'power-unknown';
if ($powerStatus['status'] === 'on') {
$powerClass = 'power-on';
} elseif ($powerStatus['status'] === 'off') {
$powerClass = 'power-off';
}
?>
<div class="vps-card">
<div class="card-header">
<div class="vps-id">#<?php echo $host['id']; ?></div>
<span class="status-badge" style="background-color: <?php echo $statusInfo['color']; ?>">
<?php echo htmlspecialchars($statusInfo['name']); ?>
</span>
</div>
<div class="card-body">
<div class="info-row">
<span class="info-label">域名</span>
<span class="info-value"><?php echo htmlspecialchars($host['domain']); ?></span>
</div>
<div class="info-row">
<span class="info-label">IP地址</span>
<span class="info-value"><?php echo htmlspecialchars($host['dedicatedip']); ?></span>
</div>
<div class="info-row">
<span class="info-label">产品名称</span>
<span class="info-value"><?php echo htmlspecialchars($host['product_name']); ?></span>
</div>
<div class="info-row">
<span class="info-label">注册日期</span>
<span class="info-value"><?php echo $regDate; ?></span>
</div>
<div class="info-row">
<span class="info-label">到期日期</span>
<span class="info-value"><?php echo $nextDueDate; ?></span>
</div>
<div class="info-row">
<span class="info-label">金额</span>
<span class="info-value">¥<?php echo htmlspecialchars($host['amount']); ?></span>
</div>
<div class="info-row">
<span class="info-label">计费周期</span>
<span class="info-value"><?php echo htmlspecialchars($host['billingcycle']); ?></span>
</div>
</div>
<div class="power-status">
<span class="power-indicator <?php echo $powerClass; ?>"></span>
<span class="power-text"><?php echo htmlspecialchars($powerStatus['des']); ?></span>
</div>
<form method="POST" style="margin: 0;">
<input type="hidden" name="host_id" value="<?php echo $host['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('确认硬重启?')" style="grid-column: span 2;">
🔄 硬重启
</button>
</div>
</form>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</body>
</html>