VPSHUB/mofangidc.php
2026-05-29 23:09:58 +08:00

724 lines
23 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
/**
* 魔方平台API接口封装
* 所有智简魔方的操作API以及相关函数存储在此文件中
*/
require_once __DIR__ . '/app/db_helper.php';
// Token缓存文件路径
define('TOKEN_CACHE_FILE', __DIR__ . '/app/token.php');
define('TOKEN_EXPIRE_TIME', 7200); // Token过期时间2小时
/**
* 获取缓存的Token
* @param int $configId 配置ID
* @return string|null Token字符串或null
*/
function getCachedToken($configId) {
if (!file_exists(TOKEN_CACHE_FILE)) {
return null;
}
include TOKEN_CACHE_FILE;
if (!isset($cached_tokens[$configId])) {
return null;
}
$cache = $cached_tokens[$configId];
$currentTime = time();
$tokenAge = $currentTime - $cache['timestamp'];
if ($tokenAge < TOKEN_EXPIRE_TIME) {
return $cache['token'];
}
// Token已过期清除该配置的缓存
unset($cached_tokens[$configId]);
saveTokensFile($cached_tokens);
return null;
}
/**
* 保存Token到缓存文件
* @param int $configId 配置ID
* @param string $token Token字符串
*/
function saveToken($configId, $token) {
$cached_tokens = [];
if (file_exists(TOKEN_CACHE_FILE)) {
include TOKEN_CACHE_FILE;
}
$cached_tokens[$configId] = [
'token' => $token,
'timestamp' => time()
];
saveTokensFile($cached_tokens);
}
/**
* 保存tokens数组到文件
* @param array $cached_tokens tokens数组
*/
function saveTokensFile($cached_tokens) {
$content = "<?php\n";
$content .= "// Token缓存文件 - 自动生成\n";
$content .= "// 警告: 不要手动修改此文件\n\n";
$content .= '$cached_tokens = ' . var_export($cached_tokens, true) . ";\n";
$content .= "?>\n";
file_put_contents(TOKEN_CACHE_FILE, $content);
chmod(TOKEN_CACHE_FILE, 0600);
}
/**
* 魔方平台登录获取JWT Token
* @param string $siteUrl API基础URL
* @param string $account 账户
* @param string $apiKey API密钥
* @return string|null JWT Token或null
*/
function mofangLogin($siteUrl, $account, $apiKey) {
try {
$url = rtrim($siteUrl, '/') . '/v1/login_api';
$data = [
'account' => $account,
'password' => $apiKey
];
$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);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
error_log("魔方登录请求失败HTTP状态码: {$httpCode}");
return null;
}
$result = json_decode($response, true);
if (isset($result['status']) && $result['status'] === 200 && isset($result['jwt'])) {
return $result['jwt'];
} else {
error_log("魔方登录失败: " . ($result['msg'] ?? '未知错误'));
return null;
}
} catch (Exception $e) {
error_log("魔方登录异常: " . $e->getMessage());
return null;
}
}
/**
* 获取有效的Token(先查缓存,没有则重新登录)
* @param int $configId 配置ID
* @return string|null Token或null
*/
function getValidToken($configId) {
// 先尝试从缓存获取
$token = getCachedToken($configId);
if ($token) {
return $token;
}
// 缓存不存在或已过期,重新获取
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
error_log("配置ID {$configId} 不存在");
return null;
}
$token = mofangLogin($config['site_url'], $config['account'], $config['api_key']);
if ($token) {
saveToken($configId, $token);
}
return $token;
}
/**
* 发送魔方API请求
* @param string $siteUrl API基础URL
* @param string $endpoint API端点
* @param string $method HTTP方法 (GET/POST/PUT)
* @param array $data 请求数据
* @param int $configId 配置ID
* @return array|null 响应数据或null
*/
function mofangApiRequest($siteUrl, $endpoint, $method = 'GET', $data = [], $configId = null) {
// 获取Token
if ($configId) {
$token = getValidToken($configId);
} else {
// 如果没有configId尝试从第一个配置获取
$db = getVpsDB();
$configs = $db->query('SELECT * FROM configs LIMIT 1');
if (!$configs) {
return null;
}
$config = $configs[0];
$token = getValidToken($config['id']);
$siteUrl = $config['site_url'];
}
if (!$token) {
return ['status' => 400, 'msg' => '获取Token失败'];
}
$url = rtrim($siteUrl, '/') . $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);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
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);
if ($httpCode !== 200) {
return ['status' => $httpCode, 'msg' => 'HTTP请求失败'];
}
$result = json_decode($response, true);
// 如果Token失效(405),清除缓存并重试
if (isset($result['status']) && $result['status'] === 405 && $configId) {
// 清除缓存
$cached_tokens = [];
if (file_exists(TOKEN_CACHE_FILE)) {
include TOKEN_CACHE_FILE;
}
unset($cached_tokens[$configId]);
saveTokensFile($cached_tokens);
// 重试一次
return mofangApiRequest($siteUrl, $endpoint, $method, $data, $configId);
}
return $result;
}
/**
* 获取VPS列表
* @param int $configId 配置ID
* @param int $page 页码
* @param int $limit 每页数量
* @return array|null VPS列表数据或null
*/
function mofangGetVpsList($configId, $page = 1, $limit = 100) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts?page={$page}&limit={$limit}";
return mofangApiRequest($config['site_url'], $endpoint, 'GET', [], $configId);
}
/**
* 获取VPS状态
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @param bool $updateDb 是否更新数据库默认true
* @return array|null 状态数据或null
*/
function mofangGetVpsStatus($configId, $vpsId, $updateDb = true) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts/{$vpsId}/module/status?type=host";
$result = mofangApiRequest($config['site_url'], $endpoint, 'GET', [], $configId);
// 如果获取成功且需要更新数据库
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200 && isset($result['data'])) {
$statusData = $result['data'];
$status = $statusData['status'] ?? 'unknown';
// 只更新status字段不影响其他字段
$listDb = getVpsListDB();
// 先检查记录是否存在
$existing = $listDb->queryOne(
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
[$configId, $vpsId]
);
if ($existing) {
// 记录存在执行UPDATE
$listDb->execute(
'UPDATE vps_list SET status = ?, last_check = CURRENT_TIMESTAMP WHERE config_id = ? AND vps_id = ?',
[$status, $configId, $vpsId]
);
error_log("[mofangGetVpsStatus] VPS {$vpsId} 状态已更新为: {$status}");
} else {
// 记录不存在,插入新记录
$listDb->execute(
'INSERT INTO vps_list (config_id, vps_id, status, last_check) VALUES (?, ?, ?, CURRENT_TIMESTAMP)',
[$configId, $vpsId, $status]
);
error_log("[mofangGetVpsStatus] VPS {$vpsId} 新记录已插入,状态: {$status}");
}
}
return $result;
}
/**
* 获取VPS详细信息
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @param bool $updateDb 是否更新数据库默认true
* @return array|null 详细信息或null
*/
function mofangGetVpsDetails($configId, $vpsId, $updateDb = true) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts/{$vpsId}";
$result = mofangApiRequest($config['site_url'], $endpoint, 'GET', [], $configId);
// 如果获取成功且需要更新数据库
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200 && isset($result['data']['host'])) {
$host = $result['data']['host'];
// 解析详细信息
$updates = []; // 存储要更新的字段
$values = [];
if (isset($host['config_option']) && is_array($host['config_option'])) {
foreach ($host['config_option'] as $option) {
switch ($option['key']) {
case 'cpu':
if (isset($option['value'])) {
preg_match('/(\d+)/', $option['value'], $matches);
if (!empty($matches)) {
$updates[] = 'cpu_cores = ?';
$values[] = intval($matches[1]);
}
}
break;
case 'memory':
if (isset($option['value'])) {
$updates[] = 'memory_size = ?';
$values[] = $option['value'];
}
break;
case 'system_disk_size':
if (isset($option['value'])) {
$updates[] = 'disk_size = ?';
$values[] = $option['value'];
}
break;
case 'bw':
if (isset($option['value'])) {
$updates[] = 'bandwidth = ?';
$values[] = $option['value'];
}
break;
case 'os':
if (isset($option['value'])) {
$updates[] = 'os_type = ?';
$values[] = $option['value'];
}
break;
}
}
}
// 只有当有字段需要更新时才执行UPDATE
if (!empty($updates)) {
// 添加last_check更新
$updates[] = 'last_check = CURRENT_TIMESTAMP';
// 构建动态UPDATE语句
$setClause = implode(', ', $updates);
$values[] = $configId;
$values[] = $vpsId;
$listDb = getVpsListDB();
// 先检查记录是否存在
$existing = $listDb->queryOne(
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
[$configId, $vpsId]
);
if ($existing) {
// 记录存在执行UPDATE
$listDb->execute(
"UPDATE vps_list SET {$setClause} WHERE config_id = ? AND vps_id = ?",
$values
);
error_log("[mofangGetVpsDetails] VPS {$vpsId} 详细信息已更新");
} else {
// 记录不存在,插入新记录(只插入获取到的字段)
$columns = [];
$insertValues = [];
$placeholders = [];
// 解析SET子句中的字段
foreach ($updates as $update) {
if (strpos($update, 'last_check') === false) {
list($column, $value) = explode(' = ', $update);
$columns[] = $column;
$insertValues[] = array_shift($values); // 从$values中取出对应的值
$placeholders[] = '?';
}
}
// 添加config_id和vps_id
$columns[] = 'config_id';
$columns[] = 'vps_id';
$insertValues[] = $configId;
$insertValues[] = $vpsId;
if (!empty($columns)) {
$columnStr = implode(', ', $columns);
$placeholderStr = implode(', ', $placeholders);
$listDb->execute(
"INSERT INTO vps_list ({$columnStr}) VALUES ({$placeholderStr})",
$insertValues
);
error_log("[mofangGetVpsDetails] VPS {$vpsId} 新记录已插入");
}
}
}
}
return $result;
}
/**
* VPS开机
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @return array|null 响应数据或null
*/
function mofangPowerOn($configId, $vpsId) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts/{$vpsId}/module/on";
return mofangApiRequest($config['site_url'], $endpoint, 'PUT', [], $configId);
}
/**
* VPS关机
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @return array|null 响应数据或null
*/
function mofangPowerOff($configId, $vpsId) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts/{$vpsId}/module/off";
return mofangApiRequest($config['site_url'], $endpoint, 'PUT', [], $configId);
}
/**
* VPS硬重启
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @return array|null 响应数据或null
*/
function mofangHardReboot($configId, $vpsId) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return null;
}
$endpoint = "/v1/hosts/{$vpsId}/module/hard_reboot";
return mofangApiRequest($config['site_url'], $endpoint, 'PUT', [], $configId);
}
/**
* 刷新指定配置的VPS列表并保存到数据库
* @param int $configId 配置ID
* @return bool 是否成功
*/
function refreshVpsListForConfig($configId) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return false;
}
// 获取VPS列表
$result = mofangGetVpsList($configId);
if (!$result || !isset($result['status']) || $result['status'] !== 200) {
error_log("获取VPS列表失败: " . ($result['msg'] ?? '未知错误'));
return false;
}
$hosts = $result['data']['host'] ?? [];
if (empty($hosts)) {
return true; // 没有VPS也算成功
}
// 批量保存到数据库
$listDb = getVpsListDB();
foreach ($hosts as $host) {
// 解析详细信息
$cpuCores = null;
$memorySize = null;
$diskSize = null;
$bandwidth = null;
$osType = null;
if (isset($host['config_option']) && is_array($host['config_option'])) {
foreach ($host['config_option'] as $option) {
switch ($option['key']) {
case 'cpu':
if (isset($option['value'])) {
// 提取数字,例如 "16核" -> 16
preg_match('/(\d+)/', $option['value'], $matches);
if (!empty($matches)) {
$cpuCores = intval($matches[1]);
}
}
break;
case 'memory':
if (isset($option['value'])) {
$memorySize = $option['value']; // 例如 "16G"
}
break;
case 'system_disk_size':
if (isset($option['value'])) {
$diskSize = $option['value']; // 例如 "Lin50G,Win50G"
}
break;
case 'bw':
if (isset($option['value'])) {
$bandwidth = $option['value']; // 例如 "70Mbps"
}
break;
case 'os':
if (isset($option['value'])) {
$osType = $option['value']; // 例如 "Debian-12.0_x64"
}
break;
}
}
}
// 使用INSERT OR REPLACE防止重复数据
// 注意需要保留原有的status字段避免被覆盖为NULL
$existing = $listDb->queryOne(
'SELECT status FROM vps_list WHERE config_id = ? AND vps_id = ?',
[$configId, $host['id']]
);
$currentStatus = $existing ? $existing['status'] : null;
$listDb->execute(
'INSERT OR REPLACE INTO vps_list (config_id, vps_id, domain, ip_address, product_name, cpu_cores, memory_size, disk_size, bandwidth, os_type, status, section, last_check) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)',
[
$configId,
$host['id'],
$host['domain'] ?? null,
$host['dedicatedip'] ?? null,
$host['product_name'] ?? null,
$cpuCores,
$memorySize,
$diskSize,
$bandwidth,
$osType,
$currentStatus, // 保留原有状态
$config['auto_monitor'] ? 1 : 0
]
);
}
return true;
}
/**
* 刷新所有配置的VPS列表
* @return int 成功刷新的配置数量
*/
function refreshAllVpsLists() {
$db = getVpsDB();
$configs = $db->query('SELECT * FROM configs');
$successCount = 0;
foreach ($configs as $config) {
if (refreshVpsListForConfig($config['id'])) {
$successCount++;
}
}
return $successCount;
}
/**
* 获取单个VPS的状态并更新到数据库
* @param int $configId 配置ID
* @param int $vpsId VPS ID
* @return array|null 状态信息或null
*/
function updateVpsStatusToDb($configId, $vpsId) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
error_log("配置ID {$configId} 不存在");
return null;
}
// 调用API获取VPS状态不自动更新数据库由本函数统一处理
$result = mofangGetVpsStatus($configId, $vpsId, false);
if (!$result || !isset($result['status']) || $result['status'] !== 200) {
error_log("获取VPS {$vpsId} 状态失败: " . ($result['msg'] ?? '未知错误'));
return null;
}
$statusData = $result['data'];
$status = $statusData['status'] ?? 'unknown';
$des = $statusData['des'] ?? '未知';
// 检查记录是否存在
$listDb = getVpsListDB();
$existing = $listDb->queryOne(
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
[$configId, $vpsId]
);
if ($existing) {
// 记录存在执行UPDATE
$listDb->execute(
'UPDATE vps_list SET status = ?, last_check = CURRENT_TIMESTAMP WHERE config_id = ? AND vps_id = ?',
[$status, $configId, $vpsId]
);
error_log("[updateVpsStatusToDb] VPS {$vpsId} 状态已更新为: {$status}");
} else {
// 记录不存在,插入新记录
$listDb->execute(
'INSERT INTO vps_list (config_id, vps_id, status, last_check) VALUES (?, ?, ?, CURRENT_TIMESTAMP)',
[$configId, $vpsId, $status]
);
error_log("[updateVpsStatusToDb] VPS {$vpsId} 新记录已插入,状态: {$status}");
}
return [
'vps_id' => $vpsId,
'status' => $status,
'des' => $des,
'updated' => true
];
}
/**
* 批量获取VPS状态并更新到数据库
* @param int $configId 配置ID
* @param array $vpsIds VPS ID数组为空则更新该配置下所有VPS
* @return array 更新结果统计
*/
function batchUpdateVpsStatus($configId, $vpsIds = []) {
$db = getVpsDB();
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
if (!$config) {
return ['success' => 0, 'failed' => 0, 'error' => '配置不存在'];
}
// 如果未指定VPS IDs获取该配置下的所有VPS
if (empty($vpsIds)) {
$listDb = getVpsListDB();
$vpsList = $listDb->query('SELECT vps_id FROM vps_list WHERE config_id = ?', [$configId]);
$vpsIds = array_column($vpsList, 'vps_id');
}
if (empty($vpsIds)) {
return ['success' => 0, 'failed' => 0, 'error' => '没有VPS需要更新'];
}
$successCount = 0;
$failedCount = 0;
$results = [];
foreach ($vpsIds as $vpsId) {
$result = updateVpsStatusToDb($configId, $vpsId);
if ($result) {
$successCount++;
$results[] = $result;
} else {
$failedCount++;
$results[] = [
'vps_id' => $vpsId,
'status' => 'error',
'des' => '获取状态失败',
'updated' => false
];
}
}
return [
'success' => $successCount,
'failed' => $failedCount,
'total' => count($vpsIds),
'results' => $results
];
}
?>