From 1820db2850d831ecf8599528fa477af5a3ff4e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8B=BF=E5=BF=98=E5=BF=83=E5=AE=89?= <36999228+xkatld@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:25:40 +0000 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E6=95=B4=E7=90=86?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=86=97=E4=BD=99=E6=96=87=E4=BB=B6=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=BB=84=E7=BB=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fossbilling}/Servicelxdapi/Api/Admin.php | 0 .../fossbilling}/Servicelxdapi/Api/Client.php | 0 .../Servicelxdapi/Controller/Admin.php | 0 .../Servicelxdapi/Controller/Client.php | 0 .../fossbilling}/Servicelxdapi/Service.php | 0 .../mod_servicelxdapi_config.html.twig | 0 .../mod_servicelxdapi_groups.html.twig | 0 .../mod_servicelxdapi_index.html.twig | 0 .../mod_servicelxdapi_manage.html.twig | 0 .../mod_servicelxdapi_servers.html.twig | 4 - .../mod_servicelxdapi_manage.html.twig | 0 .../mod_servicelxdapi_activated.html.twig | 34 + .../mod_servicelxdapi_canceled.html.twig | 18 + .../mod_servicelxdapi_suspended.html.twig | 22 + .../mod_servicelxdapi_unsuspended.html.twig | 20 + .../fossbilling}/Servicelxdapi/manifest.json | 2 +- .../swapidc_lxdapi/includes/lxd_api.php | 99 +++ .../swapidc/swapidc_lxdapi/swapidc_lxdapi.php | 231 +++++++ Fmis/swapidc/swapidc_lxdapi/version.php | 7 + .../whmcs}/lxdapiserver/functions.php | 0 .../whmcs}/lxdapiserver/lib/lxd_api.php | 0 .../whmcs}/lxdapiserver/lxdapiserver.php | 4 +- .../lxdapiserver/templates/overview.tpl | 0 .../zjmf}/lxdapiserver/lxdapiserver.php | 0 .../zjmf}/lxdapiserver/templates/info.html | 0 .../zjmf}/lxdwebserver/lxdwebserver.php | 0 .../zjmf}/lxdwebserver/templates/info.html | 0 .../zjmfv10}/lxdapiserver/lxdapiserver.php | 2 +- .../zjmfv10}/lxdapiserver/templates/info.html | 0 build_zfs_on_debian.sh => Shell/debian_zfs.sh | 22 +- image_import.sh => Shell/image_import.sh | 111 ++-- Shell/lxd_install.sh | 327 ++++++++++ install.sh => Shell/lxdapi_install.sh | 593 +++++------------- Shell/lxdapi_tool.sh | 112 ++++ update.sh => Shell/lxdapi_update.sh | 81 +-- storage_pool.sh => Shell/storage_pool.sh | 3 +- .../mod_servicelxdapi_activated.html.twig | 68 -- .../mod_servicelxdapi_canceled.html.twig | 68 -- .../mod_servicelxdapi_suspended.html.twig | 68 -- .../mod_servicelxdapi_unsuspended.html.twig | 68 -- 40 files changed, 1135 insertions(+), 829 deletions(-) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/Api/Admin.php (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/Api/Client.php (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/Controller/Admin.php (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/Controller/Client.php (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/Service.php (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_admin/mod_servicelxdapi_config.html.twig (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_admin/mod_servicelxdapi_groups.html.twig (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_admin/mod_servicelxdapi_index.html.twig (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_admin/mod_servicelxdapi_manage.html.twig (100%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig (98%) rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/html_client/mod_servicelxdapi_manage.html.twig (100%) create mode 100644 Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig create mode 100644 Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig create mode 100644 Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig create mode 100644 Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig rename {fossbilling => Fmis/fossbilling}/Servicelxdapi/manifest.json (91%) create mode 100644 Fmis/swapidc/swapidc_lxdapi/includes/lxd_api.php create mode 100644 Fmis/swapidc/swapidc_lxdapi/swapidc_lxdapi.php create mode 100644 Fmis/swapidc/swapidc_lxdapi/version.php rename {whmcs => Fmis/whmcs}/lxdapiserver/functions.php (100%) rename {whmcs => Fmis/whmcs}/lxdapiserver/lib/lxd_api.php (100%) rename {whmcs => Fmis/whmcs}/lxdapiserver/lxdapiserver.php (99%) rename {whmcs => Fmis/whmcs}/lxdapiserver/templates/overview.tpl (100%) rename {zjmf => Fmis/zjmf}/lxdapiserver/lxdapiserver.php (100%) rename {zjmf => Fmis/zjmf}/lxdapiserver/templates/info.html (100%) rename {zjmf => Fmis/zjmf}/lxdwebserver/lxdwebserver.php (100%) rename {zjmf => Fmis/zjmf}/lxdwebserver/templates/info.html (100%) rename {zjmfv10 => Fmis/zjmfv10}/lxdapiserver/lxdapiserver.php (99%) rename {zjmfv10 => Fmis/zjmfv10}/lxdapiserver/templates/info.html (100%) rename build_zfs_on_debian.sh => Shell/debian_zfs.sh (89%) rename image_import.sh => Shell/image_import.sh (57%) create mode 100644 Shell/lxd_install.sh rename install.sh => Shell/lxdapi_install.sh (50%) create mode 100644 Shell/lxdapi_tool.sh rename update.sh => Shell/lxdapi_update.sh (76%) rename storage_pool.sh => Shell/storage_pool.sh (98%) delete mode 100644 fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig delete mode 100644 fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig delete mode 100644 fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig delete mode 100644 fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig diff --git a/fossbilling/Servicelxdapi/Api/Admin.php b/Fmis/fossbilling/Servicelxdapi/Api/Admin.php similarity index 100% rename from fossbilling/Servicelxdapi/Api/Admin.php rename to Fmis/fossbilling/Servicelxdapi/Api/Admin.php diff --git a/fossbilling/Servicelxdapi/Api/Client.php b/Fmis/fossbilling/Servicelxdapi/Api/Client.php similarity index 100% rename from fossbilling/Servicelxdapi/Api/Client.php rename to Fmis/fossbilling/Servicelxdapi/Api/Client.php diff --git a/fossbilling/Servicelxdapi/Controller/Admin.php b/Fmis/fossbilling/Servicelxdapi/Controller/Admin.php similarity index 100% rename from fossbilling/Servicelxdapi/Controller/Admin.php rename to Fmis/fossbilling/Servicelxdapi/Controller/Admin.php diff --git a/fossbilling/Servicelxdapi/Controller/Client.php b/Fmis/fossbilling/Servicelxdapi/Controller/Client.php similarity index 100% rename from fossbilling/Servicelxdapi/Controller/Client.php rename to Fmis/fossbilling/Servicelxdapi/Controller/Client.php diff --git a/fossbilling/Servicelxdapi/Service.php b/Fmis/fossbilling/Servicelxdapi/Service.php similarity index 100% rename from fossbilling/Servicelxdapi/Service.php rename to Fmis/fossbilling/Servicelxdapi/Service.php diff --git a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_config.html.twig b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_config.html.twig similarity index 100% rename from fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_config.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_config.html.twig diff --git a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_groups.html.twig b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_groups.html.twig similarity index 100% rename from fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_groups.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_groups.html.twig diff --git a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_index.html.twig b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_index.html.twig similarity index 100% rename from fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_index.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_index.html.twig diff --git a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_manage.html.twig b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_manage.html.twig similarity index 100% rename from fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_manage.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_manage.html.twig diff --git a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig similarity index 98% rename from fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig index 9e14530..00cfeaa 100644 --- a/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig +++ b/Fmis/fossbilling/Servicelxdapi/html_admin/mod_servicelxdapi_servers.html.twig @@ -163,7 +163,6 @@
-
自签名证书请关闭
@@ -171,7 +170,6 @@
-
@@ -234,7 +232,6 @@
-
自签名证书请关闭
@@ -242,7 +239,6 @@
-
diff --git a/fossbilling/Servicelxdapi/html_client/mod_servicelxdapi_manage.html.twig b/Fmis/fossbilling/Servicelxdapi/html_client/mod_servicelxdapi_manage.html.twig similarity index 100% rename from fossbilling/Servicelxdapi/html_client/mod_servicelxdapi_manage.html.twig rename to Fmis/fossbilling/Servicelxdapi/html_client/mod_servicelxdapi_manage.html.twig diff --git a/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig new file mode 100644 index 0000000..c3e7c28 --- /dev/null +++ b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig @@ -0,0 +1,34 @@ +{% apply spaceless %} +{% autoescape false %} + +

尊敬的 {{ client.first_name }},

+ +

您的容器已激活,可以开始使用了。

+ +

容器信息

+ + + + + + + + + + + + + + + + + + +
容器名称{{ service.container_name }}
服务器{{ service.server }}
用户名root
密码{{ service.password }}
+ +

您可以在用户中心管理您的容器。

+ +

感谢您的使用!

+ +{% endautoescape %} +{% endapply %} diff --git a/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig new file mode 100644 index 0000000..83d09e0 --- /dev/null +++ b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig @@ -0,0 +1,18 @@ +{% apply spaceless %} +{% autoescape false %} + +

尊敬的 {{ client.first_name }},

+ +

您的容器服务已取消。

+ + + + + + +
容器名称{{ service.container_name }}
+ +

如有疑问,请联系客服。

+ +{% endautoescape %} +{% endapply %} diff --git a/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig new file mode 100644 index 0000000..e1a7438 --- /dev/null +++ b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig @@ -0,0 +1,22 @@ +{% apply spaceless %} +{% autoescape false %} + +

尊敬的 {{ client.first_name }},

+ +

您的容器已被暂停。

+ + + + + + + + + + +
容器名称{{ service.container_name }}
原因{{ order.reason|default('费用逾期') }}
+ +

如需恢复服务,请联系我们。

+ +{% endautoescape %} +{% endapply %} diff --git a/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig new file mode 100644 index 0000000..b50df00 --- /dev/null +++ b/Fmis/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig @@ -0,0 +1,20 @@ +{% apply spaceless %} +{% autoescape false %} + +

尊敬的 {{ client.first_name }},

+ +

您的容器已恢复,现在可以正常使用了。

+ + + + + + +
容器名称{{ service.container_name }}
+ +

您可以在用户中心管理您的容器。

+ +

感谢您的续费!

+ +{% endautoescape %} +{% endapply %} diff --git a/fossbilling/Servicelxdapi/manifest.json b/Fmis/fossbilling/Servicelxdapi/manifest.json similarity index 91% rename from fossbilling/Servicelxdapi/manifest.json rename to Fmis/fossbilling/Servicelxdapi/manifest.json index e079fa4..1464085 100644 --- a/fossbilling/Servicelxdapi/manifest.json +++ b/Fmis/fossbilling/Servicelxdapi/manifest.json @@ -5,5 +5,5 @@ "description": "LXDAPI 容器管理插件", "author": "xkatld", "homepage_url": "https://github.com/xkatld/lxdapi-web-server", - "version": "v2.0.2" + "version": "v2.0.3" } diff --git a/Fmis/swapidc/swapidc_lxdapi/includes/lxd_api.php b/Fmis/swapidc/swapidc_lxdapi/includes/lxd_api.php new file mode 100644 index 0000000..9499836 --- /dev/null +++ b/Fmis/swapidc/swapidc_lxdapi/includes/lxd_api.php @@ -0,0 +1,99 @@ +serverip = $data['serverip'] ?? ''; + $this->serverport = $data['serverport'] ?? '8443'; + $this->apikey = $data['serveraccesshash'] ?? ''; + } + + public function get($endpoint, $params = []) { + return $this->request('GET', $endpoint, $params); + } + + public function post($endpoint, $params = []) { + return $this->request('POST', $endpoint, $params); + } + + public function delete($endpoint, $params = []) { + return $this->request('DELETE', $endpoint, $params); + } + + private function request($method, $endpoint, $params = []) { + if (empty($this->serverip)) { + throw new Exception("服务器地址未配置"); + } + + $url = $this->protocol . '://' . $this->serverip . ':' . $this->serverport . $endpoint; + + if ($method === 'GET' && !empty($params) && strpos($endpoint, '?') === false) { + $url .= '?' . http_build_query($params); + } + + $ch = curl_init(); + + $headers = [ + 'X-API-Hash: ' . $this->apikey, + 'Content-Type: application/json', + ]; + + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => $this->timeout, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, + ]); + + if (($method === 'POST' || $method === 'DELETE') && !empty($params)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params)); + } + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlError = curl_error($ch); + $curlErrno = curl_errno($ch); + curl_close($ch); + + if ($curlErrno) { + throw new Exception("连接失败: {$curlError}"); + } + + if (empty($response)) { + throw new Exception("服务器返回空响应 (HTTP {$httpCode})"); + } + + $decoded = json_decode($response, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("响应解析失败: " . json_last_error_msg()); + } + + if ($httpCode >= 200 && $httpCode < 300) { + return [ + 'success' => isset($decoded['code']) ? $decoded['code'] == 200 : true, + 'message' => $decoded['msg'] ?? $decoded['message'] ?? '', + 'data' => $decoded['data'] ?? $decoded, + ]; + } + + return [ + 'success' => false, + 'message' => $decoded['msg'] ?? $decoded['message'] ?? 'HTTP ' . $httpCode, + 'data' => null, + ]; + } +} diff --git a/Fmis/swapidc/swapidc_lxdapi/swapidc_lxdapi.php b/Fmis/swapidc/swapidc_lxdapi/swapidc_lxdapi.php new file mode 100644 index 0000000..5c08601 --- /dev/null +++ b/Fmis/swapidc/swapidc_lxdapi/swapidc_lxdapi.php @@ -0,0 +1,231 @@ + array("Type" => "text", "Size" => "10"), + "内存 (MB)" => array("Type" => "text", "Size" => "10"), + "硬盘 (MB)" => array("Type" => "text", "Size" => "10"), + "系统镜像" => array("Type" => "text", "Size" => "25"), + "入站带宽 (Mbit)" => array("Type" => "text", "Size" => "10"), + "出站带宽 (Mbit)" => array("Type" => "text", "Size" => "10"), + "月流量限制 (GB)" => array("Type" => "text", "Size" => "10"), + "IPv4地址池限制" => array("Type" => "text", "Size" => "10"), + "IPv4端口映射限制" => array("Type" => "text", "Size" => "10"), + "IPv6地址池限制" => array("Type" => "text", "Size" => "10"), + "IPv6端口映射限制" => array("Type" => "text", "Size" => "10"), + "反向代理限制" => array("Type" => "text", "Size" => "10"), + "CPU使用率限制 (%)" => array("Type" => "text", "Size" => "10"), + "磁盘读取限制 (MB/s)" => array("Type" => "text", "Size" => "10"), + "磁盘写入限制 (MB/s)" => array("Type" => "text", "Size" => "10"), + "最大进程数" => array("Type" => "text", "Size" => "10"), + "嵌套虚拟化" => array("Type" => "text", "Size" => "10"), + "Swap开关" => array("Type" => "text", "Size" => "10"), + "特权模式" => array("Type" => "text", "Size" => "10") + ); +} + +function lxdapi_log($message) { + $logFile = __DIR__ . '/lxdapi_debug.log'; + $time = date('Y-m-d H:i:s'); + file_put_contents($logFile, "[$time] $message\n", FILE_APPEND); +} + +function lxdapi_generate_password($length = 16) { + $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*'; + $password = ''; + for ($i = 0; $i < $length; $i++) { + $password .= $chars[rand(0, strlen($chars) - 1)]; + } + return $password; +} + +function get_container_name($data) { + if (!empty($data['注释'])) { + return $data['注释']; + } + $serviceid = $data['serviceid'] ?? 0; + if ($serviceid) { + $result = full_query("SELECT 注释 FROM 服务 WHERE id = " . intval($serviceid)); + if ($row = mysqli_fetch_assoc($result)) { + if (!empty($row['注释'])) { + return $row['注释']; + } + } + } + return 'lxd' . rand(1000, 9999) . $data['serviceid']; +} + +function swapidc_lxdapi_CreateAccount($data) { + lxdapi_log("开始创建账户,服务ID: {$data['serviceid']}"); + + try { + $api = new LXD_API($data); + $password = lxdapi_generate_password(); + $containerName = 'lxd' . rand(1000, 9999) . $data['serviceid']; + + $requestData = [ + 'name' => $containerName, + 'image' => $data['configoption4'] ?? 'alpine320', + 'username' => 'user' . $data['uid'], + 'password' => $password, + 'cpu' => (int)($data['configoption1'] ?? 1), + 'memory' => (int)($data['configoption2'] ?? 512), + 'disk' => (int)($data['configoption3'] ?? 1024), + 'ingress' => (int)($data['configoption5'] ?? 100), + 'egress' => (int)($data['configoption6'] ?? 100), + 'traffic_limit' => (int)($data['configoption7'] ?? 100), + 'ipv4_pool_limit' => (int)($data['configoption8'] ?? 0), + 'ipv4_mapping_limit' => (int)($data['configoption9'] ?? 0), + 'ipv6_pool_limit' => (int)($data['configoption10'] ?? 0), + 'ipv6_mapping_limit' => (int)($data['configoption11'] ?? 0), + 'reverse_proxy_limit' => (int)($data['configoption12'] ?? 0), + 'cpu_allowance' => (int)($data['configoption13'] ?? 50), + 'io_read' => (int)($data['configoption14'] ?? 100), + 'io_write' => (int)($data['configoption15'] ?? 50), + 'processes_limit' => (int)($data['configoption16'] ?? 512), + 'allow_nesting' => ($data['configoption17'] ?? 'true') === 'true', + 'memory_swap' => ($data['configoption18'] ?? 'true') === 'true', + 'privileged' => ($data['configoption19'] ?? 'false') === 'true', + ]; + + $result = $api->post('/api/system/containers', $requestData); + + if (!$result['success']) { + lxdapi_log("创建容器失败: " . ($result['message'] ?? '未知错误')); + return $result['message'] ?? '创建容器失败'; + } + + $ip = $result['data']['ipv4'] ?? 'DHCP'; + + update_query("服务", [ + "用户名" => "root", + "密码" => encrypt($password), + "专用IP" => $ip, + "注释" => $containerName + ], ["id" => $data['serviceid']]); + + lxdapi_log("容器创建成功: $containerName"); + return "成功"; + + } catch (Exception $e) { + lxdapi_log("创建账户异常: " . $e->getMessage()); + return "创建失败: " . $e->getMessage(); + } +} + +function swapidc_lxdapi_SuspendAccount($data) { + lxdapi_log("暂停账户,服务ID: {$data['serviceid']}"); + + try { + $api = new LXD_API($data); + $containerName = get_container_name($data); + $result = $api->post('/api/system/containers/' . urlencode($containerName) . '/action?action=stop', []); + return $result['success'] ? "成功" : ($result['message'] ?? '暂停失败'); + } catch (Exception $e) { + return "暂停失败: " . $e->getMessage(); + } +} + +function swapidc_lxdapi_UnsuspendAccount($data) { + lxdapi_log("解除暂停,服务ID: {$data['serviceid']}"); + + try { + $api = new LXD_API($data); + $containerName = get_container_name($data); + $result = $api->post('/api/system/containers/' . urlencode($containerName) . '/action?action=start', []); + return $result['success'] ? "成功" : ($result['message'] ?? '解除暂停失败'); + } catch (Exception $e) { + return "解除暂停失败: " . $e->getMessage(); + } +} + +function swapidc_lxdapi_TerminateAccount($data) { + lxdapi_log("删除账户,服务ID: {$data['serviceid']}"); + + try { + $api = new LXD_API($data); + $containerName = get_container_name($data); + $result = $api->delete('/api/system/containers/' . urlencode($containerName)); + return $result['success'] ? "成功" : ($result['message'] ?? '删除失败'); + } catch (Exception $e) { + return "删除失败: " . $e->getMessage(); + } +} + +function swapidc_lxdapi_ChangePassword($data) { + lxdapi_log("修改密码,服务ID: {$data['serviceid']}"); + + try { + $api = new LXD_API($data); + $containerName = get_container_name($data); + $newPassword = $data['password'] ?? lxdapi_generate_password(); + + $result = $api->post('/api/system/containers/' . urlencode($containerName) . '/action?action=reset-password', [ + 'password' => $newPassword + ]); + + if ($result['success']) { + update_query("服务", ["密码" => encrypt($newPassword)], ["id" => $data['serviceid']]); + return "成功"; + } + + return $result['message'] ?? '修改密码失败'; + } catch (Exception $e) { + return "修改密码失败: " . $e->getMessage(); + } +} + +function swapidc_lxdapi_ClientArea($data) { + $js_code = " + + "; + + try { + $api = new LXD_API($data); + $containerName = get_container_name($data); + $result = $api->get('/api/system/containers/' . urlencode($containerName) . '/credential'); + + if ($result['success'] && !empty($result['data']['access_code'])) { + $accessCode = $result['data']['access_code']; + $serverHost = $data['serverip'] ?? ''; + $serverPort = $data['serverport'] ?? '8443'; + $iframeUrl = 'https://' . $serverHost . ':' . $serverPort . '/container/dashboard/lite?hash=' . $accessCode; + $panel_html = ""; + return array("", "{$js_code}{$panel_html}
    "); + } else { + $error_html = "
    无法加载管理面板
    " . ($result['message'] ?? '获取访问码失败') . "
    "; + return array("", "
{$js_code}{$error_html}
    "); + } + } catch (Exception $e) { + $error_html = "
    连接失败
    " . $e->getMessage() . "
    "; + return array("", "
{$js_code}{$error_html}
    "); + } +} diff --git a/Fmis/swapidc/swapidc_lxdapi/version.php b/Fmis/swapidc/swapidc_lxdapi/version.php new file mode 100644 index 0000000..2a8621b --- /dev/null +++ b/Fmis/swapidc/swapidc_lxdapi/version.php @@ -0,0 +1,7 @@ +'swapidc_lxdapi', + 'name'=>'SWAPIDC-LXD对接插件 by xkatld', + 'version'=>'2.0.2' +); +?> diff --git a/whmcs/lxdapiserver/functions.php b/Fmis/whmcs/lxdapiserver/functions.php similarity index 100% rename from whmcs/lxdapiserver/functions.php rename to Fmis/whmcs/lxdapiserver/functions.php diff --git a/whmcs/lxdapiserver/lib/lxd_api.php b/Fmis/whmcs/lxdapiserver/lib/lxd_api.php similarity index 100% rename from whmcs/lxdapiserver/lib/lxd_api.php rename to Fmis/whmcs/lxdapiserver/lib/lxd_api.php diff --git a/whmcs/lxdapiserver/lxdapiserver.php b/Fmis/whmcs/lxdapiserver/lxdapiserver.php similarity index 99% rename from whmcs/lxdapiserver/lxdapiserver.php rename to Fmis/whmcs/lxdapiserver/lxdapiserver.php index ffdad36..8edfc94 100644 --- a/whmcs/lxdapiserver/lxdapiserver.php +++ b/Fmis/whmcs/lxdapiserver/lxdapiserver.php @@ -4,7 +4,7 @@ * * @package WHMCS-LXD对接插件 by xkatld * @author xkatld - * @version v2.0.2 + * @version v2.0.3 * @link https://github.com/xkatld/lxdapi-web-server */ @@ -21,7 +21,7 @@ function lxdapiserver_MetaData() { return [ 'DisplayName' => 'WHMCS-LXD对接插件 by xkatld', - 'APIVersion' => 'v2.0.2', + 'APIVersion' => 'v2.0.3', 'RequiresServer' => true, 'DefaultNonSSLPort' => '8443', 'DefaultSSLPort' => '8443', diff --git a/whmcs/lxdapiserver/templates/overview.tpl b/Fmis/whmcs/lxdapiserver/templates/overview.tpl similarity index 100% rename from whmcs/lxdapiserver/templates/overview.tpl rename to Fmis/whmcs/lxdapiserver/templates/overview.tpl diff --git a/zjmf/lxdapiserver/lxdapiserver.php b/Fmis/zjmf/lxdapiserver/lxdapiserver.php similarity index 100% rename from zjmf/lxdapiserver/lxdapiserver.php rename to Fmis/zjmf/lxdapiserver/lxdapiserver.php diff --git a/zjmf/lxdapiserver/templates/info.html b/Fmis/zjmf/lxdapiserver/templates/info.html similarity index 100% rename from zjmf/lxdapiserver/templates/info.html rename to Fmis/zjmf/lxdapiserver/templates/info.html diff --git a/zjmf/lxdwebserver/lxdwebserver.php b/Fmis/zjmf/lxdwebserver/lxdwebserver.php similarity index 100% rename from zjmf/lxdwebserver/lxdwebserver.php rename to Fmis/zjmf/lxdwebserver/lxdwebserver.php diff --git a/zjmf/lxdwebserver/templates/info.html b/Fmis/zjmf/lxdwebserver/templates/info.html similarity index 100% rename from zjmf/lxdwebserver/templates/info.html rename to Fmis/zjmf/lxdwebserver/templates/info.html diff --git a/zjmfv10/lxdapiserver/lxdapiserver.php b/Fmis/zjmfv10/lxdapiserver/lxdapiserver.php similarity index 99% rename from zjmfv10/lxdapiserver/lxdapiserver.php rename to Fmis/zjmfv10/lxdapiserver/lxdapiserver.php index 42c9301..695f882 100644 --- a/zjmfv10/lxdapiserver/lxdapiserver.php +++ b/Fmis/zjmfv10/lxdapiserver/lxdapiserver.php @@ -5,7 +5,7 @@ use app\common\model\HostModel; function lxdapiserver_MetaData(){ return [ 'DisplayName' => '魔方财务V10-LXD对接插件 by xkatld', - 'Version' => 'v2.0.2', + 'Version' => 'v2.0.3', 'HelpDoc' => 'https://github.com/xkatld/lxdapi-web-server', ]; } diff --git a/zjmfv10/lxdapiserver/templates/info.html b/Fmis/zjmfv10/lxdapiserver/templates/info.html similarity index 100% rename from zjmfv10/lxdapiserver/templates/info.html rename to Fmis/zjmfv10/lxdapiserver/templates/info.html diff --git a/build_zfs_on_debian.sh b/Shell/debian_zfs.sh similarity index 89% rename from build_zfs_on_debian.sh rename to Shell/debian_zfs.sh index ff55860..91d6e31 100644 --- a/build_zfs_on_debian.sh +++ b/Shell/debian_zfs.sh @@ -14,20 +14,22 @@ exec &> >(tee -a "$LOG_FILE") set -e set -u -ZFS_VER="2.3.0" - -if [ "$(id -u)" -ne 0 ]; then - echo "错误: 此脚本需要以root权限运行。请使用 'sudo'。" >&2 - exit 1 -fi - DEBIAN_VER=$(cat /etc/debian_version | cut -d. -f1) ARCH=$(uname -m) case "$DEBIAN_VER" in - 11) DEBIAN_NAME="Debian 11 (Bullseye)" ;; - 12) DEBIAN_NAME="Debian 12 (Bookworm)" ;; - 13|trixie) DEBIAN_NAME="Debian 13 (Trixie)" ;; + 11) + DEBIAN_NAME="Debian 11 (Bullseye)" + ZFS_VER="2.2.9" + ;; + 12) + DEBIAN_NAME="Debian 12 (Bookworm)" + ZFS_VER="2.3.5" + ;; + 13|trixie) + DEBIAN_NAME="Debian 13 (Trixie)" + ZFS_VER="2.3.5" + ;; *) echo "错误: 不支持的 Debian 版本: $DEBIAN_VER" exit 1 diff --git a/image_import.sh b/Shell/image_import.sh similarity index 57% rename from image_import.sh rename to Shell/image_import.sh index a014c26..4d2d6ee 100644 --- a/image_import.sh +++ b/Shell/image_import.sh @@ -6,6 +6,8 @@ YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' +LXC="/snap/bin/lxc" + ok() { echo -e "${GREEN}[OK]${NC} $1"; } err() { echo -e "${RED}[ERROR]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } @@ -15,20 +17,6 @@ reading() { read -rp "$(echo -e "${GREEN}[INPUT]${NC} $1")" "$2" } -check_root() { - if [ "$EUID" -ne 0 ]; then - err "请使用 root 用户运行此脚本" - exit 1 - fi -} - -check_lxd() { - if ! command -v lxc &>/dev/null; then - err "未检测到 LXD,请先安装 LXD" - exit 1 - fi -} - detect_arch() { sys_arch=$(uname -m) case $sys_arch in @@ -46,39 +34,48 @@ detect_arch() { ok "系统架构: $ARCH" } -IMAGES_BASE_URL="https://github.com/xkatld/zjmf-lxd-server/releases/download/images" +IMAGES_BASE_URL="https://github.com/xkatld/lxdapi-web-server/releases/download/image" -declare -A IMAGE_MAP -IMAGE_MAP=( - [1]="alma8" [2]="alma9" [3]="alma10" - [4]="alpine319" [5]="alpine320" [6]="alpine321" [7]="alpine322" [8]="alpineEdge" - [9]="amazon2023" - [10]="centos9" [11]="centos10" - [12]="debian11" [13]="debian12" [14]="debian13" - [15]="fedora41" [16]="fedora42" - [17]="oracle8" [18]="oracle9" - [19]="rocky8" [20]="rocky9" [21]="rocky10" - [22]="suse155" [23]="suse156" [24]="suseTumbleweed" - [25]="ubuntu2204" [26]="ubuntu2404" [27]="ubuntu2410" +declare -a IMAGE_LIST=( + "almalinux-8" + "almalinux-9" + "alpine-320" + "alpine-321" + "alpine-322" + "archlinux-latest" + "centos-9-Stream" + "debian-11" + "debian-12" + "debian-13" + "fedora-42" + "fedora-43" + "opensuse-156" + "opensuse-tumbleweed" + "rockylinux-8" + "rockylinux-9" + "ubuntu-2204" + "ubuntu-2404" ) download_and_import() { local image_name="$1" - local image_url="${IMAGES_BASE_URL}/${image_name}-${ARCH}.tar.gz" + local image_type="$2" + local image_url="${IMAGES_BASE_URL}/${image_name}-${ARCH}-${image_type}.tar.gz" - info "下载: ${image_name}-${ARCH}.tar.gz" + info "下载: ${image_name}-${ARCH}-${image_type}.tar.gz" local temp_file=$(mktemp) if wget -q --show-progress -O "$temp_file" "$image_url" 2>&1; then info "导入到 LXD..." - if lxc image import "$temp_file" --alias "$image_name" 2>/dev/null; then - ok "成功导入: $image_name" + local alias="${image_name}-${image_type}" + if $LXC image import "$temp_file" --alias "$alias" 2>/dev/null; then + ok "成功导入: $alias" else - warn "导入失败: $image_name" + warn "导入失败: $alias" fi rm -f "$temp_file" else - warn "下载失败: ${image_name}" + warn "下载失败: ${image_name}-${ARCH}-${image_type}" rm -f "$temp_file" fi } @@ -86,12 +83,10 @@ download_and_import() { show_image_list() { echo echo "============================================================================================================" - echo " 1) alma8 2) alma9 3) alma10 4) alpine319 5) alpine320 " - echo " 6) alpine321 7) alpine322 8) alpineEdge 9) amazon2023 10) centos9 " - echo "11) centos10 12) debian11 13) debian12 14) debian13 15) fedora41 " - echo "16) fedora42 17) oracle8 18) oracle9 19) rocky8 20) rocky9 " - echo "21) rocky10 22) suse155 23) suse156 24) suseTumbleweed " - echo "25) ubuntu2204 26) ubuntu2404 27) ubuntu2410 " + echo " 1) almalinux-8 2) almalinux-9 3) alpine-320 4) alpine-321 5) alpine-322" + echo " 6) archlinux-latest 7) centos-9-Stream 8) debian-11 9) debian-12 10) debian-13" + echo "11) fedora-42 12) fedora-43 13) opensuse-156 14) opensuse-tumbleweed" + echo "15) rockylinux-8 16) rockylinux-9 17) ubuntu-2204 18) ubuntu-2404" echo "============================================================================================================" echo } @@ -101,18 +96,34 @@ menu_import() { info "=== 导入镜像 ===" show_image_list - reading "输入编号,多个用逗号分隔,或 all 全部导入 [2,5,13,26]: " image_choices - image_choices=${image_choices:-"2,5,13,26"} + reading "输入编号,多个用逗号分隔,或 all 全部导入 [8,9,17,18]: " image_choices + image_choices=${image_choices:-"8,9,17,18"} + + while true; do + reading "选择镜像类型 lxc/kvm [lxc]: " image_type + image_type=${image_type:-lxc} + if [[ "$image_type" =~ ^(lxc|kvm)$ ]]; then + break + else + warn "请输入 lxc 或 kvm" + fi + done + + if [[ "$image_type" == "kvm" && "$ARCH" == "arm64" ]]; then + warn "KVM 镜像不支持 arm64 架构" + return + fi if [[ "$image_choices" == "all" ]]; then - selected_images=(${IMAGE_MAP[@]}) + selected_images=("${IMAGE_LIST[@]}") else IFS=',' read -ra choices <<< "$image_choices" selected_images=() for choice in "${choices[@]}"; do choice=$(echo "$choice" | xargs) - if [[ -n "${IMAGE_MAP[$choice]}" ]]; then - selected_images+=("${IMAGE_MAP[$choice]}") + idx=$((choice - 1)) + if [[ $idx -ge 0 && $idx -lt ${#IMAGE_LIST[@]} ]]; then + selected_images+=("${IMAGE_LIST[$idx]}") fi done fi @@ -122,14 +133,14 @@ menu_import() { return fi - ok "已选择 ${#selected_images[@]} 个镜像" + ok "已选择 ${#selected_images[@]} 个镜像 (${image_type})" echo current=0 for img in "${selected_images[@]}"; do ((current++)) echo "[$current/${#selected_images[@]}]" - download_and_import "$img" + download_and_import "$img" "$image_type" echo done } @@ -137,13 +148,13 @@ menu_import() { menu_list() { echo info "=== 已有镜像 ===" - lxc image list + $LXC image list } menu_delete() { echo info "=== 删除镜像 ===" - lxc image list + $LXC image list echo reading "输入要删除的镜像别名或指纹: " image_id if [ -z "$image_id" ]; then @@ -153,7 +164,7 @@ menu_delete() { warn "确认删除镜像 $image_id?" reading "确认?(y/n) [n]: " confirm if [[ "$confirm" =~ ^[yY]$ ]]; then - if lxc image delete "$image_id"; then + if $LXC image delete "$image_id"; then ok "镜像已删除" else err "删除失败" @@ -187,7 +198,5 @@ main_menu() { done } -check_root -check_lxd detect_arch main_menu diff --git a/Shell/lxd_install.sh b/Shell/lxd_install.sh new file mode 100644 index 0000000..4079c5b --- /dev/null +++ b/Shell/lxd_install.sh @@ -0,0 +1,327 @@ +#!/bin/bash + +cd /root >/dev/null 2>&1 + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +REGEX=("debian|astra" "ubuntu") +RELEASE=("Debian" "Ubuntu") +CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(lsb_release -sd 2>/dev/null)") +SYS="${CMD[0]}" +[[ -n $SYS ]] || exit 1 + +for ((int = 0; int < ${#REGEX[@]}; int++)); do + if [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]]; then + SYSTEM="${RELEASE[int]}" + [[ -n $SYSTEM ]] && break + fi +done + +if [[ "$SYSTEM" != "Debian" && "$SYSTEM" != "Ubuntu" ]]; then + echo -e "${RED}[ERR]${NC} 此脚本仅支持 Debian 和 Ubuntu 系统" + exit 1 +fi + +if [[ "$SYSTEM" == "Debian" ]]; then + OS_VERSION=$(cat /etc/debian_version | cut -d. -f1) +elif [[ "$SYSTEM" == "Ubuntu" ]]; then + OS_VERSION=$(grep VERSION_ID /etc/os-release | cut -d'"' -f2 | cut -d. -f1) +fi + +RECOMMENDED=false +if [[ "$SYSTEM" == "Debian" && ("$OS_VERSION" == "12" || "$OS_VERSION" == "13") ]]; then + RECOMMENDED=true +elif [[ "$SYSTEM" == "Ubuntu" && ("$OS_VERSION" == "24" || "$OS_VERSION" == "25") ]]; then + RECOMMENDED=true +fi + +if [[ "$RECOMMENDED" != "true" ]]; then + echo -e "${YELLOW}[WARN]${NC} 当前系统: $SYSTEM $OS_VERSION" + echo -e "${YELLOW}[WARN]${NC} 推荐使用: Debian 12/13 或 Ubuntu 24/25" + read -rp "$(echo -e "${YELLOW}是否继续安装?(y/n) [n]:${NC}")" confirm_install + confirm_install=${confirm_install:-n} + if [[ ! "$confirm_install" =~ ^[yY]$ ]]; then + echo -e "${RED}[ERR]${NC} 安装已取消" + exit 1 + fi +fi + +log() { echo -e "$1"; } +ok() { log "${GREEN}[OK]${NC} $1"; } +info() { log "${BLUE}[INFO]${NC} $1"; } +warn() { log "${YELLOW}[WARN]${NC} $1"; } +err() { log "${RED}[ERR]${NC} $1"; exit 1; } + +reading() { read -rp "$(echo -e "${GREEN}$1${NC}")" "$2"; } + +install_package() { + package_name=$1 + if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then + ok "$package_name 已安装" + else + apt-get install -y $package_name >/dev/null 2>&1 + if [ $? -ne 0 ]; then + apt-get install -y $package_name --fix-missing >/dev/null 2>&1 + fi + if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then + ok "$package_name 已安装" + else + warn "$package_name 安装失败" + fi + fi +} + +get_available_space() { + local available_space + available_space=$(df -BG / | awk 'NR==2 {gsub("G","",$4); print $4}') + echo "$available_space" +} + +install_lxd() { + lxd_snap=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snap) + lxd_snapd=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snapd) + if [[ "$lxd_snap" =~ ^snap.* ]] && [[ "$lxd_snapd" =~ ^snapd.* ]]; then + ok "snap 已安装" + else + info "开始安装 snap..." + apt-get update >/dev/null 2>&1 + install_package snapd + fi + snap_core=$(snap list core 2>/dev/null) + snap_lxd=$(snap list lxd 2>/dev/null) + if [[ "$snap_core" =~ core.* ]] && [[ "$snap_lxd" =~ lxd.* ]]; then + ok "LXD 已安装" + lxd_lxc_detect=$(lxc list 2>/dev/null) + if [[ "$lxd_lxc_detect" =~ "snap-update-ns failed with code1".* ]]; then + systemctl restart apparmor + snap restart lxd + else + ok "环境检测无问题" + fi + else + info "开始安装 LXD..." + snap install lxd --channel=latest/stable 2>/dev/null + if [[ $? -ne 0 ]]; then + snap remove lxd 2>/dev/null + snap install core 2>/dev/null + snap install lxd --channel=latest/stable 2>/dev/null + fi + snap alias lxd.lxc lxc 2>/dev/null + snap alias lxd.lxd lxd 2>/dev/null + if [ ! -f /etc/profile.d/snap.sh ]; then + echo 'export PATH=$PATH:/snap/bin' > /etc/profile.d/snap.sh + fi + export PATH=$PATH:/snap/bin + if ! command -v lxc >/dev/null 2>&1; then + err 'lxc 路径有问题,请检查 snap alias' + fi + ok "LXD 安装完成" + fi + + if dpkg -l lxcfs 2>/dev/null | grep -q "^ii"; then + warn "检测到 deb 版 lxcfs,正在移除..." + systemctl stop lxcfs 2>/dev/null || true + systemctl disable lxcfs 2>/dev/null || true + apt-get remove -y lxcfs >/dev/null 2>&1 + ok "deb 版 lxcfs 已移除" + fi + + lxd_version=$(lxd --version 2>/dev/null) + info "LXD 版本: $lxd_version" + if [[ ! "$lxd_version" =~ ^6\. ]]; then + warn "当前 LXD 版本 $lxd_version 不兼容,推荐使用 6.x 版本" + reading "是否继续?(y/n) [y]:" version_confirm + version_confirm=${version_confirm:-y} + if [[ ! "$version_confirm" =~ ^[yY]$ ]]; then + err "已取消安装" + fi + else + ok "LXD 版本兼容" + fi + + info "配置 LXD..." + snap set lxd lxcfs.flags="-l" 2>/dev/null + snap set lxd daemon.debug=false 2>/dev/null + snap restart lxd 2>/dev/null + sleep 3 + ok "LXD 已配置" +} + +init_lxd_network() { + if ! /snap/bin/lxc network show lxdbr0 &>/dev/null; then + info "创建默认网络 lxdbr0..." + /snap/bin/lxc network create lxdbr0 + ok "网络 lxdbr0 创建成功" + else + ok "网络 lxdbr0 已存在" + fi + + if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "eth0"; then + info "配置 default profile 网络设备..." + /snap/bin/lxc profile device add default eth0 nic network=lxdbr0 name=eth0 + ok "网络设备已添加到 default profile" + fi +} + +setup_storage() { + info "配置存储池..." + + if /snap/bin/lxc storage show default &>/dev/null; then + ok "存储池 default 已存在" + /snap/bin/lxc storage list + return 0 + fi + + available_space=$(get_available_space) + info "当前可用磁盘空间: ${available_space}GB" + + while true; do + reading "请选择存储后端 zfs/btrfs/lvm [zfs]:" storage_driver + storage_driver=${storage_driver:-zfs} + if [[ "$storage_driver" =~ ^(zfs|btrfs|lvm)$ ]]; then + break + else + warn "请输入 zfs、btrfs 或 lvm" + fi + done + + case "$storage_driver" in + zfs) + if ! command -v zpool &>/dev/null; then + info "安装 ZFS..." + if [[ "$SYSTEM" == "Ubuntu" ]]; then + install_package zfsutils-linux + else + bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.1.0-vpsm.link/Shell/debian_zfs.sh) + fi + fi + info "配置 LXD 使用系统 ZFS..." + snap set lxd zfs.external=true + snap restart lxd + sleep 3 + ;; + btrfs) + install_package btrfs-progs + ;; + lvm) + install_package lvm2 + ;; + esac + + reading "请输入存储池大小(GB) [${available_space}]:" pool_size + pool_size=${pool_size:-$available_space} + + info "创建 default 存储池 (${storage_driver}, ${pool_size}GB)..." + /snap/bin/lxc storage create default ${storage_driver} size=${pool_size}GB + + if [ $? -eq 0 ]; then + ok "存储池 default 创建成功" + if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "root"; then + /snap/bin/lxc profile device add default root disk path=/ pool=default + ok "存储池已添加到 default profile" + fi + else + err "存储池创建失败" + fi +} + +main() { + echo + echo "========================================" + echo " LXD 安装脚本" + echo " by Github-xkatld" + echo "========================================" + echo + + echo "======== 步骤 1/5: 检测系统 ========" + info "系统: $SYSTEM $OS_VERSION" + if [[ "$RECOMMENDED" == "true" ]]; then + ok "系统版本符合推荐" + else + warn "建议使用 Debian 12/13 或 Ubuntu 24/25" + fi + + if [[ "$SYSTEM" == "Debian" ]]; then + echo + warn "Debian 使用 ZFS 存储需要编译安装,耗时较长" + warn "如需使用 ZFS,推荐使用 Ubuntu 系统" + reading "是否继续使用 Debian?(y/n) [y]:" debian_confirm + debian_confirm=${debian_confirm:-y} + if [[ ! "$debian_confirm" =~ ^[yY]$ ]]; then + info "已取消安装" + exit 0 + fi + fi + echo + + echo "======== 步骤 2/5: 安装 LXD ========" + reading "是否安装 LXD?(y/n) [y]:" step2_confirm + step2_confirm=${step2_confirm:-y} + if [[ "$step2_confirm" =~ ^[yY]$ ]]; then + install_lxd + ok "LXD 安装完成" + else + info "已跳过 LXD 安装" + fi + echo + + echo "======== 步骤 3/5: 网络配置 ========" + reading "是否配置网络?(y/n) [y]:" step3_confirm + step3_confirm=${step3_confirm:-y} + if [[ "$step3_confirm" =~ ^[yY]$ ]]; then + init_lxd_network + reading "是否开启 IPv4 分配,分配NAT和独立IP需要开启 (y/n) [y]:" ipv4_dhcp + ipv4_dhcp=${ipv4_dhcp:-y} + if [[ ! "$ipv4_dhcp" =~ ^[yY]$ ]]; then + /snap/bin/lxc network set lxdbr0 ipv4.dhcp false + ok "IPv4 分配已关闭" + else + ok "IPv4 分配已开启" + fi + reading "是否开启 IPv6 分配,分配NAT和独立IP需要开启 (y/n) [y]:" ipv6_dhcp + ipv6_dhcp=${ipv6_dhcp:-y} + if [[ ! "$ipv6_dhcp" =~ ^[yY]$ ]]; then + /snap/bin/lxc network set lxdbr0 ipv6.dhcp false + /snap/bin/lxc network set lxdbr0 ipv6.address none + ok "IPv6 分配已关闭" + else + ok "IPv6 分配已开启" + fi + ok "网络配置完成" + else + info "已跳过网络配置" + fi + echo + + echo "======== 步骤 4/5: 存储配置 ========" + info "配置 default 存储池,首次安装推荐配置" + reading "是否配置存储池?(y/n) [y]:" step4_confirm + step4_confirm=${step4_confirm:-y} + if [[ "$step4_confirm" =~ ^[yY]$ ]]; then + setup_storage + ok "存储配置完成" + else + info "已跳过存储配置" + fi + echo + + echo "======== 步骤 5/5: 完成 ========" + echo + echo "========================================" + echo " LXD 安装完成" + echo "========================================" + echo + info "LXD 版本: $(lxd --version 2>/dev/null)" + echo + info "===== 网络配置 =====" + lxc network list 2>/dev/null || warn "无法获取网络列表" + echo + info "===== 存储配置 =====" + lxc storage list 2>/dev/null || warn "无法获取存储列表" +} + +main diff --git a/install.sh b/Shell/lxdapi_install.sh similarity index 50% rename from install.sh rename to Shell/lxdapi_install.sh index 28e429d..2ce8e75 100644 --- a/install.sh +++ b/Shell/lxdapi_install.sh @@ -26,106 +26,14 @@ if [[ "$SYSTEM" != "Debian" && "$SYSTEM" != "Ubuntu" ]]; then exit 1 fi -if [[ "$SYSTEM" == "Debian" ]]; then - OS_VERSION=$(cat /etc/debian_version | cut -d. -f1) -elif [[ "$SYSTEM" == "Ubuntu" ]]; then - OS_VERSION=$(grep VERSION_ID /etc/os-release | cut -d'"' -f2 | cut -d. -f1) -fi - -RECOMMENDED=false -if [[ "$SYSTEM" == "Debian" && ("$OS_VERSION" == "12" || "$OS_VERSION" == "13") ]]; then - RECOMMENDED=true -elif [[ "$SYSTEM" == "Ubuntu" && ("$OS_VERSION" == "24" || "$OS_VERSION" == "25") ]]; then - RECOMMENDED=true -fi - -if [[ "$RECOMMENDED" != "true" ]]; then - echo -e "${YELLOW}[WARN]${NC} 当前系统: $SYSTEM $OS_VERSION" - echo -e "${YELLOW}[WARN]${NC} 推荐使用: Debian 12/13 或 Ubuntu 24/25" - read -rp "$(echo -e "${YELLOW}是否继续安装?(y/n) [n]:${NC}")" confirm_install - confirm_install=${confirm_install:-n} - if [[ ! "$confirm_install" =~ ^[yY]$ ]]; then - echo -e "${RED}[ERR]${NC} 安装已取消" - exit 1 - fi -fi - - -if [ ! -d "/usr/local/bin" ]; then - mkdir -p /usr/local/bin -fi - log() { echo -e "$1"; } ok() { log "${GREEN}[OK]${NC} $1"; } info() { log "${BLUE}[INFO]${NC} $1"; } warn() { log "${YELLOW}[WARN]${NC} $1"; } err() { log "${RED}[ERR]${NC} $1"; exit 1; } -print_step() { - local step=$1 - local total=$2 - local title=$3 - echo - echo "========================================" - echo " 步骤 $step/$total: $title" - echo "========================================" - echo -} - reading() { read -rp "$(echo -e "${GREEN}$1${NC}")" "$2"; } -sed_compatible() { - if echo "test" | sed -E 's/test/ok/' >/dev/null 2>&1; then - sed -E "$@" - else - sed -r "$@" - fi -} - -service_manager() { - local action=$1 - local service_name=$2 - case "$action" in - enable) - systemctl enable "$service_name" 2>/dev/null - ;; - disable) - systemctl disable "$service_name" 2>/dev/null - ;; - start) - systemctl start "$service_name" 2>/dev/null - ;; - stop) - systemctl stop "$service_name" 2>/dev/null - ;; - restart) - systemctl restart "$service_name" 2>/dev/null - ;; - daemon-reload) - systemctl daemon-reload 2>/dev/null - ;; - is-active) - systemctl is-active --quiet "$service_name" 2>/dev/null - return $? - ;; - esac - return 0 -} - - -set_locale() { - utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "utf8|UTF-8") - export DEBIAN_FRONTEND=noninteractive - if [[ -z "$utf8_locale" ]]; then - warn "未找到 UTF-8 语言环境" - else - export LC_ALL="$utf8_locale" - export LANG="$utf8_locale" - export LANGUAGE="$utf8_locale" - ok "语言环境设置为 $utf8_locale" - fi -} - install_package() { package_name=$1 if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then @@ -143,174 +51,28 @@ install_package() { fi } - - - - -get_available_space() { - local available_space - available_space=$(df -BG / | awk 'NR==2 {gsub("G","",$4); print $4}') - echo "$available_space" -} - install_base_packages() { info "更新软件包列表..." apt-get update >/dev/null 2>&1 apt-get autoremove -y >/dev/null 2>&1 - install_package wget - install_package curl - install_package sudo - install_package unzip - install_package iptables-persistent - install_package nftables - install_package nginx - if dpkg -l lxcfs 2>/dev/null | grep -q "^ii"; then - warn "检测到 deb 版 lxcfs,正在移除以避免与 snap 版 LXD 冲突..." - systemctl stop lxcfs 2>/dev/null || true - systemctl disable lxcfs 2>/dev/null || true - apt-get remove -y lxcfs >/dev/null 2>&1 - ok "deb 版 lxcfs 已移除,将使用 snap 版 LXD 内置的 lxcfs" + info "安装基础软件包..." + DEBIAN_FRONTEND=noninteractive apt-get install -y unzip e2fsprogs bc nftables fdisk parted iptables-persistent nginx >/dev/null 2>&1 + ok "软件包安装完成" + + systemctl enable nftables >/dev/null 2>&1 + systemctl start nftables >/dev/null 2>&1 + ok "nftables 已启动" + + if command -v lxc &>/dev/null && lxc network show lxdbr0 &>/dev/null; then + lxc network set lxdbr0 ipv4.nat true 2>/dev/null + lxc network set lxdbr0 ipv6.nat true 2>/dev/null + ok "LXD NAT 规则已重建" fi - if systemctl is-active --quiet nginx; then - ok "nginx 服务已运行" - else - service_manager start nginx - service_manager enable nginx - ok "nginx 服务已启动并设置为自动启动" - fi -} - -install_lxd() { - lxd_snap=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snap) - lxd_snapd=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snapd) - if [[ "$lxd_snap" =~ ^snap.* ]] && [[ "$lxd_snapd" =~ ^snapd.* ]]; then - ok "snap 已安装" - else - info "开始安装 snap..." - apt-get update >/dev/null 2>&1 - install_package snapd - fi - snap_core=$(snap list core 2>/dev/null) - snap_lxd=$(snap list lxd 2>/dev/null) - if [[ "$snap_core" =~ core.* ]] && [[ "$snap_lxd" =~ lxd.* ]]; then - ok "LXD 已安装" - lxd_lxc_detect=$(lxc list 2>/dev/null) - if [[ "$lxd_lxc_detect" =~ "snap-update-ns failed with code1".* ]]; then - service_manager restart apparmor - snap restart lxd - else - ok "环境检测无问题" - fi - else - info "开始安装 LXD..." - snap install lxd --channel=latest/stable 2>/dev/null - if [[ $? -ne 0 ]]; then - snap remove lxd 2>/dev/null - snap install core 2>/dev/null - snap install lxd --channel=latest/stable 2>/dev/null - fi - snap alias lxd.lxc lxc 2>/dev/null - snap alias lxd.lxd lxd 2>/dev/null - if [ ! -f /etc/profile.d/snap.sh ]; then - echo 'export PATH=$PATH:/snap/bin' > /etc/profile.d/snap.sh - fi - export PATH=$PATH:/snap/bin - if ! command -v lxc >/dev/null 2>&1; then - err 'lxc 路径有问题,请检查 snap alias' - fi - ok "LXD 安装完成" - fi - - info "配置 LXD..." - snap set lxd lxcfs.flags="-l" 2>/dev/null - snap set lxd daemon.debug=false 2>/dev/null - snap restart lxd 2>/dev/null - sleep 3 - ok "LXD 已配置(lxcfs legacy 模式 + 关闭调试)" -} - -setup_storage() { - info "配置存储池..." - - if /snap/bin/lxc storage show default &>/dev/null; then - ok "存储池 default 已存在" - /snap/bin/lxc storage list - return 0 - fi - - available_space=$(get_available_space) - info "当前可用磁盘空间: ${available_space}GB" - - while true; do - reading "请选择存储后端 zfs/btrfs/lvm [zfs]:" storage_driver - storage_driver=${storage_driver:-zfs} - if [[ "$storage_driver" =~ ^(zfs|btrfs|lvm)$ ]]; then - break - else - warn "请输入 zfs、btrfs 或 lvm" - fi - done - - case "$storage_driver" in - zfs) - if ! command -v zpool &>/dev/null; then - info "安装 ZFS..." - if [[ "$SYSTEM" == "Ubuntu" ]]; then - install_package zfsutils-linux - else - bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.0.0-main/build_zfs_on_debian.sh) - fi - fi - info "配置 LXD 使用系统 ZFS..." - snap set lxd zfs.external=true - snap restart lxd - sleep 3 - ;; - btrfs) - install_package btrfs-progs - ;; - lvm) - install_package lvm2 - ;; - esac - - reading "请输入存储池大小(GB) [${available_space}]:" pool_size - pool_size=${pool_size:-$available_space} - - info "创建 default 存储池 (${storage_driver}, ${pool_size}GB)..." - /snap/bin/lxc storage create default ${storage_driver} size=${pool_size}GB - - if [ $? -eq 0 ]; then - ok "存储池 default 创建成功" - if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "root"; then - /snap/bin/lxc profile device add default root disk path=/ pool=default - ok "存储池已添加到 default profile" - fi - else - err "存储池创建失败" - fi -} - -init_lxd_network() { - if ! /snap/bin/lxc network show lxdbr0 &>/dev/null; then - info "创建默认网络 lxdbr0..." - /snap/bin/lxc network create lxdbr0 - ok "网络 lxdbr0 创建成功" - else - ok "网络 lxdbr0 已存在" - fi - - if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "eth0"; then - info "配置 default profile 网络设备..." - /snap/bin/lxc profile device add default eth0 nic network=lxdbr0 name=eth0 - ok "网络设备已添加到 default profile" - fi -} - -import_container_images() { - bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.0.0-main/image_import.sh) + systemctl enable nginx >/dev/null 2>&1 + systemctl start nginx >/dev/null 2>&1 + ok "nginx 已启动" } deploy_lxdapi() { @@ -340,8 +102,6 @@ deploy_lxdapi() { fi done - info "获取最新版本..." - if [[ "$download_source" == "github" ]]; then latest_tag=$(curl -s https://api.github.com/repos/xkatld/lxdapi-web-server/releases/latest | grep '"tag_name"' | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') base_url="https://github.com/xkatld/lxdapi-web-server/releases/download" @@ -354,9 +114,12 @@ deploy_lxdapi() { err "无法获取最新版本信息" fi - ok "最新版本: $latest_tag" + info "最新版本: $latest_tag" + reading "请输入安装版本 [$latest_tag]:" install_version + install_version=${install_version:-$latest_tag} + ok "安装版本: $install_version" - download_url="${base_url}/${latest_tag}/lxdapi-linux-${arch}.tar.gz" + download_url="${base_url}/${install_version}/lxdapi-linux-${arch}.tar.gz" info "下载 lxdapi..." info "下载地址: $download_url" @@ -393,11 +156,72 @@ configure_lxdapi() { ok "API密钥已生成: $api_hash" fi - reading "请输入流量采集间隔秒数 [30]:" traffic_interval - traffic_interval=${traffic_interval:-30} + reading "请输入管理员用户名 [admin]:" admin_user + admin_user=${admin_user:-admin} - reading "请输入流量批量更新数量 [5]:" traffic_batch_size - traffic_batch_size=${traffic_batch_size:-5} + reading "请输入管理员密码 [随机生成]:" admin_pass + if [ -z "$admin_pass" ]; then + admin_pass=$(openssl rand -hex 8) + ok "管理员密码已生成: $admin_pass" + fi + + session_secret=$(openssl rand -hex 16) + + reading "请输入流量采集间隔秒数 [20]:" traffic_interval + traffic_interval=${traffic_interval:-20} + + reading "请输入流量批量更新数量 [10]:" traffic_batch_size + traffic_batch_size=${traffic_batch_size:-10} + + reading "请输入任务自动清理天数 [7]:" auto_cleanup_days + auto_cleanup_days=${auto_cleanup_days:-7} + + while true; do + reading "请选择任务队列后端 memory/redis [memory]:" task_backend + task_backend=${task_backend:-memory} + if [[ "$task_backend" =~ ^(memory|redis)$ ]]; then + break + else + warn "请输入 memory 或 redis" + fi + done + + if [[ "$task_backend" == "redis" ]]; then + while true; do + reading "使用本地安装还是远程配置?local/remote [local]:" redis_location + redis_location=${redis_location:-local} + if [[ "$redis_location" =~ ^(local|remote)$ ]]; then + break + else + warn "请输入 local 或 remote" + fi + done + + if [[ "$redis_location" == "local" ]]; then + info "安装 Redis..." + apt-get install -y redis-server >/dev/null 2>&1 + systemctl start redis-server + systemctl enable redis-server + + redis_host="localhost" + redis_port="6379" + redis_password="" + redis_db="0" + ok "Redis 已安装" + else + reading "请输入 Redis 主机地址:" redis_host + reading "请输入 Redis 端口 [6379]:" redis_port + redis_port=${redis_port:-6379} + reading "请输入 Redis 密码 [留空表示无密码]:" redis_password + reading "请输入 Redis 数据库编号 [0]:" redis_db + redis_db=${redis_db:-0} + fi + else + redis_host="localhost" + redis_port="6379" + redis_password="" + redis_db="0" + fi while true; do reading "请选择数据库类型 sqlite/mysql/postgres [sqlite]:" db_type @@ -422,9 +246,9 @@ configure_lxdapi() { if [[ "$mysql_location" == "local" ]]; then info "安装 MariaDB..." - install_package mariadb-server - service_manager start mariadb - service_manager enable mariadb + apt-get install -y mariadb-server >/dev/null 2>&1 + systemctl start mariadb + systemctl enable mariadb mysql_host="localhost" mysql_port="3306" @@ -451,12 +275,6 @@ EOF reading "请输入 MySQL 数据库名:" mysql_database fi - sed -i "s|__MYSQL_HOST__|$mysql_host|g" "$config_file" - sed -i "s|__MYSQL_PORT__|$mysql_port|g" "$config_file" - sed -i "s|__MYSQL_USER__|$mysql_user|g" "$config_file" - sed -i "s|__MYSQL_PASSWORD__|$mysql_password|g" "$config_file" - sed -i "s|__MYSQL_DATABASE__|$mysql_database|g" "$config_file" - elif [[ "$db_type" == "postgres" ]]; then while true; do reading "使用本地安装还是远程配置?local/remote [local]:" postgres_location @@ -470,9 +288,9 @@ EOF if [[ "$postgres_location" == "local" ]]; then info "安装 PostgreSQL..." - install_package postgresql - service_manager start postgresql - service_manager enable postgresql + apt-get install -y postgresql >/dev/null 2>&1 + systemctl start postgresql + systemctl enable postgresql postgres_host="localhost" postgres_port="5432" @@ -500,88 +318,54 @@ EOF reading "请输入 PostgreSQL SSL模式 [disable]:" postgres_sslmode postgres_sslmode=${postgres_sslmode:-disable} fi - + fi + + info "写入配置文件..." + sed -i "s|__SERVER_PORT__|$server_port|g" "$config_file" + sed -i "s|__API_HASH__|$api_hash|g" "$config_file" + sed -i "s|__ADMIN_USER__|$admin_user|g" "$config_file" + sed -i "s|__ADMIN_PASS__|$admin_pass|g" "$config_file" + sed -i "s|__SESSION_SECRET__|$session_secret|g" "$config_file" + sed -i "s|__TRAFFIC_INTERVAL__|$traffic_interval|g" "$config_file" + sed -i "s|__TRAFFIC_BATCH_SIZE__|$traffic_batch_size|g" "$config_file" + sed -i "s|__AUTO_CLEANUP_DAYS__|$auto_cleanup_days|g" "$config_file" + sed -i "s|__TASK_BACKEND__|$task_backend|g" "$config_file" + sed -i "s|__REDIS_HOST__|$redis_host|g" "$config_file" + sed -i "s|__REDIS_PORT__|$redis_port|g" "$config_file" + sed -i "s|__REDIS_PASSWORD__|$redis_password|g" "$config_file" + sed -i "s|__REDIS_DB__|$redis_db|g" "$config_file" + sed -i "s|__DB_TYPE__|$db_type|g" "$config_file" + + if [[ "$db_type" == "mysql" ]]; then + sed -i "s|__MYSQL_HOST__|$mysql_host|g" "$config_file" + sed -i "s|__MYSQL_PORT__|$mysql_port|g" "$config_file" + sed -i "s|__MYSQL_USER__|$mysql_user|g" "$config_file" + sed -i "s|__MYSQL_PASSWORD__|$mysql_password|g" "$config_file" + sed -i "s|__MYSQL_DATABASE__|$mysql_database|g" "$config_file" + else + sed -i "s|__MYSQL_HOST__|localhost|g" "$config_file" + sed -i "s|__MYSQL_PORT__|3306|g" "$config_file" + sed -i "s|__MYSQL_USER__|root|g" "$config_file" + sed -i "s|__MYSQL_PASSWORD__||g" "$config_file" + sed -i "s|__MYSQL_DATABASE__|lxdapi|g" "$config_file" + fi + + if [[ "$db_type" == "postgres" ]]; then sed -i "s|__POSTGRES_HOST__|$postgres_host|g" "$config_file" sed -i "s|__POSTGRES_PORT__|$postgres_port|g" "$config_file" sed -i "s|__POSTGRES_USER__|$postgres_user|g" "$config_file" sed -i "s|__POSTGRES_PASSWORD__|$postgres_password|g" "$config_file" sed -i "s|__POSTGRES_DATABASE__|$postgres_database|g" "$config_file" sed -i "s|__POSTGRES_SSLMODE__|$postgres_sslmode|g" "$config_file" + else + sed -i "s|__POSTGRES_HOST__|localhost|g" "$config_file" + sed -i "s|__POSTGRES_PORT__|5432|g" "$config_file" + sed -i "s|__POSTGRES_USER__|postgres|g" "$config_file" + sed -i "s|__POSTGRES_PASSWORD__||g" "$config_file" + sed -i "s|__POSTGRES_DATABASE__|lxdapi|g" "$config_file" + sed -i "s|__POSTGRES_SSLMODE__|disable|g" "$config_file" fi - while true; do - reading "请选择任务队列后端 memory/redis [memory]:" task_backend - task_backend=${task_backend:-memory} - if [[ "$task_backend" =~ ^(memory|redis)$ ]]; then - break - else - warn "请输入 memory 或 redis" - fi - done - - if [[ "$task_backend" == "redis" ]]; then - info "安装 Redis..." - install_package redis-server - service_manager start redis-server - service_manager enable redis-server - - redis_host="localhost" - redis_port="6379" - redis_password="" - redis_db="0" - - ok "Redis 已安装并启动" - - sed -i "s|__REDIS_HOST__|$redis_host|g" "$config_file" - sed -i "s|__REDIS_PORT__|$redis_port|g" "$config_file" - sed -i "s|__REDIS_PASSWORD__|$redis_password|g" "$config_file" - sed -i "s|__REDIS_DB__|$redis_db|g" "$config_file" - fi - - reading "请输入管理员用户名 [admin]:" admin_user - admin_user=${admin_user:-admin} - - reading "请输入管理员密码 [随机生成]:" admin_pass - if [ -z "$admin_pass" ]; then - admin_pass=$(openssl rand -hex 4) - ok "管理员密码已生成: $admin_pass" - fi - - reading "请输入Session密钥 [随机生成]:" session_secret - if [ -z "$session_secret" ]; then - session_secret=$(openssl rand -hex 16) - ok "Session密钥已生成: $session_secret" - fi - - info "写入配置文件..." - sed -i "s|__SERVER_PORT__|$server_port|g" "$config_file" - sed -i "s|__API_HASH__|$api_hash|g" "$config_file" - sed -i "s|__TRAFFIC_INTERVAL__|$traffic_interval|g" "$config_file" - sed -i "s|__TRAFFIC_BATCH_SIZE__|$traffic_batch_size|g" "$config_file" - sed -i "s|__DB_TYPE__|$db_type|g" "$config_file" - sed -i "s|__TASK_BACKEND__|$task_backend|g" "$config_file" - sed -i "s|__ADMIN_USER__|$admin_user|g" "$config_file" - sed -i "s|__ADMIN_PASS__|$admin_pass|g" "$config_file" - sed -i "s|__SESSION_SECRET__|$session_secret|g" "$config_file" - - sed -i "s|__MYSQL_HOST__|localhost|g" "$config_file" - sed -i "s|__MYSQL_PORT__|3306|g" "$config_file" - sed -i "s|__MYSQL_USER__|lxdapi|g" "$config_file" - sed -i "s|__MYSQL_PASSWORD__|password|g" "$config_file" - sed -i "s|__MYSQL_DATABASE__|lxdapi|g" "$config_file" - - sed -i "s|__POSTGRES_HOST__|localhost|g" "$config_file" - sed -i "s|__POSTGRES_PORT__|5432|g" "$config_file" - sed -i "s|__POSTGRES_USER__|lxdapi|g" "$config_file" - sed -i "s|__POSTGRES_PASSWORD__|password|g" "$config_file" - sed -i "s|__POSTGRES_DATABASE__|lxdapi|g" "$config_file" - sed -i "s|__POSTGRES_SSLMODE__|disable|g" "$config_file" - - sed -i "s|__REDIS_HOST__|localhost|g" "$config_file" - sed -i "s|__REDIS_PORT__|6379|g" "$config_file" - sed -i "s|__REDIS_PASSWORD__||g" "$config_file" - sed -i "s|__REDIS_DB__|0|g" "$config_file" - ok "配置文件已更新" } @@ -635,26 +419,21 @@ EOF ok "服务文件已创建: $service_file" - info "重载 systemd 配置..." systemctl daemon-reload - - info "启用开机自启..." systemctl enable lxdapi - - info "启动 lxdapi 服务..." systemctl start lxdapi - sleep 2 + info "等待服务启动..." + for i in {1..10}; do + printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i" + sleep 1 + done + echo if systemctl is-active --quiet lxdapi; then ok "lxdapi 服务已启动" - echo - info "===== 服务状态 =====" - systemctl status lxdapi --no-pager | head -10 else warn "lxdapi 服务启动失败" - echo - info "===== 错误日志 =====" journalctl -u lxdapi -n 20 --no-pager fi } @@ -666,94 +445,66 @@ main() { echo " by Github-xkatld" echo "========================================" echo - print_step "1" "4" "初始化环境" - reading "是否执行环境初始化?(y/n) [y]:" step1_confirm + + echo "======== 步骤 1/5: 基础软件包安装 ========" + reading "是否安装基础软件包?(y/n) [y]:" step1_confirm step1_confirm=${step1_confirm:-y} if [[ "$step1_confirm" =~ ^[yY]$ ]]; then - set_locale install_base_packages - ok "环境初始化完成" + ok "基础软件包安装完成" else - info "已跳过环境初始化" + info "已跳过基础软件包安装" fi + echo - print_step "2" "4" "安装 LXD" - reading "是否执行 LXD 安装?(y/n) [y]:" step2_confirm + echo "======== 步骤 2/5: 下载 ========" + reading "是否下载 lxdapi?(y/n) [y]:" step2_confirm step2_confirm=${step2_confirm:-y} if [[ "$step2_confirm" =~ ^[yY]$ ]]; then - install_lxd - init_lxd_network - ok "LXD 安装完成" + deploy_lxdapi + ok "下载完成" else - info "已跳过 LXD 安装" + info "已跳过下载" fi + echo - print_step "3" "4" "配置存储资源" - reading "是否执行存储配置?(y/n) [y]:" step3_confirm + echo "======== 步骤 3/5: 配置 ========" + reading "是否配置 lxdapi?(y/n) [y]:" step3_confirm step3_confirm=${step3_confirm:-y} if [[ "$step3_confirm" =~ ^[yY]$ ]]; then - setup_storage - ok "存储配置完成" - else - info "已跳过存储配置" - fi - - print_step "4" "4" "部署 lxdapi" - reading "是否执行 lxdapi 部署?(y/n) [y]:" step5_confirm - step5_confirm=${step5_confirm:-y} - if [[ "$step5_confirm" =~ ^[yY]$ ]]; then - deploy_lxdapi configure_lxdapi - setup_lxdapi_service - ok "lxdapi 部署完成" + ok "配置完成" else - info "已跳过 lxdapi 部署" + info "已跳过配置" fi + echo + echo "======== 步骤 4/5: 启动服务 ========" + reading "是否启动 lxdapi 服务?(y/n) [y]:" step4_confirm + step4_confirm=${step4_confirm:-y} + if [[ "$step4_confirm" =~ ^[yY]$ ]]; then + setup_lxdapi_service + ok "服务已启动" + else + info "已跳过服务启动" + fi + echo + + echo "======== 步骤 5/5: 完成 ========" echo echo "========================================" - echo " lxdapi 安装完成" + echo " LXDAPI 安装完成" echo "========================================" echo - - info "LXD 版本: $(lxd --version)" - info "LXC 版本: $(lxc --version)" - echo - - info "===== 1. 网络配置 =====" - lxc network list - echo - - info "===== 2. 存储配置 =====" - lxc storage list - echo - - info "===== 3. 后端配置 =====" info "服务端口: $server_port" info "API密钥: $api_hash" - info "流量间隔: $traffic_interval 秒" - info "批量大小: $traffic_batch_size" - info "数据库: $db_type" - if [[ "$db_type" == "mysql" ]]; then - info "MySQL: $mysql_user@$mysql_host:$mysql_port/$mysql_database" - info "MySQL密码: $mysql_password" - elif [[ "$db_type" == "postgres" ]]; then - info "PostgreSQL: $postgres_user@$postgres_host:$postgres_port/$postgres_database" - info "PostgreSQL密码: $postgres_password" - fi - info "任务队列: $task_backend" - if [[ "$task_backend" == "redis" ]]; then - info "Redis: localhost:6379" - fi - info "管理员: $admin_user" + info "管理员用户: $admin_user" info "管理员密码: $admin_pass" - info "Session密钥: $session_secret" + info "数据库类型: $db_type" + info "任务队列: $task_backend" + info "流量采集间隔: ${traffic_interval}s" echo - - info "===== 5. lxdapi 服务状态 =====" - info "等待服务启动..." - sleep 5 - systemctl status lxdapi --no-pager -l + systemctl status lxdapi --no-pager | head -5 } main diff --git a/Shell/lxdapi_tool.sh b/Shell/lxdapi_tool.sh new file mode 100644 index 0000000..1e74ddc --- /dev/null +++ b/Shell/lxdapi_tool.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +INSTALL_DIR="/opt/lxdapi" +SERVICE_NAME="lxdapi" +SCRIPT_PATH="/usr/local/bin/lxdapi" + +GREEN='\033[0;32m' +NC='\033[0m' + +if [ "$(realpath "$0")" != "$SCRIPT_PATH" ]; then + cp "$0" "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" + echo "lxdapi 命令已安装" + echo "用法: lxdapi 或 lxdapi {start|stop|restart|status|config|machine-id}" + exit 0 +fi + +wait_progress() { + echo "等待服务响应..." + for i in {1..10}; do + printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i" + sleep 1 + done + echo +} + +do_start() { + systemctl start $SERVICE_NAME + echo "lxdapi 已启动" + wait_progress + systemctl status $SERVICE_NAME --no-pager | head -5 +} + +do_stop() { + systemctl stop $SERVICE_NAME + echo "lxdapi 已停止" + sleep 1 + systemctl status $SERVICE_NAME --no-pager | head -5 +} + +do_restart() { + systemctl restart $SERVICE_NAME + echo "lxdapi 已重启" + wait_progress + systemctl status $SERVICE_NAME --no-pager | head -5 +} + +do_status() { + systemctl status $SERVICE_NAME --no-pager | head -5 + echo + echo "===== 最近日志 =====" + journalctl -u $SERVICE_NAME -n 10 --no-pager +} + +do_config() { + CONFIG_FILE="$INSTALL_DIR/configs/config.yaml" + PORT=$(grep "port:" "$CONFIG_FILE" | head -1 | awk '{print $2}') + API_KEY=$(grep "api_key:" "$CONFIG_FILE" | awk -F'"' '{print $2}') + echo "端口: $PORT" + echo "API密钥: $API_KEY" +} + +do_machine_id() { + ARCH=$(uname -m) + case $ARCH in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + esac + $INSTALL_DIR/lxdapi-$ARCH --machine-id +} + +show_menu() { + echo + echo "================================" + echo " LXDAPI 管理工具" + echo "================================" + echo "1. 启动" + echo "2. 停止" + echo "3. 重启" + echo "4. 状态" + echo "5. 配置" + echo "6. 机器码" + echo "0. 退出" + echo "================================" + read -rp "$(echo -e "${GREEN}请选择 [0-6]: ${NC}")" choice + + case "$choice" in + 1) do_start ;; + 2) do_stop ;; + 3) do_restart ;; + 4) do_status ;; + 5) do_config ;; + 6) do_machine_id ;; + 0) exit 0 ;; + *) echo "无效选择" ;; + esac + show_menu +} + +case "$1" in + start) do_start ;; + stop) do_stop ;; + restart) do_restart ;; + status) do_status ;; + config) do_config ;; + machine-id) do_machine_id ;; + "") show_menu ;; + *) + echo "用法: lxdapi 或 lxdapi {start|stop|restart|status|config|machine-id}" + exit 1 + ;; +esac diff --git a/update.sh b/Shell/lxdapi_update.sh similarity index 76% rename from update.sh rename to Shell/lxdapi_update.sh index bc51d91..e30a3e7 100644 --- a/update.sh +++ b/Shell/lxdapi_update.sh @@ -20,25 +20,10 @@ SERVICE_NAME="lxdapi" check_environment() { info "检测运行环境..." - if [ "$EUID" -ne 0 ]; then - err "请使用 root 用户运行此脚本" - fi - if [ ! -d "$INSTALL_DIR" ]; then err "未检测到 lxdapi 安装目录: $INSTALL_DIR" fi - if [ ! -f "$CONFIG_FILE" ]; then - err "未检测到配置文件: $CONFIG_FILE" - fi - - if ! command -v nft &>/dev/null; then - info "安装 nftables..." - apt-get update >/dev/null 2>&1 - apt-get install -y nftables >/dev/null 2>&1 - ok "nftables 已安装" - fi - ok "环境检测通过" } @@ -62,9 +47,9 @@ detect_arch() { get_current_version() { if [ -f "$INSTALL_DIR/lxdapi-$ARCH" ]; then - CURRENT_VERSION=$("$INSTALL_DIR/lxdapi-$ARCH" --version 2>/dev/null || echo "未知") + CURRENT_VERSION=$(stat -c %y "$INSTALL_DIR/lxdapi-$ARCH" 2>/dev/null | cut -d' ' -f1) else - CURRENT_VERSION="未知" + CURRENT_VERSION="未安装" fi info "当前版本: $CURRENT_VERSION" } @@ -77,7 +62,10 @@ get_latest_version() { err "无法获取最新版本信息,请检查网络连接" fi - ok "最新版本: $LATEST_VERSION" + info "最新版本: $LATEST_VERSION" + read -rp "$(echo -e "${GREEN}请输入更新版本 [$LATEST_VERSION]: ${NC}")" UPDATE_VERSION + UPDATE_VERSION=${UPDATE_VERSION:-$LATEST_VERSION} + ok "更新版本: $UPDATE_VERSION" } stop_service() { @@ -121,53 +109,10 @@ backup_files() { fi } -fix_service_file() { - info "检查服务文件..." - - SERVICE_FILE="/etc/systemd/system/lxdapi.service" - - if [ ! -f "$SERVICE_FILE" ]; then - warn "服务文件不存在,跳过修复" - return - fi - - if grep -q "Environment=\"PATH=" "$SERVICE_FILE"; then - ok "服务文件PATH配置正常" - return - fi - - info "修复服务文件PATH配置..." - - EXEC_BIN="$INSTALL_DIR/lxdapi-$ARCH" - - cat > "$SERVICE_FILE" << EOF -[Unit] -Description=LXD API Server -After=network.target lxd.service -Wants=lxd.service - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/lxdapi -Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" -ExecStart=$EXEC_BIN -Restart=always -RestartSec=5 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - - ok "服务文件已修复" -} - download_latest() { - info "下载最新版本..." + info "下载版本 $UPDATE_VERSION..." - DOWNLOAD_URL="https://github.com/xkatld/lxdapi-web-server/releases/download/${LATEST_VERSION}/lxdapi-linux-${ARCH}.tar.gz" + DOWNLOAD_URL="https://github.com/xkatld/lxdapi-web-server/releases/download/${UPDATE_VERSION}/lxdapi-linux-${ARCH}.tar.gz" info "下载地址: $DOWNLOAD_URL" TEMP_FILE=$(mktemp) @@ -208,7 +153,12 @@ start_service() { systemctl daemon-reload systemctl start $SERVICE_NAME - sleep 3 + info "等待服务启动..." + for i in {1..10}; do + printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i" + sleep 1 + done + echo if systemctl is-active --quiet $SERVICE_NAME; then ok "服务已启动" @@ -226,7 +176,7 @@ show_result() { echo "========================================" echo info "更新前版本: $CURRENT_VERSION" - info "更新后版本: $LATEST_VERSION" + info "更新后版本: $UPDATE_VERSION" info "备份目录: $BACKUP_PATH" echo info "===== 服务状态 =====" @@ -275,7 +225,6 @@ main() { echo stop_service backup_files - fix_service_file if download_latest; then start_service diff --git a/storage_pool.sh b/Shell/storage_pool.sh similarity index 98% rename from storage_pool.sh rename to Shell/storage_pool.sh index 7ccf048..271c73e 100644 --- a/storage_pool.sh +++ b/Shell/storage_pool.sh @@ -47,7 +47,7 @@ install_zfs() { return 1 fi info "开始编译安装 ZFS..." - bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.0.0-main/build_zfs_on_debian.sh) || return 1 + bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.1.0-vpsm.link/Shell/debian_zfs.sh) || return 1 else info "安装 ZFS..." apt-get update -qq && apt-get install -y zfsutils-linux -qq @@ -329,4 +329,5 @@ main_menu() { check_root check_lxd +install_package bc main_menu diff --git a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig b/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig deleted file mode 100644 index 5fe320e..0000000 --- a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_activated.html.twig +++ /dev/null @@ -1,68 +0,0 @@ -{# ========== 汉化邮箱插件 by xkatld 开始 ========== #} -{# @link https://github.com/xkatld/FOSSBilling-Patch #} -{# @version v1.0.0 #} - -{% block subject %}[{{ guest.system_company.name }}] {{ order.title }} 已激活{% endblock %} -{% block content %} - - - - - - - - - - - -{% endblock %} - -{# ========== 汉化邮箱插件 by xkatld 结束 ========== #} diff --git a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig b/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig deleted file mode 100644 index 743ac0c..0000000 --- a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_canceled.html.twig +++ /dev/null @@ -1,68 +0,0 @@ -{# ========== 汉化邮箱插件 by xkatld 开始 ========== #} -{# @link https://github.com/xkatld/FOSSBilling-Patch #} -{# @version v1.0.0 #} - -{% block subject %}[{{ guest.system_company.name }}] {{ order.title }} 已取消{% endblock %} -{% block content %} - - - - - - - - - - - -{% endblock %} - -{# ========== 汉化邮箱插件 by xkatld 结束 ========== #} diff --git a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig b/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig deleted file mode 100644 index ef3e79c..0000000 --- a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_suspended.html.twig +++ /dev/null @@ -1,68 +0,0 @@ -{# ========== 汉化邮箱插件 by xkatld 开始 ========== #} -{# @link https://github.com/xkatld/FOSSBilling-Patch #} -{# @version v1.0.0 #} - -{% block subject %}[{{ guest.system_company.name }}] {{ order.title }} 已暂停{% endblock %} -{% block content %} - - - - - - - - - - - -{% endblock %} - -{# ========== 汉化邮箱插件 by xkatld 结束 ========== #} diff --git a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig b/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig deleted file mode 100644 index d5c10f1..0000000 --- a/fossbilling/Servicelxdapi/html_email/mod_servicelxdapi_unsuspended.html.twig +++ /dev/null @@ -1,68 +0,0 @@ -{# ========== 汉化邮箱插件 by xkatld 开始 ========== #} -{# @link https://github.com/xkatld/FOSSBilling-Patch #} -{# @version v1.0.0 #} - -{% block subject %}[{{ guest.system_company.name }}] {{ order.title }} 已恢复{% endblock %} -{% block content %} - - - - - - - - - - - -{% endblock %} - -{# ========== 汉化邮箱插件 by xkatld 结束 ========== #}