diff --git a/app/console/commands/panel.go b/app/console/commands/panel.go index d16a50d5..dcb2f5bc 100644 --- a/app/console/commands/panel.go +++ b/app/console/commands/panel.go @@ -17,6 +17,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -143,7 +144,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { return nil } - port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) + port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { color.Red().Printfln(translate.Get("commands.panel.portFail")) return nil @@ -164,7 +165,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { color.Green().Printfln(translate.Get("commands.panel.getInfo.address") + ": " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance")) case "getPort": - port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) + port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { color.Red().Printfln(translate.Get("commands.panel.portFail")) return nil @@ -176,12 +177,12 @@ func (receiver *Panel) Handle(ctx console.Context) error { color.Green().Printfln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance")) case "deleteEntrance": - oldEntrance, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`) + oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail")) return nil } - if _, err = tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil { + if _, err = shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil { color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail")) return nil } @@ -288,7 +289,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { } backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip" - if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil { + if _, err := shell.Execf(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error()) return nil } @@ -307,13 +308,13 @@ func (receiver *Panel) Handle(ctx console.Context) error { color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name) color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport")) - if _, err = tools.Exec(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil { + if _, err = shell.Execf(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error()) return nil } color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess")) color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress")) - if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { + if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error()) return nil } @@ -333,7 +334,7 @@ func (receiver *Panel) Handle(ctx console.Context) error { case "postgresql": backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql" - check, err := tools.Exec(`su - postgres -c "psql -l" 2>&1`) + check, err := shell.Execf(`su - postgres -c "psql -l" 2>&1`) if err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error()) color.Green().Printfln(hr) @@ -347,13 +348,13 @@ func (receiver *Panel) Handle(ctx console.Context) error { color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name) color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport")) - if _, err = tools.Exec(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil { + if _, err = shell.Execf(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error()) return nil } color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess")) color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress")) - if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { + if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error()) return nil } @@ -432,11 +433,11 @@ func (receiver *Panel) Handle(ctx console.Context) error { } backupPath := "/www/wwwlogs/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".log.zip" - if _, err := tools.Exec(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil { + if _, err := shell.Execf(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error()) return nil } - if _, err := tools.Exec(`echo "" > ` + logPath); err != nil { + if _, err := shell.Execf(`echo "" > ` + logPath); err != nil { color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error()) return nil } diff --git a/app/console/commands/panel_task.go b/app/console/commands/panel_task.go index da101998..5c682fbd 100644 --- a/app/console/commands/panel_task.go +++ b/app/console/commands/panel_task.go @@ -8,6 +8,7 @@ import ( "github.com/goravel/framework/facades" "github.com/goravel/framework/support/carbon" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -58,7 +59,7 @@ func (receiver *PanelTask) Handle(console.Context) error { } // 清理 7 天前的备份 - if _, err := tools.Exec(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil { + if _, err := shell.Execf(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil { types.Status = types.StatusFailed facades.Log().Tags("面板", "每日任务"). With(map[string]any{ diff --git a/app/http/controllers/cron_controller.go b/app/http/controllers/cron_controller.go index 8c37d669..2946c5b2 100644 --- a/app/http/controllers/cron_controller.go +++ b/app/http/controllers/cron_controller.go @@ -12,6 +12,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -65,7 +66,7 @@ func (r *CronController) Add(ctx http.Context) http.Response { return Error(ctx, http.StatusUnprocessableEntity, "时间格式错误") } - shell := ctx.Request().Input("script") + script := ctx.Request().Input("script") cronType := ctx.Request().Input("type") if cronType == "backup" { backupType := ctx.Request().Input("backup_type") @@ -78,7 +79,7 @@ func (r *CronController) Add(ctx http.Context) http.Response { backupPath = r.setting.Get(models.SettingKeyBackupPath) + "/" + backupType } backupSave := ctx.Request().InputInt("save", 10) - shell = `#!/bin/bash + script = `#!/bin/bash export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH # 耗子面板 - 数据备份脚本 @@ -95,7 +96,7 @@ panel backup ${type} ${name} ${path} ${save} 2>&1 if cronType == "cutoff" { website := ctx.Request().Input("website") save := ctx.Request().InputInt("save", 180) - shell = `#!/bin/bash + script = `#!/bin/bash export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH # 耗子面板 - 日志切割脚本 @@ -117,10 +118,10 @@ panel cutoff ${name} ${save} 2>&1 return Error(ctx, http.StatusInternalServerError, "计划任务日志目录不存在") } shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + tools.RandomString(16) - if err := tools.Write(shellDir+shellFile+".sh", shell, 0700); err != nil { + if err := tools.Write(shellDir+shellFile+".sh", script, 0700); err != nil { return Error(ctx, http.StatusInternalServerError, err.Error()) } - if out, err := tools.Exec("dos2unix " + shellDir + shellFile + ".sh"); err != nil { + if out, err := shell.Execf("dos2unix " + shellDir + shellFile + ".sh"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -156,12 +157,12 @@ func (r *CronController) Script(ctx http.Context) http.Response { return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在") } - shell, err := tools.Read(cron.Shell) + script, err := tools.Read(cron.Shell) if err != nil { return Error(ctx, http.StatusInternalServerError, err.Error()) } - return Success(ctx, shell) + return Success(ctx, script) } // Update 更新计划任务 @@ -200,7 +201,7 @@ func (r *CronController) Update(ctx http.Context) http.Response { if err := tools.Write(cron.Shell, ctx.Request().Input("script"), 0644); err != nil { return Error(ctx, http.StatusInternalServerError, err.Error()) } - if out, err := tools.Exec("dos2unix " + cron.Shell); err != nil { + if out, err := shell.Execf("dos2unix " + cron.Shell); err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -284,7 +285,7 @@ func (r *CronController) Log(ctx http.Context) http.Response { return Error(ctx, http.StatusUnprocessableEntity, "日志文件不存在") } - log, err := tools.Exec("tail -n 1000 " + cron.Log) + log, err := shell.Execf("tail -n 1000 " + cron.Log) if err != nil { return Error(ctx, http.StatusInternalServerError, err.Error()) } diff --git a/app/http/controllers/file_controller.go b/app/http/controllers/file_controller.go index f5f90847..65cf0349 100644 --- a/app/http/controllers/file_controller.go +++ b/app/http/controllers/file_controller.go @@ -12,6 +12,7 @@ import ( "github.com/goravel/framework/support/carbon" requests "github.com/TheTNB/panel/app/http/requests/file" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -42,7 +43,7 @@ func (r *FileController) Create(ctx http.Context) http.Response { isDir := ctx.Request().InputBool("dir") if !isDir { - if out, err := tools.Exec("touch " + request.Path); err != nil { + if out, err := shell.Execf("touch " + request.Path); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } else { diff --git a/app/http/controllers/info_controller.go b/app/http/controllers/info_controller.go index bcc0e003..e1b61c74 100644 --- a/app/http/controllers/info_controller.go +++ b/app/http/controllers/info_controller.go @@ -12,6 +12,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -152,7 +153,7 @@ func (r *InfoController) CountInfo(ctx http.Context) http.Response { if postgresqlInstalled { status, err := tools.ServiceStatus("postgresql") if status && err == nil { - raw, err := tools.Exec(`echo "\l" | su - postgres -c "psql"`) + raw, err := shell.Execf(`echo "\l" | su - postgres -c "psql"`) if err == nil { databases := strings.Split(raw, "\n") if len(databases) >= 4 { @@ -173,7 +174,7 @@ func (r *InfoController) CountInfo(ctx http.Context) http.Response { var ftpCount int64 var ftpPlugin = r.plugin.GetInstalledBySlug("pureftpd") if ftpPlugin.ID != 0 { - listRaw, err := tools.Exec("pure-pw list") + listRaw, err := shell.Execf("pure-pw list") if len(listRaw) != 0 && err == nil { listArr := strings.Split(listRaw, "\n") ftpCount = int64(len(listArr)) diff --git a/app/http/controllers/plugins/fail2ban_controller.go b/app/http/controllers/plugins/fail2ban_controller.go index e15496f8..959ad5cd 100644 --- a/app/http/controllers/plugins/fail2ban_controller.go +++ b/app/http/controllers/plugins/fail2ban_controller.go @@ -12,6 +12,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -171,15 +172,15 @@ ignoreregex = logPath = "/var/log/secure" } filter = "sshd" - port, err = tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") + port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") case "mysql": logPath = "/www/server/mysql/mysql-error.log" filter = "mysqld-auth" - port, err = tools.Exec("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'") + port, err = shell.Execf("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'") case "pure-ftpd": logPath = "/var/log/messages" filter = "pure-ftpd" - port, err = tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`) + port, err = shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`) default: return controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务") } @@ -206,7 +207,7 @@ logpath = ` + logPath + ` } } - if _, err := tools.Exec("fail2ban-client reload"); err != nil { + if _, err := shell.Execf("fail2ban-client reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败") } @@ -231,7 +232,7 @@ func (r *Fail2banController) Delete(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败") } - if _, err := tools.Exec("fail2ban-client reload"); err != nil { + if _, err := shell.Execf("fail2ban-client reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败") } @@ -245,15 +246,15 @@ func (r *Fail2banController) BanList(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数") } - currentlyBan, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Currently banned" | awk '{print $4}'`) + currentlyBan, err := shell.Execf(`fail2ban-client status %s | grep "Currently banned" | awk '{print $4}'`, name) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败") } - totalBan, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Total banned" | awk '{print $4}'`) + totalBan, err := shell.Execf(`fail2ban-client status %s | grep "Total banned" | awk '{print $4}'`, name) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败") } - bannedIp, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Banned IP list" | awk -F ":" '{print $2}'`) + bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | awk -F ":" '{print $2}'`, name) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败") } @@ -287,7 +288,7 @@ func (r *Fail2banController) Unban(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数") } - if _, err := tools.Exec("fail2ban-client set " + name + " unbanip " + ip); err != nil { + if _, err := shell.Execf("fail2ban-client set %s unbanip %s", name, ip); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "解封失败") } @@ -317,7 +318,7 @@ func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败") } - if _, err := tools.Exec("fail2ban-client reload"); err != nil { + if _, err := shell.Execf("fail2ban-client reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败") } return controllers.Success(ctx, nil) diff --git a/app/http/controllers/plugins/mysql_controller.go b/app/http/controllers/plugins/mysql_controller.go index f2e0211f..c5d43ce8 100644 --- a/app/http/controllers/plugins/mysql_controller.go +++ b/app/http/controllers/plugins/mysql_controller.go @@ -12,6 +12,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -68,7 +69,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response { return controllers.Success(ctx, []types.NV{}) } - raw, err := tools.Exec("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1") + raw, err := shell.Execf("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败") } @@ -125,7 +126,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response { // ErrorLog 获取错误日志 func (r *MySQLController) ErrorLog(ctx http.Context) http.Response { - log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log") + log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-error.log") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, log) } @@ -135,7 +136,7 @@ func (r *MySQLController) ErrorLog(ctx http.Context) http.Response { // ClearErrorLog 清空错误日志 func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response { - if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-error.log"); err != nil { + if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-error.log"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -144,7 +145,7 @@ func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response { // SlowLog 获取慢查询日志 func (r *MySQLController) SlowLog(ctx http.Context) http.Response { - log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log") + log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-slow.log") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, log) } @@ -154,7 +155,7 @@ func (r *MySQLController) SlowLog(ctx http.Context) http.Response { // ClearSlowLog 清空慢查询日志 func (r *MySQLController) ClearSlowLog(ctx http.Context) http.Response { - if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-slow.log"); err != nil { + if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-slow.log"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } return controllers.Success(ctx, nil) @@ -187,15 +188,15 @@ func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response { oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if oldRootPassword != rootPassword { - if _, err = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil { + if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置root密码失败: %v", err)) } - if _, err = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil { + if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败") } if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil { - _, _ = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword)) - _, _ = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword)) + _, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword)) + _, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword)) return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err)) } } @@ -269,16 +270,16 @@ func (r *MySQLController) AddDatabase(ctx http.Context) http.Response { user := ctx.Request().Input("user") password := ctx.Request().Input("password") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -295,7 +296,7 @@ func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response { rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) database := ctx.Request().Input("database") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -477,13 +478,13 @@ func (r *MySQLController) AddUser(ctx http.Context) http.Response { user := ctx.Request().Input("user") password := ctx.Request().Input("password") database := ctx.Request().Input("database") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -500,7 +501,7 @@ func (r *MySQLController) DeleteUser(ctx http.Context) http.Response { rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -519,10 +520,10 @@ func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response { rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") password := ctx.Request().Input("password") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -541,13 +542,13 @@ func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response { rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") database := ctx.Request().Input("database") - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { + if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } diff --git a/app/http/controllers/plugins/openresty_controller.go b/app/http/controllers/plugins/openresty_controller.go index 4eb6d9b4..1586d43d 100644 --- a/app/http/controllers/plugins/openresty_controller.go +++ b/app/http/controllers/plugins/openresty_controller.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -77,7 +78,7 @@ func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response { return controllers.Success(ctx, "") } - out, err := tools.Exec("tail -n 100 /www/wwwlogs/openresty_error.log") + out, err := shell.Execf("tail -n 100 /www/wwwlogs/openresty_error.log") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -94,7 +95,7 @@ func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response { // @Success 200 {object} controllers.SuccessResponse // @Router /plugins/openresty/clearErrorLog [post] func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response { - if out, err := tools.Exec("echo '' > /www/wwwlogs/openresty_error.log"); err != nil { + if out, err := shell.Execf("echo '' > /www/wwwlogs/openresty_error.log"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -119,7 +120,7 @@ func (r *OpenRestyController) Load(ctx http.Context) http.Response { raw := resp.String() var data []types.NV - workers, err := tools.Exec("ps aux | grep nginx | grep 'worker process' | wc -l") + workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败") } @@ -128,7 +129,7 @@ func (r *OpenRestyController) Load(ctx http.Context) http.Response { Value: workers, }) - out, err := tools.Exec("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'") + out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败") } diff --git a/app/http/controllers/plugins/phpmyadmin_controller.go b/app/http/controllers/plugins/phpmyadmin_controller.go index 87309dd7..a13c96e2 100644 --- a/app/http/controllers/plugins/phpmyadmin_controller.go +++ b/app/http/controllers/plugins/phpmyadmin_controller.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -52,8 +53,8 @@ func (r *PhpMyAdminController) Info(ctx http.Context) http.Response { } func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response { - port := ctx.Request().Input("port") - if len(port) == 0 { + port := ctx.Request().InputInt("port") + if port == 0 { return controllers.Error(ctx, http.StatusInternalServerError, "端口不能为空") } @@ -61,7 +62,7 @@ func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response { if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } - conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+port+";") + conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(port)+";") if err := tools.Write("/www/server/vhost/phpmyadmin.conf", conf, 0644); err != nil { facades.Log().Request(ctx.Request()).Tags("插件", "phpMyAdmin").With(map[string]any{ "error": err.Error(), @@ -70,17 +71,17 @@ func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response { } if tools.IsRHEL() { - if out, err := tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent"); err != nil { + if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%d/tcp --permanent", port); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --reload"); err != nil { + if out, err := shell.Execf("firewall-cmd --reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } else { - if out, err := tools.Exec("ufw allow " + port + "/tcp"); err != nil { + if out, err := shell.Execf("ufw allow %d/tcp", port); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw reload"); err != nil { + if out, err := shell.Execf("ufw reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } diff --git a/app/http/controllers/plugins/postgresql_controller.go b/app/http/controllers/plugins/postgresql_controller.go index 23740abe..f3a607e7 100644 --- a/app/http/controllers/plugins/postgresql_controller.go +++ b/app/http/controllers/plugins/postgresql_controller.go @@ -11,6 +11,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -92,23 +93,23 @@ func (r *PostgreSQLController) Load(ctx http.Context) http.Response { return controllers.Success(ctx, []types.NV{}) } - time, err := tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`) + time, err := shell.Execf(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL启动时间失败") } - pid, err := tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`) + pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程PID失败") } - process, err := tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`) + process, err := shell.Execf(`ps aux | grep postgres | grep -v grep | wc -l`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程数失败") } - connections, err := tools.Exec(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`) + connections, err := shell.Execf(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL连接数失败") } - storage, err := tools.Exec(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`) + storage, err := shell.Execf(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL空间占用失败") } @@ -126,7 +127,7 @@ func (r *PostgreSQLController) Load(ctx http.Context) http.Response { // Log 获取日志 func (r *PostgreSQLController) Log(ctx http.Context) http.Response { - log, err := tools.Exec("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log") + log, err := shell.Execf("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, log) } @@ -136,7 +137,7 @@ func (r *PostgreSQLController) Log(ctx http.Context) http.Response { // ClearLog 清空日志 func (r *PostgreSQLController) ClearLog(ctx http.Context) http.Response { - if out, err := tools.Exec("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log"); err != nil { + if out, err := shell.Execf("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -211,21 +212,21 @@ func (r *PostgreSQLController) AddDatabase(ctx http.Context) http.Response { user := ctx.Request().Input("user") password := ctx.Request().Input("password") - if out, err := tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" - if out, err := tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil { + if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -245,7 +246,7 @@ func (r *PostgreSQLController) DeleteDatabase(ctx http.Context) http.Response { } database := ctx.Request().Input("database") - if out, err := tools.Exec(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -428,15 +429,15 @@ func (r *PostgreSQLController) AddRole(ctx http.Context) http.Response { user := ctx.Request().Input("user") password := ctx.Request().Input("password") database := ctx.Request().Input("database") - if out, err := tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" - if out, err := tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil { + if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -456,10 +457,10 @@ func (r *PostgreSQLController) DeleteRole(ctx http.Context) http.Response { } user := ctx.Request().Input("user") - if out, err := tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil { + if out, err := shell.Execf(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -481,7 +482,7 @@ func (r *PostgreSQLController) SetRolePassword(ctx http.Context) http.Response { user := ctx.Request().Input("user") password := ctx.Request().Input("password") - if out, err := tools.Exec(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { + if out, err := shell.Execf(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } diff --git a/app/http/controllers/plugins/pureftpd_controller.go b/app/http/controllers/plugins/pureftpd_controller.go index 9148833c..9ab63546 100644 --- a/app/http/controllers/plugins/pureftpd_controller.go +++ b/app/http/controllers/plugins/pureftpd_controller.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -21,7 +22,7 @@ func NewPureFtpdController() *PureFtpdController { // List 获取用户列表 func (r *PureFtpdController) List(ctx http.Context) http.Response { - listRaw, err := tools.Exec("pure-pw list") + listRaw, err := shell.Execf("pure-pw list") if err != nil { return controllers.Success(ctx, http.Json{ "total": 0, @@ -78,10 +79,10 @@ func (r *PureFtpdController) Add(ctx http.Context) http.Response { if err := tools.Chown(path, "www", "www"); err != nil { return nil } - if out, err := tools.Exec(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path); err != nil { + if out, err := shell.Execf(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("pure-pw mkdb"); err != nil { + if out, err := shell.Execf("pure-pw mkdb"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -98,10 +99,10 @@ func (r *PureFtpdController) Delete(ctx http.Context) http.Response { username := ctx.Request().Input("username") - if out, err := tools.Exec("pure-pw userdel " + username + " -m"); err != nil { + if out, err := shell.Execf("pure-pw userdel " + username + " -m"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("pure-pw mkdb"); err != nil { + if out, err := shell.Execf("pure-pw mkdb"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -120,10 +121,10 @@ func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response { username := ctx.Request().Input("username") password := ctx.Request().Input("password") - if out, err := tools.Exec(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`); err != nil { + if out, err := shell.Execf(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("pure-pw mkdb"); err != nil { + if out, err := shell.Execf("pure-pw mkdb"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -132,7 +133,7 @@ func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response { // GetPort 获取端口 func (r *PureFtpdController) GetPort(ctx http.Context) http.Response { - port, err := tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`) + port, err := shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败") } @@ -149,21 +150,21 @@ func (r *PureFtpdController) SetPort(ctx http.Context) http.Response { } port := ctx.Request().Input("port") - if out, err := tools.Exec(`sed -i "s/Bind.*/Bind 0.0.0.0,` + port + `/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`); err != nil { + if out, err := shell.Execf(`sed -i "s/Bind.*/Bind 0.0.0.0,%s/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`, port); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } if tools.IsRHEL() { - if out, err := tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent"); err != nil { + if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%s/tcp --permanent", port); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --reload"); err != nil { + if out, err := shell.Execf("firewall-cmd --reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } else { - if out, err := tools.Exec("ufw allow " + port + "/tcp"); err != nil { + if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw reload"); err != nil { + if out, err := shell.Execf("ufw reload"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } diff --git a/app/http/controllers/plugins/redis_controller.go b/app/http/controllers/plugins/redis_controller.go index 88a3283f..00fce46a 100644 --- a/app/http/controllers/plugins/redis_controller.go +++ b/app/http/controllers/plugins/redis_controller.go @@ -6,6 +6,7 @@ import ( "github.com/goravel/framework/contracts/http" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -56,7 +57,7 @@ func (r *RedisController) Load(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "Redis已停止运行") } - raw, err := tools.Exec("redis-cli info") + raw, err := shell.Execf("redis-cli info") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败") } diff --git a/app/http/controllers/plugins/rsync_controller.go b/app/http/controllers/plugins/rsync_controller.go index 24f843a2..19e4cb79 100644 --- a/app/http/controllers/plugins/rsync_controller.go +++ b/app/http/controllers/plugins/rsync_controller.go @@ -8,6 +8,7 @@ import ( "github.com/TheTNB/panel/app/http/controllers" requests "github.com/TheTNB/panel/app/http/requests/plugins/rsync" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -69,7 +70,7 @@ func (r *RsyncController) List(ctx http.Context) http.Response { currentModule.ReadOnly = value == "yes" || value == "true" case "auth users": currentModule.AuthUser = value - currentModule.Secret, err = tools.Exec("grep -E '^" + currentModule.AuthUser + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'") + currentModule.Secret, err = shell.Execf("grep -E '^" + currentModule.AuthUser + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'") if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取模块"+currentModule.AuthUser+"的密钥失败") } @@ -131,7 +132,7 @@ secrets file = /etc/rsyncd.secrets if err := tools.WriteAppend("/etc/rsyncd.conf", conf); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } - if out, err := tools.Exec("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil { + if out, err := shell.Execf("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -172,7 +173,7 @@ func (r *RsyncController) Destroy(ctx http.Context) http.Response { match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module) if len(match) == 2 { authUser := match[1] - if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil { + if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } @@ -230,7 +231,7 @@ secrets file = /etc/rsyncd.secrets match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module) if len(match) == 2 { authUser := match[1] - if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil { + if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } } @@ -238,7 +239,7 @@ secrets file = /etc/rsyncd.secrets if err = tools.Write("/etc/rsyncd.conf", config, 0644); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } - if out, err := tools.Exec("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil { + if out, err := shell.Execf("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } diff --git a/app/http/controllers/plugins/s3fs_controller.go b/app/http/controllers/plugins/s3fs_controller.go index daadcebd..93a657c5 100644 --- a/app/http/controllers/plugins/s3fs_controller.go +++ b/app/http/controllers/plugins/s3fs_controller.go @@ -11,6 +11,7 @@ import ( "github.com/TheTNB/panel/app/http/controllers" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -90,16 +91,16 @@ func (r *S3fsController) Add(ctx http.Context) http.Response { if err := tools.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600); err != nil { return nil } - out, err := tools.Exec(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`) + out, err := shell.Execf(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if mountCheck, err := tools.Exec("mount -a 2>&1"); err != nil { - _, _ = tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`) + if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil { + _, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`) return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck) } - if _, err := tools.Exec("df -h | grep " + path + " 2>&1"); err != nil { - _, _ = tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`) + if _, err := shell.Execf("df -h | grep " + path + " 2>&1"); err != nil { + _, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`) return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确") } @@ -145,16 +146,16 @@ func (r *S3fsController) Delete(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在") } - if out, err := tools.Exec(`fusermount -u '` + mount.Path + `' 2>&1`); err != nil { + if out, err := shell.Execf(`fusermount -u '` + mount.Path + `' 2>&1`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`umount '` + mount.Path + `' 2>&1`); err != nil { + if out, err := shell.Execf(`umount '` + mount.Path + `' 2>&1`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`); err != nil { + if out, err := shell.Execf(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } - if mountCheck, err := tools.Exec("mount -a 2>&1"); err != nil { + if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck) } if err := tools.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil { diff --git a/app/http/controllers/plugins/supervisor_controller.go b/app/http/controllers/plugins/supervisor_controller.go index 35747bcf..f7c34dba 100644 --- a/app/http/controllers/plugins/supervisor_controller.go +++ b/app/http/controllers/plugins/supervisor_controller.go @@ -5,10 +5,11 @@ import ( "strconv" "strings" - "github.com/TheTNB/panel/pkg/tools" "github.com/goravel/framework/contracts/http" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" + "github.com/TheTNB/panel/pkg/tools" ) type SupervisorController struct { @@ -35,7 +36,7 @@ func (r *SupervisorController) Service(ctx http.Context) http.Response { // Log 日志 func (r *SupervisorController) Log(ctx http.Context) http.Response { - log, err := tools.Exec(`tail -n 200 /var/log/supervisor/supervisord.log`) + log, err := shell.Execf(`tail -n 200 /var/log/supervisor/supervisord.log`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, log) } @@ -45,7 +46,7 @@ func (r *SupervisorController) Log(ctx http.Context) http.Response { // ClearLog 清空日志 func (r *SupervisorController) ClearLog(ctx http.Context) http.Response { - if out, err := tools.Exec(`echo "" > /var/log/supervisor/supervisord.log`); err != nil { + if out, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -99,7 +100,7 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response { Uptime string `json:"uptime"` } - out, err := tools.Exec(`supervisorctl status | awk '{print $1}'`) + out, err := shell.Execf(`supervisorctl status | awk '{print $1}'`) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -112,13 +113,13 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response { var p process p.Name = line - if status, err := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $2}'`); err == nil { + if status, err := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $2}'`); err == nil { p.Status = status } if p.Status == "RUNNING" { - pid, _ := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $4}'`) + pid, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $4}'`) p.Pid = strings.ReplaceAll(pid, ",", "") - uptime, _ := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $6}'`) + uptime, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $6}'`) p.Uptime = uptime } else { p.Pid = "-" @@ -138,7 +139,7 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response { // StartProcess 启动进程 func (r *SupervisorController) StartProcess(ctx http.Context) http.Response { process := ctx.Request().Input("process") - if out, err := tools.Exec(`supervisorctl start ` + process); err != nil { + if out, err := shell.Execf(`supervisorctl start %s`, process); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -148,7 +149,7 @@ func (r *SupervisorController) StartProcess(ctx http.Context) http.Response { // StopProcess 停止进程 func (r *SupervisorController) StopProcess(ctx http.Context) http.Response { process := ctx.Request().Input("process") - if out, err := tools.Exec(`supervisorctl stop ` + process); err != nil { + if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -158,7 +159,7 @@ func (r *SupervisorController) StopProcess(ctx http.Context) http.Response { // RestartProcess 重启进程 func (r *SupervisorController) RestartProcess(ctx http.Context) http.Response { process := ctx.Request().Input("process") - if out, err := tools.Exec(`supervisorctl restart ` + process); err != nil { + if out, err := shell.Execf(`supervisorctl restart %s`, process); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -171,16 +172,16 @@ func (r *SupervisorController) ProcessLog(ctx http.Context) http.Response { var logPath string var err error if tools.IsRHEL() { - logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) } else { - logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) } if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径") } - log, err := tools.Exec(`tail -n 200 ` + logPath) + log, err := shell.Execf(`tail -n 200 ` + logPath) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, log) } @@ -194,16 +195,16 @@ func (r *SupervisorController) ClearProcessLog(ctx http.Context) http.Response { var logPath string var err error if tools.IsRHEL() { - logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) } else { - logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) } if err != nil { - return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径") + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("无法从进程%s的配置文件中获取日志路径", process)) } - if out, err := tools.Exec(`echo "" > ` + logPath); err != nil { + if out, err := shell.Execf(`echo "" > ` + logPath); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } @@ -243,9 +244,9 @@ func (r *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error()) } - _, _ = tools.Exec(`supervisorctl reread`) - _, _ = tools.Exec(`supervisorctl update`) - _, _ = tools.Exec(`supervisorctl restart ` + process) + _, _ = shell.Execf(`supervisorctl reread`) + _, _ = shell.Execf(`supervisorctl update`) + _, _ = shell.Execf(`supervisorctl restart %s`, process) return controllers.Success(ctx, nil) } @@ -291,9 +292,9 @@ stdout_logfile_maxbytes=2MB return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error()) } - _, _ = tools.Exec(`supervisorctl reread`) - _, _ = tools.Exec(`supervisorctl update`) - _, _ = tools.Exec(`supervisorctl start ` + name) + _, _ = shell.Execf(`supervisorctl reread`) + _, _ = shell.Execf(`supervisorctl update`) + _, _ = shell.Execf(`supervisorctl start %s`, name) return controllers.Success(ctx, nil) } @@ -301,19 +302,19 @@ stdout_logfile_maxbytes=2MB // DeleteProcess 删除进程 func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response { process := ctx.Request().Input("process") - if out, err := tools.Exec(`supervisorctl stop ` + process); err != nil { + if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, out) } var logPath string var err error if tools.IsRHEL() { - logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) if err := tools.Remove(`/etc/supervisord.d/` + process + `.conf`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } } else { - logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`) + logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process) if err := tools.Remove(`/etc/supervisor/conf.d/` + process + `.conf`); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } @@ -326,8 +327,8 @@ func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response { if err := tools.Remove(logPath); err != nil { return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) } - _, _ = tools.Exec(`supervisorctl reread`) - _, _ = tools.Exec(`supervisorctl update`) + _, _ = shell.Execf(`supervisorctl reread`) + _, _ = shell.Execf(`supervisorctl update`) return controllers.Success(ctx, nil) } diff --git a/app/http/controllers/plugins/toolbox_controller.go b/app/http/controllers/plugins/toolbox_controller.go index f040bcd0..c2938c6b 100644 --- a/app/http/controllers/plugins/toolbox_controller.go +++ b/app/http/controllers/plugins/toolbox_controller.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/app/http/controllers" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -74,7 +75,7 @@ func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response { total = "0.00 B" } - raw, err := tools.Exec("free | grep Swap") + raw, err := shell.Execf("free | grep Swap") if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 SWAP 信息失败") } @@ -98,19 +99,19 @@ func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response { size := ctx.Request().InputInt("size") if tools.Exists("/www/swap") { - if out, err := tools.Exec("swapoff /www/swap"); err != nil { + if out, err := shell.Execf("swapoff /www/swap"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } - if out, err := tools.Exec("rm -f /www/swap"); err != nil { + if out, err := shell.Execf("rm -f /www/swap"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } - if out, err := tools.Exec("sed -i '/www\\/swap/d' /etc/fstab"); err != nil { + if out, err := shell.Execf("sed -i '/www\\/swap/d' /etc/fstab"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } } if size > 1 { - free, err := tools.Exec("df -k /www | awk '{print $4}' | tail -n 1") + free, err := shell.Execf("df -k /www | awk '{print $4}' | tail -n 1") if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取磁盘空间失败") } @@ -118,26 +119,26 @@ func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "磁盘空间不足,当前剩余 "+tools.FormatBytes(cast.ToFloat64(free))) } - btrfsCheck, _ := tools.Exec("df -T /www | awk '{print $2}' | tail -n 1") + btrfsCheck, _ := shell.Execf("df -T /www | awk '{print $2}' | tail -n 1") if strings.Contains(btrfsCheck, "btrfs") { - if out, err := tools.Exec("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap"); err != nil { + if out, err := shell.Execf("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } } else { - if out, err := tools.Exec("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size)); err != nil { + if out, err := shell.Execf("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size)); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } - if out, err := tools.Exec("mkswap -f /www/swap"); err != nil { + if out, err := shell.Execf("mkswap -f /www/swap"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } if err := tools.Chmod("/www/swap", 0600); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "设置 SWAP 权限失败") } } - if out, err := tools.Exec("swapon /www/swap"); err != nil { + if out, err := shell.Execf("swapon /www/swap"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } - if out, err := tools.Exec("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab"); err != nil { + if out, err := shell.Execf("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab"); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } } @@ -147,7 +148,7 @@ func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response { // GetTimezone 获取时区 func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response { - raw, err := tools.Exec("timedatectl | grep zone") + raw, err := shell.Execf("timedatectl | grep zone") if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区信息失败") } @@ -162,7 +163,7 @@ func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response { Value string `json:"value"` } - zonesRaw, err := tools.Exec("timedatectl list-timezones") + zonesRaw, err := shell.Execf("timedatectl list-timezones") if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区列表失败") } @@ -189,7 +190,7 @@ func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "时区不能为空") } - if out, err := tools.Exec("timedatectl set-timezone " + timezone); err != nil { + if out, err := shell.Execf("timedatectl set-timezone %s", timezone); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } @@ -231,7 +232,7 @@ func (r *ToolBoxController) SetRootPassword(ctx http.Context) http.Response { } password = strings.ReplaceAll(password, `'`, `\'`) - if out, err := tools.Exec(`yes '` + password + `' | passwd root`); err != nil { + if out, err := shell.Execf(`yes '` + password + `' | passwd root`); err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, out) } diff --git a/app/http/controllers/safe_controller.go b/app/http/controllers/safe_controller.go index 5a2e4b44..e40e8194 100644 --- a/app/http/controllers/safe_controller.go +++ b/app/http/controllers/safe_controller.go @@ -7,6 +7,7 @@ import ( "github.com/goravel/framework/contracts/http" "github.com/spf13/cast" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -42,7 +43,7 @@ func (r *SafeController) SetFirewallStatus(ctx http.Context) http.Response { err = tools.ServiceEnable("firewalld") } } else { - _, err = tools.Exec("echo y | ufw enable") + _, err = shell.Execf("echo y | ufw enable") if err == nil { err = tools.ServiceStart("ufw") } @@ -57,7 +58,7 @@ func (r *SafeController) SetFirewallStatus(ctx http.Context) http.Response { err = tools.ServiceDisable("firewalld") } } else { - _, err = tools.Exec("ufw disable") + _, err = shell.Execf("ufw disable") if err == nil { err = tools.ServiceStop("ufw") } @@ -82,7 +83,7 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response { var rules []map[string]string if tools.IsRHEL() { - out, err := tools.Exec("firewall-cmd --list-all 2>&1") + out, err := shell.Execf("firewall-cmd --list-all") if err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -103,7 +104,7 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response { }) } } else { - out, err := tools.Exec("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'") + out, err := shell.Execf("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'") if err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -158,13 +159,13 @@ func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response { } if tools.IsRHEL() { - if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil { + if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --add-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil { + if out, err := shell.Execf("firewall-cmd --add-port=%s/%s --permanent", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --reload"); err != nil { + if out, err := shell.Execf("firewall-cmd --reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } else { @@ -172,13 +173,13 @@ func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response { if strings.Contains(port, "-") { port = strings.ReplaceAll(port, "-", ":") } - if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol); err != nil { + if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw allow " + cast.ToString(port) + "/" + protocol); err != nil { + if out, err := shell.Execf("ufw allow %s/%s", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw reload"); err != nil { + if out, err := shell.Execf("ufw reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } @@ -192,24 +193,24 @@ func (r *SafeController) DeleteFirewallRule(ctx http.Context) http.Response { return Error(ctx, http.StatusUnprocessableEntity, "防火墙未启动") } - port := ctx.Request().InputInt("port", 0) - protocol := ctx.Request().Input("protocol", "") - if port == 0 || protocol == "" { + port := ctx.Request().Input("port") + protocol := ctx.Request().Input("protocol") + if port == "" || protocol == "" { return Error(ctx, http.StatusUnprocessableEntity, "参数错误") } if tools.IsRHEL() { - if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil { + if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --reload"); err != nil { + if out, err := shell.Execf("firewall-cmd --reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } else { - if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol); err != nil { + if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw reload"); err != nil { + if out, err := shell.Execf("ufw reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } @@ -262,7 +263,7 @@ func (r *SafeController) SetSshStatus(ctx http.Context) http.Response { // GetSshPort 获取 SSH 端口 func (r *SafeController) GetSshPort(ctx http.Context) http.Response { - out, err := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") + out, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") if err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -277,12 +278,12 @@ func (r *SafeController) SetSshPort(ctx http.Context) http.Response { return Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - oldPort, err := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") + oldPort, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'") if err != nil { return Error(ctx, http.StatusInternalServerError, oldPort) } - _, _ = tools.Exec("sed -i 's/#Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config") - _, _ = tools.Exec("sed -i 's/Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config") + _, _ = shell.Execf("sed -i 's/#Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port) + _, _ = shell.Execf("sed -i 's/Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port) status, _ := tools.ServiceStatus(r.ssh) if status { @@ -295,7 +296,7 @@ func (r *SafeController) SetSshPort(ctx http.Context) http.Response { // GetPingStatus 获取 Ping 状态 func (r *SafeController) GetPingStatus(ctx http.Context) http.Response { if tools.IsRHEL() { - out, err := tools.Exec(`firewall-cmd --list-all 2>&1`) + out, err := shell.Execf(`firewall-cmd --list-all`) if err != nil { return Error(ctx, http.StatusInternalServerError, out) } @@ -324,15 +325,15 @@ func (r *SafeController) SetPingStatus(ctx http.Context) http.Response { var err error if tools.IsRHEL() { if ctx.Request().InputBool("status") { - out, err = tools.Exec(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`) + out, err = shell.Execf(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`) } else { - out, err = tools.Exec(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`) + out, err = shell.Execf(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`) } } else { if ctx.Request().InputBool("status") { - out, err = tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`) + out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`) } else { - out, err = tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`) + out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`) } } @@ -341,9 +342,9 @@ func (r *SafeController) SetPingStatus(ctx http.Context) http.Response { } if tools.IsRHEL() { - out, err = tools.Exec(`firewall-cmd --reload`) + out, err = shell.Execf(`firewall-cmd --reload`) } else { - out, err = tools.Exec(`ufw reload`) + out, err = shell.Execf(`ufw reload`) } if err != nil { diff --git a/app/http/controllers/setting_controller.go b/app/http/controllers/setting_controller.go index b8f147b3..9ea6536d 100644 --- a/app/http/controllers/setting_controller.go +++ b/app/http/controllers/setting_controller.go @@ -9,6 +9,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" "github.com/TheTNB/panel/internal/services" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -50,7 +51,7 @@ func (r *SettingController) List(ctx http.Context) http.Response { return ErrorSystem(ctx) } - port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) + port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{ "error": err.Error(), @@ -147,7 +148,7 @@ func (r *SettingController) Update(ctx http.Context) http.Response { return ErrorSystem(ctx) } - oldPort, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) + oldPort, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{ "error": err.Error(), @@ -157,33 +158,33 @@ func (r *SettingController) Update(ctx http.Context) http.Response { port := cast.ToString(updateRequest.Port) if oldPort != port { - if out, err := tools.Exec("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf"); err != nil { + if out, err := shell.Execf("sed -i 's/APP_PORT=%s/APP_PORT=%s/g' /www/panel/panel.conf", oldPort, port); err != nil { return Error(ctx, http.StatusInternalServerError, out) } if tools.IsRHEL() { - if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/tcp --permanent 2>&1"); err != nil { + if out, err := shell.Execf("firewall-cmd --remove-port=%s/tcp --permanent", oldPort); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --add-port=" + cast.ToString(port) + "/tcp --permanent 2>&1"); err != nil { + if out, err := shell.Execf("firewall-cmd --add-port=%s/tcp --permanent", port); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("firewall-cmd --reload"); err != nil { + if out, err := shell.Execf("firewall-cmd --reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } else { - if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/tcp"); err != nil { + if out, err := shell.Execf("ufw delete allow %s/tcp", oldPort); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw allow " + cast.ToString(port) + "/tcp"); err != nil { + if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil { return Error(ctx, http.StatusInternalServerError, out) } - if out, err := tools.Exec("ufw reload"); err != nil { + if out, err := shell.Execf("ufw reload"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } } - oldEntrance, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`) + oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{ "error": err.Error(), @@ -192,12 +193,12 @@ func (r *SettingController) Update(ctx http.Context) http.Response { } entrance := cast.ToString(updateRequest.Entrance) if oldEntrance != entrance { - if out, err := tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=" + entrance + "!g' /www/panel/panel.conf"); err != nil { + if out, err := shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=" + entrance + "!g' /www/panel/panel.conf"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } - oldLanguage, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_LOCALE | awk -F '=' '{print $2}' | tr -d '\n'`) + oldLanguage, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_LOCALE | awk -F '=' '{print $2}' | tr -d '\n'`) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{ "error": err.Error(), @@ -205,17 +206,17 @@ func (r *SettingController) Update(ctx http.Context) http.Response { return ErrorSystem(ctx) } if oldLanguage != updateRequest.Language { - if out, err := tools.Exec("sed -i 's/APP_LOCALE=" + oldLanguage + "/APP_LOCALE=" + updateRequest.Language + "/g' /www/panel/panel.conf"); err != nil { + if out, err := shell.Execf("sed -i 's/APP_LOCALE=" + oldLanguage + "/APP_LOCALE=" + updateRequest.Language + "/g' /www/panel/panel.conf"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } if updateRequest.SSL { - if out, err := tools.Exec("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil { + if out, err := shell.Execf("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } else { - if out, err := tools.Exec("sed -i 's/APP_SSL=true/APP_SSL=false/g' /www/panel/panel.conf"); err != nil { + if out, err := shell.Execf("sed -i 's/APP_SSL=true/APP_SSL=false/g' /www/panel/panel.conf"); err != nil { return Error(ctx, http.StatusInternalServerError, out) } } diff --git a/app/http/controllers/task_controller.go b/app/http/controllers/task_controller.go index 233c23d6..e9d72ed3 100644 --- a/app/http/controllers/task_controller.go +++ b/app/http/controllers/task_controller.go @@ -5,7 +5,7 @@ import ( "github.com/goravel/framework/facades" "github.com/TheTNB/panel/app/models" - "github.com/TheTNB/panel/pkg/tools" + "github.com/TheTNB/panel/pkg/shell" ) type TaskController struct { @@ -63,7 +63,7 @@ func (r *TaskController) Log(ctx http.Context) http.Response { return ErrorSystem(ctx) } - log, err := tools.Exec(`tail -n 500 '` + task.Log + `'`) + log, err := shell.Execf(`tail -n 500 '` + task.Log + `'`) if err != nil { return Error(ctx, http.StatusInternalServerError, "日志已被清理") } diff --git a/app/jobs/process_task.go b/app/jobs/process_task.go index c25d4042..60acfb37 100644 --- a/app/jobs/process_task.go +++ b/app/jobs/process_task.go @@ -1,10 +1,10 @@ package jobs import ( - "github.com/TheTNB/panel/pkg/tools" "github.com/goravel/framework/facades" "github.com/TheTNB/panel/app/models" + "github.com/TheTNB/panel/pkg/shell" ) // ProcessTask 处理面板任务 @@ -48,7 +48,7 @@ func (receiver *ProcessTask) Handle(args ...any) error { "task_id": taskID, }).Infof("开始执行任务") - if _, err := tools.Exec(task.Shell); err != nil { + if _, err := shell.Execf(task.Shell); err != nil { task.Status = models.TaskStatusFailed if err := facades.Orm().Query().Save(&task); err != nil { facades.Log().Tags("面板", "异步任务").With(map[string]any{ diff --git a/internal/services/backup.go b/internal/services/backup.go index eb6f2ad9..8a65fa19 100644 --- a/internal/services/backup.go +++ b/internal/services/backup.go @@ -11,6 +11,7 @@ import ( "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -73,7 +74,7 @@ func (s *BackupImpl) WebSiteBackup(website models.Website) error { } backupFile := backupPath + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip" - if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil { + if _, err := shell.Execf(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil { return err } @@ -163,10 +164,10 @@ func (s *BackupImpl) MysqlBackup(database string) error { return err } - if _, err := tools.Exec("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil { + if _, err := shell.Execf("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil { return err } - if _, err := tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil { + if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil { return err } if err := tools.Remove(backupPath + "/" + backupFile); err != nil { @@ -217,7 +218,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error { return errors.New("无法找到备份文件") } - if _, err = tools.Exec("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil { + if _, err = shell.Execf("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil { return err } @@ -271,10 +272,10 @@ func (s *BackupImpl) PostgresqlBackup(database string) error { } } - if _, err := tools.Exec(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile); err != nil { + if _, err := shell.Execf(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile); err != nil { return err } - if _, err := tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil { + if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil { return err } @@ -317,7 +318,7 @@ func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error return errors.New("无法找到备份文件") } - if _, err = tools.Exec(`su - postgres -c "psql ` + database + `" < ` + filepath.Join(tempDir, backupFile)); err != nil { + if _, err = shell.Execf(`su - postgres -c "psql ` + database + `" < ` + filepath.Join(tempDir, backupFile)); err != nil { return err } diff --git a/internal/services/cron.go b/internal/services/cron.go index a6aad70d..b34e4b2c 100644 --- a/internal/services/cron.go +++ b/internal/services/cron.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/TheTNB/panel/app/models" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" ) @@ -19,14 +20,14 @@ func NewCronImpl() *CronImpl { // AddToSystem 添加到系统 func (r *CronImpl) AddToSystem(cron models.Cron) error { if tools.IsRHEL() { - if _, err := tools.Exec(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/root`, cron.Time, cron.Shell, cron.Log)); err != nil { + if _, err := shell.Execf(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/root`, cron.Time, cron.Shell, cron.Log)); err != nil { return err } return tools.ServiceRestart("crond") } if tools.IsDebian() { - if _, err := tools.Exec(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/crontabs/root`, cron.Time, cron.Shell, cron.Log)); err != nil { + if _, err := shell.Execf(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/crontabs/root`, cron.Time, cron.Shell, cron.Log)); err != nil { return err } return tools.ServiceRestart("cron") @@ -40,14 +41,14 @@ func (r *CronImpl) DeleteFromSystem(cron models.Cron) error { // 需要转义 shell 路径的/为\/ cron.Shell = strings.ReplaceAll(cron.Shell, "/", "\\/") if tools.IsRHEL() { - if _, err := tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root"); err != nil { + if _, err := shell.Execf("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root"); err != nil { return err } return tools.ServiceRestart("crond") } if tools.IsDebian() { - if _, err := tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root"); err != nil { + if _, err := shell.Execf("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root"); err != nil { return err } return tools.ServiceRestart("cron") diff --git a/internal/services/php.go b/internal/services/php.go index b8e9993e..dec30d06 100644 --- a/internal/services/php.go +++ b/internal/services/php.go @@ -13,6 +13,7 @@ import ( "github.com/spf13/cast" "github.com/TheTNB/panel/app/models" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -82,15 +83,15 @@ func (r *PHPImpl) Load() ([]types.NV, error) { } func (r *PHPImpl) GetErrorLog() (string, error) { - return tools.Exec("tail -n 500 /www/server/php/" + r.version + "/var/log/php-fpm.log") + return shell.Execf("tail -n 500 /www/server/php/%s/var/log/php-fpm.log", r.version) } func (r *PHPImpl) GetSlowLog() (string, error) { - return tools.Exec("tail -n 500 /www/server/php/" + r.version + "/var/log/slow.log") + return shell.Execf("tail -n 500 /www/server/php/%s/var/log/slow.log", r.version) } func (r *PHPImpl) ClearErrorLog() error { - if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil { + if out, err := shell.Execf("echo '' > /www/server/php/%s/var/log/php-fpm.log", r.version); err != nil { return errors.New(out) } @@ -98,7 +99,7 @@ func (r *PHPImpl) ClearErrorLog() error { } func (r *PHPImpl) ClearSlowLog() error { - if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil { + if out, err := shell.Execf("echo '' > /www/server/php/%s/var/log/slow.log", r.version); err != nil { return errors.New(out) } @@ -270,7 +271,7 @@ func (r *PHPImpl) GetExtensions() ([]types.PHPExtension, error) { }) } - raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") + raw, err := shell.Execf("/www/server/php/%s/bin/php -m", r.version) if err != nil { return extensions, err } diff --git a/internal/services/website.go b/internal/services/website.go index 0daf9cb1..10c40e78 100644 --- a/internal/services/website.go +++ b/internal/services/website.go @@ -16,6 +16,7 @@ import ( requests "github.com/TheTNB/panel/app/http/requests/website" "github.com/TheTNB/panel/app/models" "github.com/TheTNB/panel/internal" + "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) @@ -285,18 +286,18 @@ server rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if website.Db && website.DbType == "mysql" { - _, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`) - _, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`) - _, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`) - _, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`) + _, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`) + _, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`) + _, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`) + _, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`) } if website.Db && website.DbType == "postgresql" { - _, _ = tools.Exec(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`) - _, _ = tools.Exec(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`) - _, _ = tools.Exec(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`) - _, _ = tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`) + _, _ = shell.Execf(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`) + _, _ = shell.Execf(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`) + _, _ = shell.Execf(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`) + _, _ = shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`) userConfig := "host " + website.DbName + " " + website.DbUser + " 127.0.0.1/32 scram-sha-256" - _, _ = tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) + _, _ = shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) _ = tools.ServiceReload("postgresql") } @@ -659,7 +660,7 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) { rewrite, _ := tools.Read("/www/server/vhost/rewrite/" + website.Name + ".conf") setting.Rewrite = rewrite - log, _ := tools.Exec(`tail -n 100 '/www/wwwlogs/` + website.Name + `.log'`) + log, _ := shell.Execf(`tail -n 100 '/www/wwwlogs/%s.log'`, website.Name) setting.Log = log return setting, err diff --git a/pkg/shell/exec.go b/pkg/shell/exec.go new file mode 100644 index 00000000..82f94e3d --- /dev/null +++ b/pkg/shell/exec.go @@ -0,0 +1,119 @@ +package shell + +import ( + "bytes" + "errors" + "fmt" + "os" + "os/exec" + "strings" + "time" + + "github.com/TheTNB/panel/pkg/slice" + "github.com/goravel/framework/support" +) + +// Execf 执行 shell 命令 +func Execf(shell string, args ...any) (string, error) { + if !CheckArgs(slice.ToString(args)...) { + return "", errors.New("你想干什么?") + } + + var cmd *exec.Cmd + _ = os.Setenv("LC_ALL", "C") + cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...)) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + return "", errors.New(strings.TrimSpace(stderr.String())) + } + + return strings.TrimSpace(stdout.String()), err +} + +// ExecfAsync 异步执行 shell 命令 +func ExecfAsync(shell string, args ...any) error { + if !CheckArgs(slice.ToString(args)...) { + return errors.New("你想干什么?") + } + + var cmd *exec.Cmd + _ = os.Setenv("LC_ALL", "C") + cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...)) + + err := cmd.Start() + if err != nil { + return err + } + + go func() { + err := cmd.Wait() + if err != nil { + if support.Env == support.EnvTest { + fmt.Println(err.Error()) + panic(err) + } + } + }() + + return nil +} + +// ExecfWithTimeout 执行 shell 命令并设置超时时间 +func ExecfWithTimeout(timeout time.Duration, shell string, args ...any) (string, error) { + if !CheckArgs(slice.ToString(args)...) { + return "", errors.New("你想干什么?") + } + + var cmd *exec.Cmd + _ = os.Setenv("LC_ALL", "C") + cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...)) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Start() + if err != nil { + return "", err + } + + done := make(chan error) + go func() { + done <- cmd.Wait() + }() + + select { + case <-time.After(timeout): + _ = cmd.Process.Kill() + return "", errors.New("执行超时") + case err = <-done: + if err != nil { + return "", errors.New(strings.TrimSpace(stderr.String())) + } + } + + return strings.TrimSpace(stdout.String()), err +} + +// CheckArgs 检查危险的参数 +func CheckArgs(args ...string) bool { + if len(args) == 0 { + return true + } + + dangerous := []string{"&", "|", ";", "$", "'", `"`, "(", ")", "`", "\n", "\r", ">", "<", "{", "}", "[", "]", "\\"} + for _, arg := range args { + for _, char := range dangerous { + if strings.Contains(arg, char) { + return false + } + } + } + + return true +} diff --git a/pkg/slice/slice.go b/pkg/slice/slice.go new file mode 100644 index 00000000..cd4d4a11 --- /dev/null +++ b/pkg/slice/slice.go @@ -0,0 +1,30 @@ +package slice + +import "github.com/spf13/cast" + +// ToAny 将任意类型切片转换为 []any +func ToAny[T any](s []T) []any { + result := make([]any, len(s)) + for i, v := range s { + result[i] = v + } + return result +} + +// ToString 将任意类型切片转换为 []string +func ToString[T any](s []T) []string { + result := make([]string, len(s)) + for i, v := range s { + result[i] = cast.ToString(v) + } + return result +} + +// ToInt 将任意类型切片转换为 []int +func ToInt[T any](s []T) []int { + result := make([]int, len(s)) + for i, v := range s { + result[i] = cast.ToInt(v) + } + return result +} diff --git a/pkg/tools/service.go b/pkg/tools/service.go index 9d4797c0..3af2ef57 100644 --- a/pkg/tools/service.go +++ b/pkg/tools/service.go @@ -2,14 +2,15 @@ package tools import ( "errors" - "fmt" "os/exec" "strings" + + "github.com/TheTNB/panel/pkg/shell" ) // ServiceStatus 获取服务状态 func ServiceStatus(name string) (bool, error) { - output, err := Exec(fmt.Sprintf("systemctl status %s | grep Active | grep -v grep | awk '{print $2}'", name)) + output, err := shell.Execf("systemctl status %s | grep Active | grep -v grep | awk '{print $2}'", name) return output == "active", err } @@ -37,36 +38,36 @@ func ServiceIsEnabled(name string) (bool, error) { // ServiceStart 启动服务 func ServiceStart(name string) error { - _, err := Exec(fmt.Sprintf("systemctl start %s", name)) + _, err := shell.Execf("systemctl start %s", name) return err } // ServiceStop 停止服务 func ServiceStop(name string) error { - _, err := Exec(fmt.Sprintf("systemctl stop %s", name)) + _, err := shell.Execf("systemctl stop %s", name) return err } // ServiceRestart 重启服务 func ServiceRestart(name string) error { - _, err := Exec(fmt.Sprintf("systemctl restart %s", name)) + _, err := shell.Execf("systemctl restart %s", name) return err } // ServiceReload 重载服务 func ServiceReload(name string) error { - _, err := Exec(fmt.Sprintf("systemctl reload %s", name)) + _, err := shell.Execf("systemctl reload %s", name) return err } // ServiceEnable 启用服务 func ServiceEnable(name string) error { - _, err := Exec(fmt.Sprintf("systemctl enable %s", name)) + _, err := shell.Execf("systemctl enable %s", name) return err } // ServiceDisable 禁用服务 func ServiceDisable(name string) error { - _, err := Exec(fmt.Sprintf("systemctl disable %s", name)) + _, err := shell.Execf("systemctl disable %s", name) return err } diff --git a/pkg/tools/system.go b/pkg/tools/system.go index 1f8b5c2a..c359d750 100644 --- a/pkg/tools/system.go +++ b/pkg/tools/system.go @@ -1,7 +1,6 @@ package tools import ( - "bytes" "errors" "fmt" "io" @@ -11,7 +10,6 @@ import ( "path/filepath" "strings" - "github.com/goravel/framework/support" "github.com/goravel/framework/support/env" "github.com/mholt/archiver/v3" "github.com/spf13/cast" @@ -58,54 +56,6 @@ func Remove(path string) error { return os.RemoveAll(path) } -// Exec 执行 shell 命令 -func Exec(shell string) (string, error) { - var cmd *exec.Cmd - if env.IsLinux() { - cmd = exec.Command("bash", "-c", "LC_ALL=C "+shell) - } else { - cmd = exec.Command("cmd", "/C", "chcp 65001 >nul && "+shell) - } - - var stdoutBuf, stderrBuf bytes.Buffer - cmd.Stdout = &stdoutBuf - cmd.Stderr = &stderrBuf - - err := cmd.Run() - if err != nil { - return "", errors.New(strings.TrimSpace(stderrBuf.String())) - } - - return strings.TrimSpace(stdoutBuf.String()), err -} - -// ExecAsync 异步执行 shell 命令 -func ExecAsync(shell string) error { - var cmd *exec.Cmd - if env.IsLinux() { - cmd = exec.Command("bash", "-c", "LC_ALL=C "+shell) - } else { - cmd = exec.Command("cmd", "/C", "chcp 65001 >nul && "+shell) - } - - err := cmd.Start() - if err != nil { - return err - } - - go func() { - err := cmd.Wait() - if err != nil { - if support.Env == support.EnvTest { - fmt.Println(err.Error()) - panic(err) - } - } - }() - - return nil -} - // Mkdir 创建目录 func Mkdir(path string, permission os.FileMode) error { return os.MkdirAll(path, permission) diff --git a/pkg/tools/tools.go b/pkg/tools/tools.go index 5375dfd8..77dbceb0 100644 --- a/pkg/tools/tools.go +++ b/pkg/tools/tools.go @@ -4,7 +4,6 @@ package tools import ( "errors" "fmt" - "github.com/spf13/cast" "os" "strings" "time" @@ -19,6 +18,9 @@ import ( "github.com/shirou/gopsutil/load" "github.com/shirou/gopsutil/mem" "github.com/shirou/gopsutil/net" + "github.com/spf13/cast" + + "github.com/TheTNB/panel/pkg/shell" ) // MonitoringInfo 监控信息 @@ -159,9 +161,9 @@ func GetLatestPanelVersion() (PanelInfo, error) { isChina := IsChina() if isChina { - output, err = Exec(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/permalink/latest"`) + output, err = shell.Execf(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/permalink/latest"`) } else { - output, err = Exec(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/latest"`) + output, err = shell.Execf(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/latest"`) } if len(output) == 0 || err != nil { @@ -185,70 +187,70 @@ func GetLatestPanelVersion() (PanelInfo, error) { var name, version, body, date, downloadName, downloadUrl, checksums, checksumsUrl string if isChina { - if name, err = Exec("jq -r '.name' " + fileName); err != nil { + if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if version, err = Exec("jq -r '.tag_name' " + fileName); err != nil { + if version, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if body, err = Exec("jq -r '.description' " + fileName); err != nil { + if body, err = shell.Execf("jq -r '.description' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if date, err = Exec("jq -r '.created_at' " + fileName); err != nil { + if date, err = shell.Execf("jq -r '.created_at' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if checksums, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { + if checksums, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if checksumsUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil { + if checksumsUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } if env.IsArm() { - if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } } else { - if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } } } else { - if name, err = Exec("jq -r '.name' " + fileName); err != nil { + if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if version, err = Exec("jq -r '.tag_name' " + fileName); err != nil { + if version, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if body, err = Exec("jq -r '.body' " + fileName); err != nil { + if body, err = shell.Execf("jq -r '.body' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if date, err = Exec("jq -r '.published_at' " + fileName); err != nil { + if date, err = shell.Execf("jq -r '.published_at' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if checksums, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { + if checksums, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if checksumsUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil { + if checksumsUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } if env.IsArm() { - if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } } else { - if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } - if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取最新版本失败") } } @@ -278,9 +280,9 @@ func GetPanelVersion(version string) (PanelInfo, error) { } if isChina { - output, err = Exec(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/` + version + `"`) + output, err = shell.Execf(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/` + version + `"`) } else { - output, err = Exec(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/tags/` + version + `"`) + output, err = shell.Execf(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/tags/` + version + `"`) } if len(output) == 0 || err != nil { @@ -304,70 +306,70 @@ func GetPanelVersion(version string) (PanelInfo, error) { var name, version2, body, date, downloadName, downloadUrl, checksums, checksumsUrl string if isChina { - if name, err = Exec("jq -r '.name' " + fileName); err != nil { + if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if version2, err = Exec("jq -r '.tag_name' " + fileName); err != nil { + if version2, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if body, err = Exec("jq -r '.description' " + fileName); err != nil { + if body, err = shell.Execf("jq -r '.description' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if date, err = Exec("jq -r '.created_at' " + fileName); err != nil { + if date, err = shell.Execf("jq -r '.created_at' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if checksums, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { + if checksums, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if checksumsUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil { + if checksumsUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } if env.IsArm() { - if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } } else { - if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } } } else { - if name, err = Exec("jq -r '.name' " + fileName); err != nil { + if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if version2, err = Exec("jq -r '.tag_name' " + fileName); err != nil { + if version2, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if body, err = Exec("jq -r '.body' " + fileName); err != nil { + if body, err = shell.Execf("jq -r '.body' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if date, err = Exec("jq -r '.published_at' " + fileName); err != nil { + if date, err = shell.Execf("jq -r '.published_at' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if checksums, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { + if checksums, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if checksumsUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil { + if checksumsUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } if env.IsArm() { - if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } } else { - if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { + if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } - if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil { + if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil { return info, errors.New("获取面板版本失败") } } @@ -401,11 +403,11 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Red().Printfln("备份面板失败") return err } - if _, err := Exec("cd /www/panel/storage && zip -r /tmp/panel-storage.zip *"); err != nil { + if _, err := shell.Execf("cd /www/panel/storage && zip -r /tmp/panel-storage.zip *"); err != nil { color.Red().Printfln("备份面板数据失败") return err } - if _, err := Exec("cp -f /www/panel/panel.conf /tmp/panel.conf.bak"); err != nil { + if _, err := shell.Execf("cp -f /www/panel/panel.conf /tmp/panel.conf.bak"); err != nil { color.Red().Printfln("备份面板配置失败") return err } @@ -415,18 +417,18 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Green().Printfln("备份完成") color.Green().Printfln("清理旧版本...") - if _, err := Exec("rm -rf /www/panel/*"); err != nil { + if _, err := shell.Execf("rm -rf /www/panel/*"); err != nil { color.Red().Printfln("清理旧版本失败") return err } color.Green().Printfln("清理完成") color.Green().Printfln("正在下载...") - if _, err := Exec("wget -T 120 -t 3 -O /www/panel/" + panelInfo.DownloadName + " " + panelInfo.DownloadUrl); err != nil { + if _, err := shell.Execf("wget -T 120 -t 3 -O /www/panel/" + panelInfo.DownloadName + " " + panelInfo.DownloadUrl); err != nil { color.Red().Printfln("下载失败") return err } - if _, err := Exec("wget -T 20 -t 3 -O /www/panel/" + panelInfo.Checksums + " " + panelInfo.ChecksumsUrl); err != nil { + if _, err := shell.Execf("wget -T 20 -t 3 -O /www/panel/" + panelInfo.Checksums + " " + panelInfo.ChecksumsUrl); err != nil { color.Red().Printfln("下载失败") return err } @@ -436,7 +438,7 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Green().Printfln("下载完成") color.Green().Printfln("校验下载文件...") - check, err := Exec("cd /www/panel && sha256sum -c " + panelInfo.Checksums + " --ignore-missing") + check, err := shell.Execf("cd /www/panel && sha256sum -c " + panelInfo.Checksums + " --ignore-missing") if check != panelInfo.DownloadName+": OK" || err != nil { return errors.New("下载文件校验失败") } @@ -447,7 +449,7 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Green().Printfln("文件校验完成") color.Green().Printfln("更新新版本...") - if _, err = Exec("cd /www/panel && unzip -o " + panelInfo.DownloadName + " && rm -rf " + panelInfo.DownloadName); err != nil { + if _, err = shell.Execf("cd /www/panel && unzip -o " + panelInfo.DownloadName + " && rm -rf " + panelInfo.DownloadName); err != nil { color.Red().Printfln("更新失败") return err } @@ -457,15 +459,15 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Green().Printfln("更新完成") color.Green().Printfln("恢复面板数据...") - if _, err = Exec("cp -f /tmp/panel-storage.zip /www/panel/storage/panel-storage.zip && cd /www/panel/storage && unzip -o panel-storage.zip && rm -rf panel-storage.zip"); err != nil { + if _, err = shell.Execf("cp -f /tmp/panel-storage.zip /www/panel/storage/panel-storage.zip && cd /www/panel/storage && unzip -o panel-storage.zip && rm -rf panel-storage.zip"); err != nil { color.Red().Printfln("恢复面板数据失败") return err } - if _, err = Exec("cp -f /tmp/panel.conf.bak /www/panel/panel.conf"); err != nil { + if _, err = shell.Execf("cp -f /tmp/panel.conf.bak /www/panel/panel.conf"); err != nil { color.Red().Printfln("恢复面板配置失败") return err } - if _, err = Exec("cp -f /www/panel/scripts/panel.sh /usr/bin/panel"); err != nil { + if _, err = shell.Execf("cp -f /www/panel/scripts/panel.sh /usr/bin/panel"); err != nil { color.Red().Printfln("恢复面板脚本失败") return err } @@ -475,28 +477,28 @@ func UpdatePanel(panelInfo PanelInfo) error { color.Green().Printfln("恢复完成") color.Green().Printfln("设置面板文件权限...") - _, _ = Exec("chmod -R 700 /www/panel") - _, _ = Exec("chmod -R 700 /usr/bin/panel") + _, _ = shell.Execf("chmod -R 700 /www/panel") + _, _ = shell.Execf("chmod -R 700 /usr/bin/panel") color.Green().Printfln("设置完成") - if _, err = Exec("bash /www/panel/scripts/update_panel.sh"); err != nil { + if _, err = shell.Execf("bash /www/panel/scripts/update_panel.sh"); err != nil { color.Red().Printfln("执行面板升级后脚本失败") return err } - if _, err = Exec("panel writeSetting version " + panelInfo.Version); err != nil { + if _, err = shell.Execf("panel writeSetting version " + panelInfo.Version); err != nil { color.Red().Printfln("写入面板版本号失败") return err } - _, _ = Exec("rm -rf /tmp/panel-storage.zip") - _, _ = Exec("rm -rf /tmp/panel.conf.bak") + _, _ = shell.Execf("rm -rf /tmp/panel-storage.zip") + _, _ = shell.Execf("rm -rf /tmp/panel.conf.bak") return nil } func RestartPanel() { color.Green().Printfln("重启面板...") - err := ExecAsync("sleep 2 && systemctl restart panel") + err := shell.ExecfAsync("sleep 2 && systemctl restart panel") if err != nil { color.Red().Printfln("重启失败") return