-
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}
{$js_code}{$error_html}
{$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 %}
-
-
-
-
-
-
-
-
-
-
-
-
容器激活成功
-
您好 {{ c.first_name }} {{ c.last_name }},
-
-
-
恭喜!您的LXD容器已成功激活,可以开始使用了。
-
-
-
- - 容器名称: {{ service.container_name }}
- - 服务名称: {{ order.title }}
- - 激活时间: {{ order.activated_at|format_date }}
- {% if order.expires_at %}- 到期时间: {{ order.expires_at|format_date }}
{% endif %}
-
-
-
- 管理容器
-
-
-
-
-
-
-{% 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 %}
-
-
-
-
-
-
-
-
-
-
-
-
服务已取消
-
您好 {{ c.first_name }} {{ c.last_name }},
-
-
-
您的LXD容器服务已被取消。容器数据已被删除。
-
-
-
- - 容器名称: {{ service.container_name }}
- - 服务名称: {{ order.title }}
-
-
-
如有任何疑问,请联系我们的客服团队。感谢您的使用!
-
-
- 登录客户中心
-
-
-
-
-
-
-{% 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 %}
-
-
-
-
-
-
-
-
-
-
-
-
服务已暂停
-
您好 {{ c.first_name }} {{ c.last_name }},
-
-
-
注意:您的LXD容器已被暂停。这通常是由于账单逾期未支付导致的。
-
-
-
- - 容器名称: {{ service.container_name }}
- - 服务名称: {{ order.title }}
-
-
-
请尽快登录客户中心处理未付账单,以恢复您的容器服务。
-
-
- 登录客户中心
-
-
-
-
-
-
-{% 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 %}
-
-
-
-
-
-
-
-
-
-
-
-
服务已恢复
-
您好 {{ c.first_name }} {{ c.last_name }},
-
-
-
-
- - 容器名称: {{ service.container_name }}
- - 服务名称: {{ order.title }}
-
-
-
您现在可以继续使用容器服务了。
-
-
- 管理容器
-
-
-
-
-
-
-{% endblock %}
-
-{# ========== 汉化邮箱插件 by xkatld 结束 ========== #}