Repository: 1Panel-dev/1Panel
Pull request #8262: fix: Optimize script library scripts and execution methods
Author: login: ssongliu
Labels: name: do-not-merge/release-note-label-needed
name: approved
name: dev-v2
Created: 2025-03-26T09:41:40Z | Merged: 2025-03-26T10:07:35Z



Diff:
diff --git a/core/app/api/v2/script_library.go b/core/app/api/v2/script_library.go
index f5fa7d23e783..83d68f5193e1 100644
--- a/core/app/api/v2/script_library.go
+++ b/core/app/api/v2/script_library.go
@@ -147,9 +147,7 @@ func (b *BaseApi) RunScript(c *gin.Context) {
 	fileName := strings.ReplaceAll(scriptItem.Name, " ", "_")
 	quitChan := make(chan bool, 3)
 	if currentNode == "local" {
-		tmpFile := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script")
-		initCmd := fmt.Sprintf("d=%s && mkdir -p $d && echo %s > $d/%s && clear && bash $d/%s", tmpFile, scriptItem.Script, fileName, fileName)
-		slave, err := terminal.NewCommand(initCmd)
+		slave, err := terminal.NewCommand(scriptItem.Script)
 		if wshandleError(wsConn, err) {
 			return
 		}
diff --git a/core/init/migration/helper/script.go b/core/init/migration/helper/script.go
index 3505775c7d87..30d0306ee5fb 100644
--- a/core/init/migration/helper/script.go
+++ b/core/init/migration/helper/script.go
@@ -10,262 +10,112 @@ import (
 func LoadScript() {
 	groups := []model.Group{
 		{Name: "install", Type: "script", IsDefault: false},
-		{Name: "uninstall", Type: "script", IsDefault: false},
 		{Name: "docker", Type: "script", IsDefault: false},
-		{Name: "firewalld", Type: "script", IsDefault: false},
-		{Name: "ufw", Type: "script", IsDefault: false},
+		{Name: "firewall", Type: "script", IsDefault: false},
 		{Name: "supervisor", Type: "script", IsDefault: false},
 		{Name: "clamav", Type: "script", IsDefault: false},
 		{Name: "ftp", Type: "script", IsDefault: false},
 		{Name: "fail2ban", Type: "script", IsDefault: false}}
+	_ = global.DB.Where("`type` = ?", "script").Delete(&model.Group{}).Error
 	_ = global.DB.Create(&groups).Error
 
 	_ = global.DB.Where("is_system = ?", 1).Delete(model.ScriptLibrary{}).Error
 	list := []model.ScriptLibrary{
-		{Name: "Install Docker", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[2].ID, groups[0].ID), Script: "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)"},
+		{Name: "Install Docker", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[1].ID, groups[0].ID), Script: "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)"},
 
-		{Name: "Install Firewalld", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[3].ID, groups[0].ID), Script: loadInstallFirewalld()},
-		{Name: "Uninstall Firewalld", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[3].ID, groups[1].ID), Script: loadUninstallFirewalld()},
+		{Name: "Install Firewall", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[2].ID, groups[0].ID), Script: loadInstallFirewall()},
 
-		{Name: "Install Ufw", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[4].ID, groups[0].ID), Script: loadInstallUfw()},
-		{Name: "Uninstall Ufw", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[4].ID, groups[1].ID), Script: loadUninstallUfw()},
+		{Name: "Install Supervisor", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[3].ID, groups[0].ID), Script: loadInstallSupervisor()},
 
-		{Name: "Install Supervisor", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[5].ID, groups[0].ID), Script: loadInstallSupervisor()},
-		{Name: "Uninstall Supervisor", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[2].ID, groups[1].ID), Script: loadUninstallSupervisor()},
+		{Name: "Install ClamAV", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[4].ID, groups[0].ID), Script: loadInstallClamAV()},
 
-		{Name: "Install ClamAV", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[6].ID, groups[0].ID), Script: loadInstallClamAV()},
-		{Name: "Uninstall ClamAV", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[6].ID, groups[1].ID), Script: loadUninstallClamAV()},
+		{Name: "Install Pure-FTPd", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[5].ID, groups[0].ID), Script: loadInstallFTP()},
 
-		{Name: "Install Pure-FTPd", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[7].ID, groups[0].ID), Script: loadInstallFTP()},
-		{Name: "Uninstall Pure-FTPd", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[7].ID, groups[1].ID), Script: loadUninstallFTP()},
-
-		{Name: "Install Fail2ban", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[8].ID, groups[0].ID), Script: loadInstallFail2ban()},
-		{Name: "Uninstall Fail2ban", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[8].ID, groups[1].ID), Script: loadUninstallFail2ban()},
+		{Name: "Install Fail2ban", IsSystem: true, Groups: fmt.Sprintf("%v,%v", groups[6].ID, groups[0].ID), Script: loadInstallFail2ban()},
 	}
 
 	_ = global.DB.Create(&list).Error
 }
 
-func loadInstallFirewalld() string {
+func loadInstallFirewall() string {
 	return `#!/bin/bash
 
-# 检查是否具有 sudo 权限
-if [ "$EUID" -ne 0 ]; then
-  echo "请使用 sudo 或以 root 用户运行此脚本"
-  exit 1
-fi
-
 # 检测操作系统类型
 if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 安装 firewalld
-if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在安装 firewalld..."
-  apt-get update
-  apt-get install -y firewalld
-elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在安装 firewalld..."
-  yum install -y firewalld
+    . /etc/os-release
+    OS=$ID
+    OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
 else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 设置开机自启动并启动服务
-if command -v systemctl &> /dev/null; then
-  echo "设置 firewalld 开机自启动..."
-  systemctl enable firewalld
-
-  echo "启动 firewalld 服务..."
-  systemctl start firewalld
-
-  if systemctl is-active --quiet firewalld; then
-    echo "firewalld 已成功安装并启动"
-  else
-    echo "firewalld 启动失败，请检查日志"
+    echo "无法检测操作系统类型"
     exit 1
-  fi
-else
-  echo "systemctl 不可用，请手动启动 firewalld"
-  exit 1
-fi
-
-# 放行 SSH 端口（默认 22）
-echo "在 firewalld 中放行 SSH 端口..."
-firewall-cmd --zone=public --add-service=ssh --permanent
-firewall-cmd --reload
-
-# 检查 SSH 端口是否放行
-if firewall-cmd --zone=public --query-service=ssh &> /dev/null; then
-  echo "SSH 端口已成功放行"
-else
-  echo "SSH 端口放行失败，请手动检查"
-  exit 1
 fi
 
-# 检查 firewalld 是否正常运行
-if firewall-cmd --state &> /dev/null; then
-  echo "firewalld 安装完成并正常运行！"
-else
-  echo "firewalld 安装或配置出现问题，请检查日志"
-  exit 1
-fi
-
-exit 0`
-}
-func loadUninstallFirewalld() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 停止 firewalld 服务
-if command -v systemctl &> /dev/null; then
-  echo "停止 firewalld 服务..."
-  systemctl stop firewalld
-  systemctl disable firewalld
-elif command -v service &> /dev/null; then
-  echo "停止 firewalld 服务..."
-  service firewalld stop
-  if command -v chkconfig &> /dev/null; then
-    chkconfig firewalld off
-  fi
-else
-  echo "无法停止 firewalld 服务，请手动停止"
-fi
-
-# 卸载 firewalld
+# 安装防火墙
+FIREWALL=""
 if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在卸载 firewalld..."
-  apt-get remove --purge -y firewalld
-  apt-get autoremove -y
+    echo "检测到 Debian/Ubuntu 系统，正在安装 ufw..."
+    FIREWALL="ufw"
+    apt-get update
+    apt-get install -y ufw
 elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在卸载 firewalld..."
-  yum remove -y firewalld
-  yum autoremove -y
-else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 删除 firewalld 配置文件
-echo "删除 firewalld 配置文件..."
-rm -rf /etc/firewalld
-rm -f /etc/sysconfig/firewalld
-
-# 检查是否卸载成功
-if ! command -v firewall-cmd &> /dev/null; then
-  echo "firewalld 已成功卸载"
+    echo "检测到 Red Hat/CentOS 系统，正在安装 firewall..."
+    FIREWALL="firewalld"
+    yum update
+    yum install -y firewalld
 else
-  echo "firewalld 卸载失败，请手动检查"
-  exit 1
-fi
-
-exit 0`
-}
-
-func loadInstallUfw() string {
-	return `
-#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 仅支持 Debian/Ubuntu 系统
-if [ "$OS" != "ubuntu" ] && [ "$OS" != "debian" ]; then
-  echo "此脚本仅支持 Debian/Ubuntu 系统"
-  exit 1
-fi
-
-# 安装 ufw
-echo "检测到 Debian/Ubuntu 系统，正在安装 ufw..."
-apt-get update
-apt-get install -y ufw
-
-# 启用 ufw
-echo "启用 ufw..."
-ufw enable
-
-# 放行 SSH 端口（默认 22）
-echo "放行 SSH 端口（22）..."
-ufw allow 22/tcp
-
-# 检查 SSH 端口是否放行
-if ufw status | grep -q "22/tcp"; then
-  echo "SSH 端口已成功放行"
-else
-  echo "SSH 端口放行失败，请手动检查"
-  exit 1
-fi
-
-# 检查 ufw 是否正常运行
-if ufw status | grep -q "Status: active"; then
-  echo "ufw 安装完成并正常运行！"
-else
-  echo "ufw 安装或配置出现问题，请检查日志"
-  exit 1
+    echo "不支持的操作系统: $OS"
+    exit 1
 fi
 
-exit 0`
-}
-func loadUninstallUfw() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
+read -p "请输入需要放行的端口(多个端口用空格分隔，如 80 443 22): " PORTS
 
-# 仅支持 Debian/Ubuntu 系统
-if [ "$OS" != "ubuntu" ] && [ "$OS" != "debian" ]; then
-  echo "此脚本仅支持 Debian/Ubuntu 系统"
-  exit 1
+# 验证端口输入
+if [ -z "$PORTS" ]; then
+    echo "错误：未输入任何端口"
+    exit 1
 fi
 
-# 停止并禁用 ufw
-echo "停止并禁用 ufw..."
-ufw disable
-
-# 卸载 ufw
-echo "卸载 ufw..."
-apt-get remove --purge -y ufw
-apt-get autoremove -y
-
-# 删除 ufw 配置文件
-echo "删除 ufw 配置文件..."
-rm -rf /etc/ufw
-rm -f /etc/default/ufw
-
-# 检查是否卸载成功
-if ! command -v ufw &> /dev/null; then
-  echo "ufw 已成功卸载"
-else
-  echo "ufw 卸载失败，请手动检查"
-  exit 1
+case $FIREWALL in
+    firewalld)
+        echo "配置firewalld..."
+        systemctl start firewalld
+        systemctl enable firewalld
+        
+        for port in $PORTS; do
+            firewall-cmd --zone=public --permanent --add-port="$port/tcp"
+        done
+        
+        firewall-cmd --reload
+        echo "已放行以下TCP端口: $PORTS"
+        ;;
+        
+    ufw)
+        echo "配置ufw..."
+        ufw --force enable
+        
+        for port in $PORTS; do
+            ufw allow "$port/tcp"
+        done
+        
+        echo "已放行以下TCP端口: $PORTS"
+        ;;
+esac
+
+# 检查防火墙是否正常运行
+if [ "$FIREWALL" = "firewalld" ]; then
+    if firewall-cmd --state &> /dev/null; then
+        echo "firewalld 安装完成并正常运行！"
+    else
+        echo "firewalld 安装或配置出现问题，请检查日志"
+        exit 1
+    fi
+else 
+    if ufw status | grep -q "Status: active"; then
+        echo "ufw 安装完成并正常运行！"
+    else
+        echo "ufw 安装或配置出现问题，请检查日志"
+        exit 1
+    fi
 fi
 
 exit 0`
@@ -274,12 +124,6 @@ exit 0`
 func loadInstallFTP() string {
 	return `#!/bin/bash
 
-# 检查是否具有 sudo 权限
-if [ "$EUID" -ne 0 ]; then
-  echo "请使用 sudo 或以 root 用户运行此脚本"
-  exit 1
-fi
-
 # 检测操作系统类型
 if [ -f /etc/os-release ]; then
   . /etc/os-release
@@ -308,37 +152,21 @@ fi
 echo "配置 Pure-FTPd..."
 PURE_FTPD_CONF="/etc/pure-ftpd/pure-ftpd.conf"
 if [ -f "$PURE_FTPD_CONF" ]; then
-  # 备份原始配置文件
-  cp "$PURE_FTPD_CONF" "$PURE_FTPD_CONF.bak"
-
-  # 启用本地用户登录
-  sed -i 's/^# UnixAuthentication/UnixAuthentication/' "$PURE_FTPD_CONF"
-
-  # 拒绝匿名登录
-  echo "NoAnonymous yes" >> "$PURE_FTPD_CONF"
-
-  # 启用被动模式
-  echo "PassivePortRange 30000 30100" >> "$PURE_FTPD_CONF"
-
-  # 限制用户访问家目录
-  echo "ChrootEveryone yes" >> "$PURE_FTPD_CONF"
-
-  # 启用日志记录
-  echo "VerboseLog yes" >> "$PURE_FTPD_CONF"
+    cp "$PURE_FTPD_CONF" "$PURE_FTPD_CONF.bak"
+    echo "PureDB /etc/pure-ftpd/pureftpd.pdb" >> "$PURE_FTPD_CONF"
+    sed -i 's/^# UnixAuthentication/UnixAuthentication/' "$PURE_FTPD_CONF"
+    echo "NoAnonymous yes" >> "$PURE_FTPD_CONF"
+    echo "PassivePortRange 39000 40000" >> "$PURE_FTPD_CONF"
+    echo "ChrootEveryone yes" >> "$PURE_FTPD_CONF"
+    echo "VerboseLog yes" >> "$PURE_FTPD_CONF"
 else
-  echo "未找到 Pure-FTPd 配置文件，请手动配置"
-  exit 1
+    echo '/etc/pure-ftpd/pureftpd.pdb' > /etc/pure-ftpd/conf/PureDB
+    echo yes > /etc/pure-ftpd/conf/VerboseLog 
+    echo yes > /etc/pure-ftpd/conf/NoAnonymous
+    echo '39000 40000' > /etc/pure-ftpd/conf/PassivePortRange
+    ln -s /etc/pure-ftpd/conf/PureDB /etc/pure-ftpd/auth/50puredb
 fi
 
-# 创建必要的目录和文件
-echo "创建必要的目录和文件..."
-mkdir -p /run/pure-ftpd
-chown pure-ftpd:pure-ftpd /run/pure-ftpd
-mkdir -p /var/log/pure-ftpd
-chown pure-ftpd:pure-ftpd /var/log/pure-ftpd
-touch /var/log/pure-ftpd/pure-ftpd.log
-chown pure-ftpd:pure-ftpd /var/log/pure-ftpd/pure-ftpd.log
-
 # 设置开机自启动并启动服务
 if command -v systemctl &> /dev/null; then
   echo "设置 Pure-FTPd 开机自启动..."
@@ -358,82 +186,13 @@ else
   exit 1
 fi
 
-# 检查 Pure-FTPd 是否正常运行
-if pure-ftpd --version &> /dev/null; then
-  echo "Pure-FTPd 安装完成并正常运行！"
-else
-  echo "Pure-FTPd 安装或配置出现问题，请检查日志"
-  exit 1
-fi
-
-exit 0`
-}
-func loadUninstallFTP() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 停止 Pure-FTPd 服务
-if command -v systemctl &> /dev/null; then
-  echo "停止 Pure-FTPd 服务..."
-  systemctl stop pure-ftpd
-  systemctl disable pure-ftpd
-elif command -v service &> /dev/null; then
-  echo "停止 Pure-FTPd 服务..."
-  service pure-ftpd stop
-  if command -v chkconfig &> /dev/null; then
-    chkconfig pure-ftpd off
-  fi
-else
-  echo "无法停止 Pure-FTPd 服务，请手动停止"
-fi
-
-# 卸载 Pure-FTPd
-if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在卸载 Pure-FTPd..."
-  apt-get remove --purge -y pure-ftpd
-  apt-get autoremove -y
-elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在卸载 Pure-FTPd..."
-  yum remove -y pure-ftpd
-  yum autoremove -y
-else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 删除 Pure-FTPd 配置文件、日志文件和用户数据库
-echo "删除 Pure-FTPd 配置文件、日志文件和用户数据库..."
-rm -rf /etc/pure-ftpd
-rm -rf /var/log/pure-ftpd
-rm -rf /var/lib/pure-ftpd
-
-# 检查是否卸载成功
-if ! command -v pure-ftpd &> /dev/null; then
-  echo "Pure-FTPd 已成功卸载"
-else
-  echo "Pure-FTPd 卸载失败，请手动检查"
-  exit 1
-fi
-
 exit 0`
 }
 
 func loadInstallClamAV() string {
 	return `#!/bin/bash
 
-# ClamAV 安装配置脚本
-# 支持系统：Ubuntu/Debian/CentOS/RHEL/Rocky/AlmaLinux
-
-# 识别系统类型
+# 检测操作系统类型
 if [ -f /etc/os-release ]; then
     . /etc/os-release
     OS=$ID
@@ -546,85 +305,19 @@ setup_service() {
 echo "正在安装 ClamAV..."
 install_clamav
 
-echo -e "\n\n配置 clamd..."
+echo -e "配置 clamd..."
 configure_clamd
 
-echo -e "\n\n配置 freshclam..."
+echo -e "配置 freshclam..."
 configure_freshclam
 
-echo -e "\n\n设置服务..."
+echo -e "设置服务..."
 setup_service
 
-echo -e "\n\n安装完成！"
+echo -e "安装完成！"
 
 echo 0`
 }
-func loadUninstallClamAV() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 停止 ClamAV 服务
-if command -v systemctl &> /dev/null; then
-  echo "停止 ClamAV 服务..."
-  if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-    systemctl stop clamav-daemon
-    systemctl disable clamav-daemon
-  elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-    systemctl stop clamd@scan
-    systemctl disable clamd@scan
-  fi
-elif command -v service &> /dev/null; then
-  echo "停止 ClamAV 服务..."
-  service clamav-daemon stop
-  if command -v chkconfig &> /dev/null; then
-    chkconfig clamav-daemon off
-  fi
-else
-  echo "无法停止 ClamAV 服务，请手动停止"
-fi
-
-# 卸载 ClamAV
-if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在卸载 ClamAV..."
-  apt-get remove --purge -y clamav clamav-daemon
-  apt-get autoremove -y
-elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在卸载 ClamAV..."
-  yum remove -y clamav clamd clamav-update
-  yum autoremove -y
-else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 删除 ClamAV 配置文件、病毒数据库和日志文件
-echo "删除 ClamAV 配置文件、病毒数据库和日志文件..."
-rm -rf /etc/clamav
-rm -rf /var/lib/clamav
-rm -rf /var/log/clamav
-rm -rf /var/run/clamav
-rm -f /etc/cron.daily/freshclam
-rm -f /etc/logrotate.d/clamav
-
-# 检查是否卸载成功
-if ! command -v clamscan &> /dev/null; then
-  echo "ClamAV 已成功卸载"
-else
-  echo "ClamAV 卸载失败，请手动检查"
-  exit 1
-fi
-
-exit 0`
-}
 
 func loadInstallFail2ban() string {
 	return `#!/bin/bash
@@ -656,15 +349,30 @@ fi
 # 配置 fail2ban
 echo "配置 fail2ban..."
 FAIL2BAN_CONF="/etc/fail2ban/jail.local"
-if [ ! -f "$FAIL2BAN_CONF" ]; then
-  echo "创建自定义配置文件 $FAIL2BAN_CONF..."
-  cat <<EOF > "$FAIL2BAN_CONF"
+LOG_FILE=""
+BAN_ACTION=""
+
+if systemctl is-active --quiet firewalld 2>/dev/null; then
+    BAN_ACTION="firewallcmd-ipset"
+elif systemctl is-active --quiet ufw 2>/dev/null || service ufw status 2>/dev/null | grep -q "active"; then
+    BAN_ACTION="ufw"
+else
+    BAN_ACTION="iptables-allports"
+fi
+
+if [ -f /var/log/secure ]; then
+    LOG_FILE="/var/log/secure"
+else
+    LOG_FILE="/var/log/auth.log"
+fi
+
+cat <<EOF > "$FAIL2BAN_CONF"
 #DEFAULT-START
 [DEFAULT]
 bantime = 600
 findtime = 300
 maxretry = 5
-banaction = $banaction
+banaction = $BAN_ACTION
 action = %(action_mwl)s
 #DEFAULT-END
 
@@ -676,13 +384,10 @@ port = 22
 maxretry = 5
 findtime = 300
 bantime = 600
-banaction = $banaction
+banaction = $BAN_ACTION
 action = %(action_mwl)s
-logpath = $logpath
+logpath = $LOG_FILE
 EOF
-else
-  echo "自定义配置文件已存在，跳过创建"
-fi
 
 # 设置开机自启动并启动服务
 if command -v systemctl &> /dev/null; then
@@ -711,64 +416,6 @@ else
   exit 1
 fi
 
-exit 0`
-}
-func loadUninstallFail2ban() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 停止 fail2ban 服务
-if command -v systemctl &> /dev/null; then
-  echo "停止 fail2ban 服务..."
-  systemctl stop fail2ban
-  systemctl disable fail2ban
-elif command -v service &> /dev/null; then
-  echo "停止 fail2ban 服务..."
-  service fail2ban stop
-  if command -v chkconfig &> /dev/null; then
-    chkconfig fail2ban off
-  fi
-else
-  echo "无法停止 fail2ban 服务，请手动停止"
-fi
-
-# 卸载 fail2ban
-if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在卸载 fail2ban..."
-  apt-get remove --purge -y fail2ban
-  apt-get autoremove -y
-elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在卸载 fail2ban..."
-  yum remove -y fail2ban
-  yum autoremove -y
-else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 删除 fail2ban 配置文件和数据
-echo "删除 fail2ban 配置文件和数据..."
-rm -rf /etc/fail2ban
-rm -rf /var/lib/fail2ban
-rm -rf /var/log/fail2ban*
-
-# 检查是否卸载成功
-if ! command -v fail2ban-client &> /dev/null; then
-  echo "fail2ban 已成功卸载"
-else
-  echo "fail2ban 卸载失败，请手动检查"
-  exit 1
-fi
-
 exit 0`
 }
 
@@ -833,63 +480,3 @@ fi
 
 exit 0`
 }
-func loadUninstallSupervisor() string {
-	return `#!/bin/bash
-
-# 检测操作系统类型
-if [ -f /etc/os-release ]; then
-  . /etc/os-release
-  OS=$ID
-  OS_LIKE=$(echo $ID_LIKE | awk '{print $1}')  # 获取类似的发行版信息
-else
-  echo "无法检测操作系统类型"
-  exit 1
-fi
-
-# 停止 Supervisor 服务
-if command -v systemctl &> /dev/null; then
-  echo "停止 Supervisor 服务..."
-  systemctl stop supervisord
-  systemctl disable supervisord
-elif command -v service &> /dev/null; then
-  echo "停止 Supervisor 服务..."
-  service supervisord stop
-  if command -v chkconfig &> /dev/null; then
-    chkconfig supervisord off
-  fi
-else
-  echo "无法停止 Supervisor 服务，请手动停止"
-fi
-
-# 卸载 Supervisor
-if [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
-  echo "检测到 Debian/Ubuntu 系统，正在卸载 Supervisor..."
-  apt-get remove --purge -y supervisor
-  apt-get autoremove -y
-elif [ "$OS" == "centos" ] || [ "$OS" == "rhel" ] || [ "$OS_LIKE" == "rhel" ]; then
-  echo "检测到 Red Hat/CentOS 系统，正在卸载 Supervisor..."
-  yum remove -y supervisor
-  yum autoremove -y
-else
-  echo "不支持的操作系统: $OS"
-  exit 1
-fi
-
-# 删除 Supervisor 配置文件、日志文件和进程管理文件
-echo "删除 Supervisor 配置文件、日志文件和进程管理文件..."
-rm -rf /etc/supervisor
-rm -rf /var/log/supervisor
-rm -rf /var/run/supervisor
-rm -f /etc/default/supervisor
-rm -f /etc/init.d/supervisor
-
-# 检查是否卸载成功
-if ! command -v supervisorctl &> /dev/null; then
-  echo "Supervisor 已成功卸载"
-else
-  echo "Supervisor 卸载失败，请手动检查"
-  exit 1
-fi
-
-exit 0`
-}
diff --git a/core/utils/terminal/local_cmd.go b/core/utils/terminal/local_cmd.go
index c4ce047915b8..02ac335b2c89 100644
--- a/core/utils/terminal/local_cmd.go
+++ b/core/utils/terminal/local_cmd.go
@@ -25,20 +25,21 @@ type LocalCommand struct {
 	pty *os.File
 }
 
-func NewCommand(initCmd string) (*LocalCommand, error) {
+func NewCommand(script string) (*LocalCommand, error) {
 	cmd := exec.Command("bash")
 	if term := os.Getenv("TERM"); term != "" {
 		cmd.Env = append(os.Environ(), "TERM="+term)
 	} else {
 		cmd.Env = append(os.Environ(), "TERM=xterm")
 	}
+	cmd.Env = append(cmd.Env, "INIT_SCRIPT="+script)
 	pty, err := pty.Start(cmd)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to start command")
 	}
-	if len(initCmd) != 0 {
+	if len(script) != 0 {
 		time.Sleep(100 * time.Millisecond)
-		_, _ = pty.Write([]byte(initCmd + "\n"))
+		_, _ = pty.Write([]byte("bash -c \"$INIT_SCRIPT\"\n"))
 	}
 
 	lcmd := &LocalCommand{
diff --git a/frontend/src/components/drawer-pro/index.vue b/frontend/src/components/drawer-pro/index.vue
index e11eff6ea5eb..7d65842bc930 100644
--- a/frontend/src/components/drawer-pro/index.vue
+++ b/frontend/src/components/drawer-pro/index.vue
@@ -116,23 +116,19 @@ const handleBack = () => {
 };
 
 const handleBeforeClose = (done: () => void) => {
-    console.log(drawerContent.value, drawerContent.value.querySelector('.el-form'));
     if (drawerContent.value) {
         const hasForm = drawerContent.value.querySelector('.el-form') !== null;
-        if (hasForm) {
-            ElMessageBox.confirm(
-                i18n.global.t('commons.msg.closeDrawerHelper'),
-                i18n.global.t('commons.button.close'),
-                {
-                    confirmButtonText: i18n.global.t('commons.button.confirm'),
-                    cancelButtonText: i18n.global.t('commons.button.cancel'),
-                },
-            ).then(async () => {
-                done();
-            });
-        } else {
+        const hasTerminal = drawerContent.value.querySelector('.terminal') !== null;
+        if (!hasForm && !hasTerminal) {
             done();
+            return;
         }
+        ElMessageBox.confirm(i18n.global.t('commons.msg.closeDrawerHelper'), i18n.global.t('commons.button.close'), {
+            confirmButtonText: i18n.global.t('commons.button.confirm'),
+            cancelButtonText: i18n.global.t('commons.button.cancel'),
+        }).then(async () => {
+            done();
+        });
     } else {
         done();
     }
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index e01983093565..3ea8d5e1e7e7 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1045,6 +1045,7 @@ const message = {
             edit: 'Edit Script',
             groupHelper:
                 'Set different groups based on script characteristics, which allows for faster script filtering operations.',
+            handleHelper: 'Execute script {1} on {0}, continue?',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index 428e796672fb..80dd9f359db3 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -1004,6 +1004,7 @@ const message = {
             edit: 'スクリプトを編集',
             groupHelper:
                 'スクリプトの特徴に基づいて異なるグループを設定することで、スクリプトのフィルタリング操作をより迅速に行うことができます。',
+            handleHelper: '{0} で {1} スクリプトを実行します。続行しますか？',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index cbebf897573e..397313adc72e 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -998,6 +998,7 @@ const message = {
             edit: '스크립트 수정',
             groupHelper:
                 '스크립트 특성에 따라 다양한 그룹을 설정하여 스크립트 필터링 작업을 더 빠르게 수행할 수 있습니다.',
+            handleHelper: '{0} 에서 {1} 스크립트를 실행합니다. 계속하시겠습니까?',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index 9ab0d1d1bf59..2dbd91533e2a 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -1034,6 +1034,7 @@ const message = {
             edit: 'Sunting Skrip',
             groupHelper:
                 'Tetapkan kumpulan yang berbeza berdasarkan ciri skrip, yang membolehkan operasi penapisan skrip dilakukan dengan lebih pantas.',
+            handleHelper: 'Akan melaksanakan skrip {1} pada {0}, teruskan?',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index 64986a15396b..f6b314fb5758 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -1023,6 +1023,7 @@ const message = {
             edit: 'Editar Script',
             groupHelper:
                 'Defina grupos diferentes com base nas características do script, o que permite operações de filtragem de scripts mais rápidas.',
+            handleHelper: 'Executar o script {1} em {0}, continuar?',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index e0bd0635e337..8967ca0bbc80 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -1029,6 +1029,7 @@ const message = {
             edit: 'Редактировать скрипт',
             groupHelper:
                 'Установите разные группы на основе характеристик скрипта, что позволяет быстрее выполнять операции фильтрации скриптов.',
+            handleHelper: 'Выполнить сценарий {1} на {0}, продолжить?',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index faf8e25e6348..04530b784277 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -992,6 +992,7 @@ const message = {
             create: '添加腳本',
             edit: '修改腳本',
             groupHelper: '根據腳本特徵設置不同的分組，可以更快地對腳本進行篩選操作。',
+            handleHelper: '將在 {0} 上執行 {1} 腳本，是否繼續？',
         },
     },
     monitor: {
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 5fa46a2cd243..9e4fb103ba45 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -990,6 +990,7 @@ const message = {
             create: '添加脚本',
             edit: '修改脚本',
             groupHelper: '针对脚本特征设置不同的分组，可以更加快速的对脚本进行筛选操作。',
+            handleHelper: '将在 {0} 上执行 {1} 脚本，是否继续？',
         },
     },
     monitor: {
diff --git a/frontend/src/views/cronjob/library/index.vue b/frontend/src/views/cronjob/library/index.vue
index 5645cc7df7f9..b75cc2baf2dd 100644
--- a/frontend/src/views/cronjob/library/index.vue
+++ b/frontend/src/views/cronjob/library/index.vue
@@ -198,7 +198,20 @@ const buttons = [
     {
         label: i18n.global.t('commons.button.handle'),
         click: (row: Cronjob.ScriptInfo) => {
-            runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });
+            ElMessageBox.confirm(
+                i18n.global.t('cronjob.library.handleHelper', [
+                    globalStore.currentNode === 'local' ? i18n.global.t('xpack.node.master') : globalStore.currentNode,
+                    row.name,
+                ]),
+                i18n.global.t('commons.button.handle'),
+                {
+                    confirmButtonText: i18n.global.t('commons.button.confirm'),
+                    cancelButtonText: i18n.global.t('commons.button.cancel'),
+                    type: 'info',
+                },
+            ).then(() => {
+                runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });
+            });
         },
     },
     {


Reviews:
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T09:41:58Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T09:42:07Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T09:42:10Z
user: login: wanghe-fit2cloud
body: /lgtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T10:06:39Z

Review comments:
diff_hunk: @@ -116,23 +116,19 @@ const handleBack = () => {
 };
 
 const handleBeforeClose = (done: () => void) => {
-    console.log(drawerContent.value, drawerContent.value.querySelector('.el-form'));
     if (drawerContent.value) {
         const hasForm = drawerContent.value.querySelector('.el-form') !== null;
-        if (hasForm) {
-            ElMessageBox.confirm(
-                i18n.global.t('commons.msg.closeDrawerHelper'),
-                i18n.global.t('commons.button.close'),
-                {
-                    confirmButtonText: i18n.global.t('commons.button.confirm'),
-                    cancelButtonText: i18n.global.t('commons.button.cancel'),
-                },
-            ).then(async () => {
-                done();
-            });
-        } else {
+        const hasTerminal = drawerContent.value.querySelector('.terminal') !== null;
+        if (!hasForm && !hasTerminal) {
             done();
+            return;
         }
+        ElMessageBox.confirm(i18n.global.t('commons.msg.closeDrawerHelper'), i18n.global.t('commons.button.close'), {
+            confirmButtonText: i18n.global.t('commons.button.confirm'),
+            cancelButtonText: i18n.global.t('commons.button.cancel'),
+        }).then(async () => {
+            done();
+        });
     } else {
         done();
     }
path: frontend/src/components/drawer-pro/index.vue
user: login: wanghe-fit2cloud
body: The provided JavaScript code for handling closing operations includes several changes and optimizations:

### Changes Made

1. **Querying Elements**:
   - Removed logging statements that query elements within `drawerContent.value`.
   - Simplified the conditional logic to check for `.terminal` existence before asking for confirmation.

2. **Handling Form Check**:
   - Combined `hasForm` and `!hasTerminal` checks into a single condition, simplifying the logic.
   - Returned early if no form or terminal is present, effectively skipping the modal prompt when unnecessary.

3. **Removed Unnecessary Code Block**:
   - The original block handling both forms and terminals was redundant with the current setup, which only handles non-terminal cases.

### Optimization Suggestions/Improvements

1. **Code Readability**:
   - Added comments to clarify the purpose of key lines and decisions made during the function execution.
   - Maintaining consistent indentation throughout reduces cognitive load when reviewing the code.

2. **Consistency with Logic**:
   - Ensured all conditions and actions are handled consistently across similar scenarios (i.e., checking for presence of `.el-form` vs `.terminal`).

These modifications make the code more efficient, easier to understand, and maintain while addressing potential edge cases where there might be empty drawers without either a form or a terminal element.
created_at: 2025-03-26T09:41:58Z
author_association: MEMBER
diff_hunk: @@ -198,7 +198,20 @@ const buttons = [
     {
         label: i18n.global.t('commons.button.handle'),
         click: (row: Cronjob.ScriptInfo) => {
-            runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });
+            ElMessageBox.confirm(
+                i18n.global.t('cronjob.library.handleHelper', [
+                    globalStore.currentNode === 'local' ? i18n.global.t('xpack.node.master') : globalStore.currentNode,
+                    row.name,
+                ]),
+                i18n.global.t('commons.button.handle'),
+                {
+                    confirmButtonText: i18n.global.t('commons.button.confirm'),
+                    cancelButtonText: i18n.global.t('commons.button.cancel'),
+                    type: 'info',
+                },
+            ).then(() => {
+                runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });
+            });
         },
     },
     {
path: frontend/src/views/cronjob/library/index.vue
user: login: wanghe-fit2cloud
body: The code has two changes. The first change removes the `runRef.value!.acceptParams` call and instead replaces it with an ElMessageBox confirmation dialog prompting the user to confirm if they want to execute the script. This adds a layer of safety and prevents accidental execution of scripts without proper confirmation.

### Potential Improvements:

1. **Consistent Usage**: Ensure that all button configurations follow a consistent structure for better readability.
2. **Error Handling/Feedback**: Consider adding handling for exceptions or errors during the execution step using something like try-catch blocks to improve robustness.

Here's the modified section of the code with improved formatting:

```javascript
@@ -198,7 +198,32 @@ const buttons = [
     {
         label: i18n.global.t('commons.button.handle'),
         click: (row: Cronjob.ScriptInfo) => {
-            runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });
+            ElMessageBox.confirm(
+                i18n.global.t('cronjob.library.handleHelper', [
+                    globalStore.currentNode === 'local'
+                        ? i18n.global.t('xpack.node.master')
+                        : globalStore.currentNode,
+                    row.name,
+                ]),
+                i18n.global.t('commons.button.handle'),
+                {
+                    confirmButtonText: i18n.global.t('commons.button.confirm'),
+                    cancelButtonText: i18n.global.t('commons.button.cancel'),
+                    type: 'info',
+                },
+            )
+                .then(() => {
+                    // Assuming runRef.value is defined elsewhere and acceptsParams method exists
+                    try {
+                        runRef.value.acceptParams({
+                            scriptID: row.id,
+                            scriptName: row.name,
+                        });
+                    } catch (error) {
+                        console.error(`Failed to execute script '${row.name}':`, error);
+                        ElMessage.error(i18n.global.t('cronjob.errorMessage'));
+                    }
+                })
+                .catch(() => {
+                    // User canceled operation
+                    ElMessage.info(i18n.global.t('cronjob.cancelledOperation'));
+                });
         },
     },
 };
```

This version includes basic exception handling and uses a more detailed message when the operation fails.
created_at: 2025-03-26T09:42:06Z
author_association: MEMBER
diff_hunk: @@ -25,20 +25,21 @@ type LocalCommand struct {
 	pty *os.File
 }
 
-func NewCommand(initCmd string) (*LocalCommand, error) {
+func NewCommand(script string) (*LocalCommand, error) {
 	cmd := exec.Command("bash")
 	if term := os.Getenv("TERM"); term != "" {
 		cmd.Env = append(os.Environ(), "TERM="+term)
 	} else {
 		cmd.Env = append(os.Environ(), "TERM=xterm")
 	}
+	cmd.Env = append(cmd.Env, "INIT_SCRIPT="+script)
 	pty, err := pty.Start(cmd)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to start command")
 	}
-	if len(initCmd) != 0 {
+	if len(script) != 0 {
 		time.Sleep(100 * time.Millisecond)
-		_, _ = pty.Write([]byte(initCmd + "\n"))
+		_, _ = pty.Write([]byte("bash -c \"$INIT_SCRIPT\"\n"))
 	}
 
 	lcmd := &LocalCommand{
path: core/utils/terminal/local_cmd.go
user: login: wanghe-fit2cloud
body: The main issues with the code is:

1. The comment in `initCmd` should be updated to mention that it's an `INIT_SCRIPT`.
2. There should be a space added after "bash" in `"bash -c "$INIT_SCRIPT\""`, as per Unix shell syntax.

Suggestions for improvement:
- Use a function name like `CreateCommand`. It better describes what the function does and aligns with Go naming conventions.
- Consider using `filepath.Join()` instead of manual concatenation when creating full paths if applicable, which would enhance safety and make the code more robust for different environments.
created_at: 2025-03-26T09:42:10Z
author_association: MEMBER

PR comments:
user: login: f2c-ci-robot[bot]
created_at: 2025-03-26T09:41:44Z
body: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-03-26T09:43:30Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8262) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8262&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8262&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=1Panel-dev_1Panel&pullRequest=8262&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8262&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8262&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8262)


author_association: NONE
user: login: wanghe-fit2cloud
created_at: 2025-03-26T10:06:49Z
body: /approve
author_association: MEMBER
user: login: f2c-ci-robot[bot]
created_at: 2025-03-26T10:06:56Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/1Panel-dev/1Panel/pull/8262#issuecomment-2753861113" title="Approved">wanghe-fit2cloud</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=1Panel-dev%2F1Panel).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/1Panel-dev/1Panel/blob/dev-v2/OWNERS)~~ [wanghe-fit2cloud]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE