更新:添加lxdwebserver改进和新增lxdapiuserserver模块

This commit is contained in:
勿忘心安
2025-12-30 02:07:12 +00:00
parent 1820db2850
commit e911ba3694
3 changed files with 898 additions and 1 deletions

View File

@@ -0,0 +1,820 @@
<?php
use think\Db;
define('LXDAPISERVER_DEBUG', false);
function lxdapiuserserver_debug($message, $data = null)
{
if (!LXDAPISERVER_DEBUG) return;
$log = '[LXDAPISERVER-DEBUG] ' . date('Y-m-d H:i:s') . ' | ' . $message;
if ($data !== null) {
$log .= ' | Data: ' . json_encode($data, JSON_UNESCAPED_UNICODE);
}
$log .= PHP_EOL;
$logFile = __DIR__ . '/debug.log';
file_put_contents($logFile, $log, FILE_APPEND);
}
function lxdapiuserserver_MetaData()
{
return [
'DisplayName' => '魔方财务-LXDAPI用户销售对接插件 by xkatld',
'APIVersion' => 'v2.0.5',
'HelpDoc' => 'https://github.com/xkatld/lxdapi-web-server',
];
}
function lxdapiuserserver_ConfigOptions()
{
return [
'cpus' => [
'type' => 'text',
'name' => 'CPU核心数',
'description' => 'CPU核心数量',
'default' => '1',
'key' => 'cpus',
],
'memory' => [
'type' => 'text',
'name' => '内存',
'description' => '内存大小单位MB',
'default' => '512',
'key' => 'memory',
],
'disk' => [
'type' => 'text',
'name' => '硬盘',
'description' => '硬盘大小单位MB',
'default' => '1024',
'key' => 'disk',
],
'image' => [
'type' => 'text',
'name' => '镜像',
'description' => '系统镜像名称',
'default' => 'alpine320',
'key' => 'image',
],
'traffic_limit' => [
'type' => 'text',
'name' => '月流量限制',
'description' => '单位GB',
'default' => '100',
'key' => 'traffic_limit',
],
'ipv4_pool_limit' => [
'type' => 'text',
'name' => 'IPv4地址池限制',
'description' => 'IPv4独立地址数量上限',
'default' => '0',
'key' => 'ipv4_pool_limit',
],
'ipv4_mapping_limit' => [
'type' => 'text',
'name' => 'IPv4端口映射限制',
'description' => 'IPv4端口转发规则上限',
'default' => '0',
'key' => 'ipv4_mapping_limit',
],
'ipv6_pool_limit' => [
'type' => 'text',
'name' => 'IPv6地址池限制',
'description' => 'IPv6独立地址数量上限',
'default' => '0',
'key' => 'ipv6_pool_limit',
],
'ipv6_mapping_limit' => [
'type' => 'text',
'name' => 'IPv6端口映射限制',
'description' => 'IPv6端口转发规则上限',
'default' => '0',
'key' => 'ipv6_mapping_limit',
],
'reverse_proxy_limit' => [
'type' => 'text',
'name' => '反向代理限制',
'description' => '反向代理域名数量上限',
'default' => '0',
'key' => 'reverse_proxy_limit',
],
];
}
function lxdapiuserserver_ParseMemory($str)
{
$str = trim($str);
if (empty($str)) return 0;
if (stripos($str, 'GB') !== false) {
return intval($str) * 1024;
} elseif (stripos($str, 'MB') !== false) {
return intval($str);
} else {
return intval($str);
}
}
function lxdapiuserserver_ParseBandwidth($str)
{
$str = trim($str);
if (empty($str)) return 0;
if (stripos($str, 'Gbit') !== false) {
return intval($str) * 1000;
} elseif (stripos($str, 'Mbit') !== false) {
return intval($str);
} else {
return intval($str);
}
}
function lxdapiuserserver_ApiRequest($params, $endpoint, $data = [], $method = 'POST')
{
$curl = curl_init();
$protocol = 'https';
$url = $protocol . '://' . $params['server_ip'] . ':' . $params['port'] . $endpoint;
// 使用正确的参数名
$username = $params['server_username'] ?? $params['username'] ?? '';
$password = $params['server_password'] ?? $params['password'] ?? '';
lxdapiuserserver_debug('API请求', [
'url' => $url,
'method' => $method,
'username' => $username
]);
$curlOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'X-User-Username: ' . $username,
'X-User-Password: ' . $password,
'Content-Type: application/json',
],
];
$curlOptions[CURLOPT_SSL_VERIFYPEER] = false;
$curlOptions[CURLOPT_SSL_VERIFYHOST] = false;
$curlOptions[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2;
if ($method === 'POST' || $method === 'PUT') {
if (!empty($data)) {
$curlOptions[CURLOPT_POSTFIELDS] = json_encode($data);
}
}
curl_setopt_array($curl, $curlOptions);
$response = curl_exec($curl);
$errno = curl_errno($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
lxdapiuserserver_debug('API响应', [
'http_code' => $httpCode,
'response_length' => strlen($response),
'curl_errno' => $errno
]);
if ($errno) {
lxdapiuserserver_debug('CURL错误', [
'errno' => $errno,
'error' => $curlError
]);
return null;
}
$decoded = json_decode($response, true);
return $decoded;
}
function lxdapiuserserver_TestLink($params)
{
lxdapiuserserver_debug('测试API连接', $params);
$res = lxdapiuserserver_ApiRequest($params, '/api/user/containers', [], 'GET');
lxdapiuserserver_debug('TestLink API响应', $res);
if ($res === null) {
return [
'status' => 200,
'data' => [
'server_status' => 0,
'msg' => '连接失败: 无法连接到服务器'
]
];
}
if (isset($res['code']) && $res['code'] == 200) {
return [
'status' => 200,
'data' => [
'server_status' => 1,
'msg' => '连接成功'
]
];
}
return [
'status' => 200,
'data' => [
'server_status' => 0,
'msg' => '连接失败: ' . ($res['msg'] ?? '未知错误')
]
];
}
function lxdapiuserserver_CreateAccount($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('创建容器', ['domain' => $containerName]);
// 使用正确的参数名检查
$username = $params['server_username'] ?? $params['username'] ?? '';
$password = $params['server_password'] ?? $params['password'] ?? '';
if (empty($username) || empty($password)) {
lxdapiuserserver_debug('认证参数缺失', $params);
return ['status' => 'error', 'msg' => '服务器配置错误:缺少认证信息'];
}
$configoptions = $params['configoptions'];
$requestData = [
'name' => $containerName,
'password' => $params['password'],
'image' => $configoptions['image'] ?? 'alpine320',
'cpu' => (int)($configoptions['cpus'] ?? 1),
'memory' => (int)($configoptions['memory'] ?? 512),
'disk' => (int)($configoptions['disk'] ?? 1024),
'traffic_limit' => (int)($configoptions['traffic_limit'] ?? 100),
'ipv4_pool_limit' => (int)($configoptions['ipv4_pool_limit'] ?? 0),
'ipv4_mapping_limit' => (int)($configoptions['ipv4_mapping_limit'] ?? 0),
'ipv6_pool_limit' => (int)($configoptions['ipv6_pool_limit'] ?? 0),
'ipv6_mapping_limit' => (int)($configoptions['ipv6_mapping_limit'] ?? 0),
'reverse_proxy_limit' => (int)($configoptions['reverse_proxy_limit'] ?? 0),
];
lxdapiuserserver_debug('创建请求数据', $requestData);
$res = lxdapiuserserver_ApiRequest($params, '/api/user/containers', $requestData, 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
try {
$update = [
'domainstatus' => 'Active',
'username' => 'root',
'dedicatedip' => $params['server_ip'],
'bwlimit' => (int)($configoptions['traffic_limit'] ?? 100),
];
Db::name('host')->where('id', $params['hostid'])->update($update);
lxdapiuserserver_debug('数据库更新成功', $update);
} catch (\Exception $e) {
return ['status' => 'error', 'msg' => '创建成功但同步数据失败: ' . $e->getMessage()];
}
return ['status' => 'success', 'msg' => $res['msg'] ?? '创建成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '创建失败'];
}
function lxdapiuserserver_TerminateAccount($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('删除容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName);
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'DELETE');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '删除成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '删除失败'];
}
function lxdapiuserserver_On($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('启动容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=start';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '启动成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '启动失败'];
}
function lxdapiuserserver_Off($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('停止容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=stop';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '停止成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '停止失败'];
}
function lxdapiuserserver_Reboot($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('重启容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=restart';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '重启成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '重启失败'];
}
function lxdapiuserserver_SuspendAccount($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('暂停容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=suspend';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '暂停成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '暂停失败'];
}
function lxdapiuserserver_UnsuspendAccount($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('恢复容器', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=unsuspend';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '恢复成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '恢复失败'];
}
function lxdapiuserserver_Status($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('查询状态', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName);
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'GET');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200 && isset($res['data']['status'])) {
$containerStatus = $res['data']['status'];
$result = ['status' => 'success'];
switch (strtoupper($containerStatus)) {
case 'RUNNING':
$result['data']['status'] = 'on';
$result['data']['des'] = '运行中';
break;
case 'STOPPED':
$result['data']['status'] = 'off';
$result['data']['des'] = '已停止';
break;
case 'FROZEN':
$result['data']['status'] = 'suspend';
$result['data']['des'] = '已暂停';
break;
default:
$result['data']['status'] = 'unknown';
$result['data']['des'] = '未知状态';
break;
}
return $result;
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '查询失败'];
}
function lxdapiuserserver_UsageUpdate($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
$res = lxdapiuserserver_ApiRequest($params, '/api/user/traffic?name=' . urlencode($containerName), [], 'GET');
if ($res === null || !isset($res['code']) || $res['code'] != 200) {
return ['status' => 'error', 'msg' => '获取流量失败'];
}
$usedGB = isset($res['data']['TotalGB']) ? (float)$res['data']['TotalGB'] : 0;
Db::name('host')->where('id', $params['hostid'])->update(['bwusage' => $usedGB]);
return ['status' => 'success', 'msg' => '流量同步成功'];
}
function lxdapiuserserver_Sync($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('同步容器信息', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName);
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'GET');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
try {
$update = [];
if (isset($res['data']['status'])) {
$containerStatus = strtoupper($res['data']['status']);
if ($containerStatus === 'RUNNING') {
$update['domainstatus'] = 'Active';
} elseif ($containerStatus === 'STOPPED') {
$update['domainstatus'] = 'Suspended';
}
}
if (isset($res['data']['traffic_limit'])) {
$update['bwlimit'] = (int)$res['data']['traffic_limit'];
}
$trafficRes = lxdapiuserserver_ApiRequest($params, '/api/user/traffic?name=' . urlencode($containerName), [], 'GET');
if ($trafficRes && isset($trafficRes['code']) && $trafficRes['code'] == 200 && isset($trafficRes['data']['TotalGB'])) {
$update['bwusage'] = (float)$trafficRes['data']['TotalGB'];
}
if (!empty($update)) {
Db::name('host')->where('id', $params['hostid'])->update($update);
lxdapiuserserver_debug('同步数据库成功', $update);
}
return ['status' => 'success', 'msg' => '同步成功'];
} catch (\Exception $e) {
return ['status' => 'error', 'msg' => '同步失败: ' . $e->getMessage()];
}
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '同步失败'];
}
function lxdapiuserserver_Reinstall($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('重装系统', ['domain' => $containerName, 'reinstall_os' => $params['reinstall_os'] ?? 'null']);
if (empty($params['reinstall_os'])) {
return ['status' => 'error', 'msg' => '操作系统参数错误'];
}
$requestData = [
'image' => $params['reinstall_os'],
'password' => $params['password'],
];
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=reinstall';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, $requestData, 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '重装成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '重装失败'];
}
function lxdapiuserserver_AdminButton($params)
{
if (!empty($params['domain'])) {
return [
'Sync' => '同步状态',
'TrafficReset' => '重置流量',
];
}
return [];
}
function lxdapiuserserver_TrafficReset($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('重置流量', ['domain' => $containerName]);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=reset-traffic';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => $res['msg'] ?? '流量重置成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '流量重置失败'];
}
function lxdapiuserserver_CrackPassword($params, $new_pass)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('重置密码', ['domain' => $containerName]);
$requestData = [
'password' => $new_pass
];
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/action?action=reset-password';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, $requestData, 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
try {
Db::name('host')->where('id', $params['hostid'])->update(['password' => $new_pass]);
} catch (\Exception $e) {
return ['status' => 'error', 'msg' => '密码重置成功但同步数据失败: ' . $e->getMessage()];
}
return ['status' => 'success', 'msg' => $res['msg'] ?? '密码重置成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '密码重置失败'];
}
function lxdapiuserserver_vnc($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('VNC控制台', ['domain' => $containerName]);
$requestData = ['hostname' => $containerName];
$res = lxdapiuserserver_ApiRequest($params, '/api/user/console/create-token', $requestData, 'POST');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200 && isset($res['data']['token'])) {
$consoleUrl = 'https://' . $params['server_ip'] . ':' . $params['port'] . '/console?token=' . $res['data']['token'];
return [
'status' => 'success',
'url' => $consoleUrl
];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? 'VNC连接失败'];
}
function lxdapiuserserver_ClientArea($params)
{
return [
'info' => ['name' => '容器信息'],
];
}
function lxdapiuserserver_ClientAreaOutput($params, $key)
{
lxdapiuserserver_debug('ClientAreaOutput调用', ['key' => $key]);
if ($key == 'info') {
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/credential';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, [], 'GET');
$jumpUrl = '';
$iframeUrl = '';
$accessCode = '';
$errorMsg = '';
if (isset($res['code']) && $res['code'] == 200 && isset($res['data'])) {
$accessCode = $res['data']['hash'] ?? '';
$protocol = 'https';
$baseUrl = $protocol . '://' . $params['server_ip'] . ':' . $params['port'];
$jumpUrl = $baseUrl . '/container/dashboard?hash=' . $accessCode;
$iframeUrl = $baseUrl . '/container/dashboard/base?hash=' . $accessCode;
} else {
$errorMsg = $res['msg'] ?? '获取访问码失败';
}
return [
'template' => 'templates/info.html',
'vars' => [
'container_name' => $containerName,
'server_ip' => $params['server_ip'],
'server_port' => $params['port'],
'jump_url' => $jumpUrl,
'iframe_url' => $iframeUrl,
'access_code' => $accessCode,
'error_msg' => $errorMsg,
]
];
}
return '';
}
function lxdapiuserserver_ChangePackage($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
lxdapiuserserver_debug('升级配置', ['domain' => $containerName]);
$configoptions = $params['configoptions'];
$requestData = [
'cpu' => (int)($configoptions['cpus'] ?? 0) ?: null,
'memory' => (int)($configoptions['memory'] ?? 0) ?: null,
'disk' => (int)($configoptions['disk'] ?? 0) ?: null,
'traffic_limit' => (int)($configoptions['traffic_limit'] ?? 0) ?: null,
'ipv4_pool_limit' => (int)($configoptions['ipv4_pool_limit'] ?? 0) ?: null,
'ipv4_mapping_limit' => (int)($configoptions['ipv4_mapping_limit'] ?? 0) ?: null,
'ipv6_pool_limit' => (int)($configoptions['ipv6_pool_limit'] ?? 0) ?: null,
'ipv6_mapping_limit' => (int)($configoptions['ipv6_mapping_limit'] ?? 0) ?: null,
'reverse_proxy_limit'=> (int)($configoptions['reverse_proxy_limit'] ?? 0) ?: null,
];
$requestData = array_filter($requestData, function($v) { return $v !== null; });
lxdapiuserserver_debug('升级请求数据', $requestData);
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/config';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, $requestData, 'PUT');
if ($res === null) {
return ['status' => 'error', 'msg' => '请求失败'];
}
if (isset($res['code']) && $res['code'] == 200) {
if (isset($configoptions['traffic_limit'])) {
Db::name('host')->where('id', $params['hostid'])->update([
'bwlimit' => (int)$configoptions['traffic_limit']
]);
}
return ['status' => 'success', 'msg' => '配置升级成功'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '升级失败'];
}
function lxdapiuserserver_FlowPacketPaid($params)
{
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
$capacity = Db::name('dcim_buy_record')
->where('type', 'flow_packet')
->where('hostid', $params['hostid'])
->where('uid', $params['uid'])
->where('status', 1)
->where('show_status', 0)
->where('pay_time', '>', strtotime(date('Y-m-01 00:00:00')))
->sum('capacity');
$originalTraffic = (int)Db::name('host')->where('id', $params['hostid'])->value('bwlimit');
$originalTraffic = $originalTraffic ?: 10;
$totalTraffic = $originalTraffic + (int)$capacity;
$requestData = ['traffic_limit' => $totalTraffic];
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/config';
$res = lxdapiuserserver_ApiRequest($params, $endpoint, $requestData, 'PUT');
Db::name('host')->where('id', $params['hostid'])->update(['bwlimit' => $totalTraffic]);
if ($res && isset($res['code']) && $res['code'] == 200) {
return ['status' => 'success', 'msg' => '流量包已生效'];
}
return ['status' => 'error', 'msg' => $res['msg'] ?? '更新失败'];
}
function lxdapiuserserver_DailyCron()
{
if (date('Y-m-d') != date('Y-m-01')) {
return;
}
$host_data = Db::name('host')
->alias('h')
->leftJoin('servers s', 'h.serverid=s.id')
->where('s.type', 'lxdapiserver')
->whereIn('h.domainstatus', ['Active', 'Suspended'])
->field('h.*')
->select()
->toArray();
$model = new \app\common\model\HostModel();
foreach ($host_data as $v) {
try {
$params = $model->getProvisionParams($v['id']);
$containerName = is_array($params['domain']) ? $params['domain'][0] : $params['domain'];
$originalTraffic = 0;
$hostConfig = Db::name('host_config_options')
->where('relid', $v['id'])
->find();
if ($hostConfig) {
$configOption = Db::name('product_config_options_sub')
->where('config_id', $hostConfig['configid'])
->where('id', $hostConfig['optionid'])
->find();
if ($configOption && strpos($configOption['option_name'], '|') !== false) {
$originalTraffic = (int)explode('|', $configOption['option_name'])[0];
}
}
if (!$originalTraffic) {
$originalTraffic = (int)($params['configoptions']['traffic_limit'] ?? 0);
if (!$originalTraffic) {
$originalTraffic = Db::name('products')
->where('id', $v['productid'])
->value('config_option7');
$originalTraffic = $originalTraffic ? (int)$originalTraffic : 10;
}
}
$requestData = ['traffic_limit' => $originalTraffic];
$endpoint = '/api/user/containers/' . urlencode($containerName) . '/config';
lxdapiuserserver_ApiRequest($params, $endpoint, $requestData, 'PUT');
} catch (Exception $e) {
}
Db::name('host')->where('id', $v['id'])->update(['bwlimit' => $originalTraffic]);
}
}
function lxdapiuserserver_FiveMinuteCron()
{
$now = date('Y-m-d H:i');
$start = date('Y-m-01') . ' 00:00';
$end = date('Y-m-01') . ' 00:05';
if ($now >= $start && $now <= $end) {
lxdapiuserserver_DailyCron();
}
}

View File

@@ -0,0 +1,68 @@
<div class="container-fluid">
<div class="card shadow mb-3">
<div class="card-header py-2">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-external-link-alt mr-2"></i>容器面板
</h6>
</div>
<div class="card-body py-3">
{if $error_msg}
<div class="alert alert-danger">
<i class="fas fa-exclamation-triangle"></i> {$error_msg}
</div>
{elseif $jump_url}
<div class="d-flex align-items-center">
<a href="{$jump_url}"
target="_blank"
class="btn btn-primary px-4">
<i class="fas fa-external-link-alt mr-2"></i>进入面板
</a>
<span class="ml-3 text-muted" style="font-size: 13px;">
<i class="fas fa-info-circle mr-1"></i>
点击按钮将在新窗口打开容器管理面板,也可以通过下方容器管理进行管理。
</span>
</div>
{/if}
</div>
</div>
{if $iframe_url}
<div class="card shadow">
<div class="card-header py-2">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-desktop mr-2"></i>容器管理
</h6>
</div>
<div class="card-body p-0">
<iframe
id="container-iframe"
src="{$iframe_url}"
style="width: 100%; height: 80vh; border: none; display: block;"
frameborder="0"
scrolling="auto">
</iframe>
</div>
</div>
{/if}
</div>
<style>
.card {
border: none;
}
.card-header {
background-color: #f8f9fc;
border-bottom: 1px solid #e3e6f0;
}
.btn-primary {
background-color: #2563eb;
border-color: #2563eb;
transition: all 0.3s ease;
}
.btn-primary:hover {
background-color: #1d4ed8;
border-color: #1d4ed8;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
</style>

View File

@@ -6,7 +6,7 @@ function lxdwebserver_MetaData()
{
return [
'DisplayName' => '魔方财务-LXD用户对接插件 by xkatld',
'APIVersion' => 'v2.0.4',
'APIVersion' => 'v2.0.5',
'HelpDoc' => 'https://github.com/xkatld/lxdapi-web-server',
];
}
@@ -21,6 +21,13 @@ function lxdwebserver_ConfigOptions()
'default' => '2',
'key' => 'cpu_limit',
],
'max_cpu_per_container' => [
'type' => 'text',
'name' => '单容器CPU限制',
'description' => '单个容器最大CPU核心数0表示不限制',
'default' => '0',
'key' => 'max_cpu_per_container',
],
'memory_limit' => [
'type' => 'text',
'name' => '内存总配额',
@@ -195,6 +202,7 @@ function lxdwebserver_CreateAccount($params)
'username' => $username,
'password' => $params['password'],
'cpu_quota' => (int)($configoptions['cpu_limit'] ?? 2),
'max_cpu_per_container' => (int)($configoptions['max_cpu_per_container'] ?? 0),
'memory_quota' => (int)($configoptions['memory_limit'] ?? 1024),
'disk_quota' => (int)($configoptions['disk_limit'] ?? 10240),
'traffic_limit' => (int)($configoptions['traffic_limit'] ?? 100),
@@ -549,6 +557,7 @@ function lxdwebserver_ChangePackage($params)
$requestData = [
'cpu_quota' => (int)($configoptions['cpu_limit'] ?? 0) ?: null,
'max_cpu_per_container' => (int)($configoptions['max_cpu_per_container'] ?? 0) ?: null,
'memory_quota' => (int)($configoptions['memory_limit'] ?? 0) ?: null,
'disk_quota' => (int)($configoptions['disk_limit'] ?? 0) ?: null,
'traffic_limit' => (int)($configoptions['traffic_limit'] ?? 0) ?: null,