mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-30 17:05:19 +08:00
refactor: 统一日志清理
This commit is contained in:
@@ -156,6 +156,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||
@@ -336,6 +338,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
|
||||
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
|
||||
@@ -40,9 +40,7 @@ func (s *App) Route(r chi.Router) {
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Post("/clear_log", s.ClearLog)
|
||||
r.Get("/slow_log", s.SlowLog)
|
||||
r.Post("/clear_slow_log", s.ClearSlowLog)
|
||||
r.Get("/root_password", s.GetRootPassword)
|
||||
r.Post("/root_password", s.SetRootPassword)
|
||||
r.Get("/config_tune", s.GetConfigTune)
|
||||
@@ -194,31 +192,11 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, load)
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if err := systemctl.LogClear("mysqld"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// SlowLog 获取慢查询日志
|
||||
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-slow.log", app.Root))
|
||||
}
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("cat /dev/null > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetRootPassword 获取root密码
|
||||
func (s *App) GetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
@@ -41,7 +41,6 @@ func (s *App) Route(r chi.Router) {
|
||||
r.Post("/user_config", s.UpdateUserConfig)
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/log", s.Log)
|
||||
r.Post("/clear_log", s.ClearLog)
|
||||
r.Get("/postgres_password", s.GetPostgresPassword)
|
||||
r.Post("/postgres_password", s.SetPostgresPassword)
|
||||
r.Get("/config_tune", s.GetConfigTune)
|
||||
@@ -175,19 +174,15 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, data)
|
||||
}
|
||||
|
||||
// Log 获取日志
|
||||
// Log 获取应用日志路径列表
|
||||
func (s *App) Log(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly)))
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("rm -rf %s/server/postgresql/logs/postgresql-*.log", app.Root); err != nil {
|
||||
paths, err := filepath.Glob(fmt.Sprintf("%s/server/postgresql/logs/postgresql-*.log", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
service.Success(w, paths)
|
||||
}
|
||||
|
||||
// GetPostgresPassword 获取 postgres 用户密码
|
||||
|
||||
@@ -37,7 +37,6 @@ func NewApp(t *gotext.Locale) *App {
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/service", s.Service)
|
||||
r.Post("/clear_log", s.ClearLog)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Get("/processes", s.Processes)
|
||||
@@ -45,7 +44,6 @@ func (s *App) Route(r chi.Router) {
|
||||
r.Post("/processes/{process}/stop", s.StopProcess)
|
||||
r.Post("/processes/{process}/restart", s.RestartProcess)
|
||||
r.Get("/processes/{process}/log", s.ProcessLog)
|
||||
r.Post("/processes/{process}/clear_log", s.ClearProcessLog)
|
||||
r.Get("/processes/{process}", s.ProcessConfig)
|
||||
r.Post("/processes/{process}", s.UpdateProcessConfig)
|
||||
r.Delete("/processes/{process}", s.DeleteProcess)
|
||||
@@ -57,16 +55,6 @@ func (s *App) Service(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, s.name)
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf(`cat /dev/null > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var config string
|
||||
@@ -227,35 +215,6 @@ func (s *App) ProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, logPath)
|
||||
}
|
||||
|
||||
// ClearProcessLog 清空进程日志
|
||||
func (s *App) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
name := programName(req.Process)
|
||||
var logPath string
|
||||
if os.IsRHEL() {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, name)
|
||||
} else {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, name)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`cat /dev/null > '%s'`, logPath); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// ProcessConfig 获取进程配置
|
||||
func (s *App) ProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
|
||||
@@ -45,7 +45,6 @@ type WebsiteRepo interface {
|
||||
Create(ctx context.Context, req *request.WebsiteCreate) (*Website, error)
|
||||
Update(ctx context.Context, req *request.WebsiteUpdate) error
|
||||
Delete(ctx context.Context, req *request.WebsiteDelete) error
|
||||
ClearLog(id uint) error
|
||||
UpdateRemark(id uint, remark string) error
|
||||
ResetConfig(id uint) error
|
||||
UpdateStatus(id uint, status bool) error
|
||||
|
||||
@@ -828,16 +828,6 @@ func (r *websiteRepo) Delete(ctx context.Context, req *request.WebsiteDelete) er
|
||||
return r.reloadWebServer()
|
||||
}
|
||||
|
||||
func (r *websiteRepo) ClearLog(id uint) error {
|
||||
website := new(biz.Website)
|
||||
if err := r.db.Where("id", id).First(website).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := shell.Execf(`cat /dev/null > %s/sites/%s/log/access.log`, app.Root, website.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *websiteRepo) UpdateRemark(id uint, remark string) error {
|
||||
website := new(biz.Website)
|
||||
if err := r.db.Where("id", id).First(website).Error; err != nil {
|
||||
|
||||
@@ -264,7 +264,6 @@ func (route *Http) Register(r *chi.Mux) {
|
||||
r.Get("/{id}", route.website.Get)
|
||||
r.Put("/{id}", route.website.Update)
|
||||
r.Delete("/{id}", route.website.Delete)
|
||||
r.Delete("/{id}/log", route.website.ClearLog)
|
||||
r.Post("/{id}/update_remark", route.website.UpdateRemark)
|
||||
r.Post("/{id}/reset_config", route.website.ResetConfig)
|
||||
r.Post("/{id}/status", route.website.UpdateStatus)
|
||||
@@ -432,8 +431,6 @@ func (route *Http) Register(r *chi.Mux) {
|
||||
r.Get("/{version}/load", route.environmentPHP.Load)
|
||||
r.Get("/{version}/log", route.environmentPHP.Log)
|
||||
r.Get("/{version}/slow_log", route.environmentPHP.SlowLog)
|
||||
r.Post("/{version}/clear_log", route.environmentPHP.ClearLog)
|
||||
r.Post("/{version}/clear_slow_log", route.environmentPHP.ClearSlowLog)
|
||||
r.Get("/{version}/modules", route.environmentPHP.ModuleList)
|
||||
r.Post("/{version}/modules", route.environmentPHP.InstallModule)
|
||||
r.Delete("/{version}/modules", route.environmentPHP.UninstallModule)
|
||||
@@ -556,6 +553,7 @@ func (route *Http) Register(r *chi.Mux) {
|
||||
r.Post("/create", route.file.Create)
|
||||
r.Get("/content", route.file.Content)
|
||||
r.Post("/save", route.file.Save)
|
||||
r.Post("/truncate", route.file.Truncate)
|
||||
r.Post("/delete", route.file.Delete)
|
||||
r.Post("/upload", route.file.Upload)
|
||||
r.Post("/exist", route.file.Exist)
|
||||
@@ -606,6 +604,7 @@ func (route *Http) Register(r *chi.Mux) {
|
||||
r.Post("/reload", route.systemctl.Reload)
|
||||
r.Post("/start", route.systemctl.Start)
|
||||
r.Post("/stop", route.systemctl.Stop)
|
||||
r.Post("/clear_log", route.systemctl.ClearLog)
|
||||
})
|
||||
|
||||
r.Route("/toolbox_network", func(r chi.Router) {
|
||||
|
||||
@@ -249,44 +249,6 @@ func (s *EnvironmentPHPService) SlowLog(w http.ResponseWriter, r *http.Request)
|
||||
Success(w, fmt.Sprintf("%s/server/php/%d/var/log/slow.log", app.Root, req.Version))
|
||||
}
|
||||
|
||||
func (s *EnvironmentPHPService) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.EnvironmentPHPVersion](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
if !s.environmentRepo.IsInstalled("php", fmt.Sprintf("%d", req.Version)) {
|
||||
Error(w, http.StatusUnprocessableEntity, s.t.Get("PHP-%d is not installed", req.Version))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("cat /dev/null > %s/server/php/%d/var/log/php-fpm.log", app.Root, req.Version); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *EnvironmentPHPService) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.EnvironmentPHPVersion](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
if !s.environmentRepo.IsInstalled("php", fmt.Sprintf("%d", req.Version)) {
|
||||
Error(w, http.StatusUnprocessableEntity, s.t.Get("PHP-%d is not installed", req.Version))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("cat /dev/null > %s/server/php/%d/var/log/slow.log", app.Root, req.Version); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *EnvironmentPHPService) ModuleList(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.EnvironmentPHPVersion](r)
|
||||
if err != nil {
|
||||
|
||||
@@ -106,6 +106,22 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// Truncate 截断文件至 0 长度(保留文件本身和元数据)
|
||||
func (s *FileService) Truncate(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.FilePath](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = stdos.Truncate(req.Path, 0); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *FileService) Save(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.FileSave](r)
|
||||
if err != nil {
|
||||
|
||||
@@ -140,3 +140,19 @@ func (s *SystemctlService) Stop(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
// ClearLog 清空指定 systemd 服务的 journald 日志
|
||||
func (s *SystemctlService) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.SystemctlService](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.LogClear(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to clear log for %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
@@ -177,21 +177,6 @@ func (s *WebsiteService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *WebsiteService) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.ID](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.websiteRepo.ClearLog(req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *WebsiteService) UpdateRemark(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.WebsiteUpdateRemark](r)
|
||||
if err != nil {
|
||||
|
||||
@@ -7,12 +7,8 @@ export default {
|
||||
config: (): any => http.Get('/apps/mariadb/config'),
|
||||
// 保存配置
|
||||
saveConfig: (config: string): any => http.Post('/apps/mariadb/config', { config }),
|
||||
// 清空日志
|
||||
clearLog: (): any => http.Post('/apps/mariadb/clear_log'),
|
||||
// 获取慢查询日志
|
||||
slowLog: (): any => http.Get('/apps/mariadb/slow_log'),
|
||||
// 清空慢查询日志
|
||||
clearSlowLog: (): any => http.Post('/apps/mariadb/clear_slow_log'),
|
||||
// 获取 root 密码
|
||||
rootPassword: (): any => http.Get('/apps/mariadb/root_password'),
|
||||
// 修改 root 密码
|
||||
|
||||
@@ -7,12 +7,8 @@ export default {
|
||||
config: (): any => http.Get('/apps/mysql/config'),
|
||||
// 保存配置
|
||||
saveConfig: (config: string): any => http.Post('/apps/mysql/config', { config }),
|
||||
// 清空日志
|
||||
clearLog: (): any => http.Post('/apps/mysql/clear_log'),
|
||||
// 获取慢查询日志
|
||||
slowLog: (): any => http.Get('/apps/mysql/slow_log'),
|
||||
// 清空慢查询日志
|
||||
clearSlowLog: (): any => http.Post('/apps/mysql/clear_slow_log'),
|
||||
// 获取 root 密码
|
||||
rootPassword: (): any => http.Get('/apps/mysql/root_password'),
|
||||
// 修改 root 密码
|
||||
|
||||
@@ -7,12 +7,8 @@ export default {
|
||||
config: (): any => http.Get('/apps/percona/config'),
|
||||
// 保存配置
|
||||
saveConfig: (config: string): any => http.Post('/apps/percona/config', { config }),
|
||||
// 清空日志
|
||||
clearLog: (): any => http.Post('/apps/percona/clear_log'),
|
||||
// 获取慢查询日志
|
||||
slowLog: (): any => http.Get('/apps/percona/slow_log'),
|
||||
// 清空慢查询日志
|
||||
clearSlowLog: (): any => http.Post('/apps/percona/clear_slow_log'),
|
||||
// 获取 root 密码
|
||||
rootPassword: (): any => http.Get('/apps/percona/root_password'),
|
||||
// 修改 root 密码
|
||||
|
||||
@@ -13,8 +13,6 @@ export default {
|
||||
saveUserConfig: (config: string): any => http.Post('/apps/postgresql/user_config', { config }),
|
||||
// 获取日志
|
||||
log: (): any => http.Get('/apps/postgresql/log'),
|
||||
// 清空错误日志
|
||||
clearLog: (): any => http.Post('/apps/postgresql/clear_log'),
|
||||
// 获取 postgres 密码
|
||||
postgresPassword: (): any => http.Get('/apps/postgresql/postgres_password'),
|
||||
// 设置 postgres 密码
|
||||
|
||||
@@ -5,8 +5,6 @@ export default {
|
||||
service: (): any => http.Get('/apps/supervisor/service'),
|
||||
// 获取错误日志
|
||||
log: (): any => http.Get('/apps/supervisor/log'),
|
||||
// 清空错误日志
|
||||
clearLog: (): any => http.Post('/apps/supervisor/clear_log'),
|
||||
// 获取配置
|
||||
config: (): any => http.Get('/apps/supervisor/config'),
|
||||
// 保存配置
|
||||
@@ -23,9 +21,6 @@ export default {
|
||||
http.Post(`/apps/supervisor/processes/${process}/restart`),
|
||||
// 进程日志
|
||||
processLog: (process: string): any => http.Get(`/apps/supervisor/processes/${process}/log`),
|
||||
// 清空进程日志
|
||||
clearProcessLog: (process: string): any =>
|
||||
http.Post(`/apps/supervisor/processes/${process}/clear_log`),
|
||||
// 进程配置
|
||||
processConfig: (process: string): any => http.Get(`/apps/supervisor/processes/${process}`),
|
||||
// 保存进程配置
|
||||
|
||||
@@ -19,12 +19,8 @@ export default {
|
||||
load: (slug: number): any => http.Get(`/environment/php/${slug}/load`),
|
||||
// 获取日志
|
||||
log: (slug: number): any => http.Get(`/environment/php/${slug}/log`),
|
||||
// 清空日志
|
||||
clearLog: (slug: number): any => http.Post(`/environment/php/${slug}/clear_log`),
|
||||
// 获取慢日志
|
||||
slowLog: (slug: number): any => http.Get(`/environment/php/${slug}/slow_log`),
|
||||
// 清空慢日志
|
||||
clearSlowLog: (slug: number): any => http.Post(`/environment/php/${slug}/clear_slow_log`),
|
||||
// 拓展列表
|
||||
modules: (slug: number): any => http.Get(`/environment/php/${slug}/modules`),
|
||||
// 安装拓展
|
||||
|
||||
@@ -7,6 +7,8 @@ export default {
|
||||
content: (path: string): any => http.Get('/file/content', { params: { path } }),
|
||||
// 保存文件
|
||||
save: (path: string, content: string): any => http.Post('/file/save', { path, content }),
|
||||
// 截断文件至 0 长度
|
||||
truncate: (path: string): any => http.Post('/file/truncate', { path }),
|
||||
// 删除文件
|
||||
delete: (path: string): any => http.Post('/file/delete', { path }),
|
||||
// 上传文件
|
||||
|
||||
@@ -16,5 +16,7 @@ export default {
|
||||
// 启动服务
|
||||
start: (service: string): any => http.Post('/systemctl/start', { service }),
|
||||
// 停止服务
|
||||
stop: (service: string): any => http.Post('/systemctl/stop', { service })
|
||||
stop: (service: string): any => http.Post('/systemctl/stop', { service }),
|
||||
// 清空指定服务的 journald 日志
|
||||
clearLog: (service: string): any => http.Post('/systemctl/clear_log', { service })
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ export default {
|
||||
config: (id: number): any => http.Get('/website/' + id),
|
||||
// 保存网站配置
|
||||
saveConfig: (id: number, data: any): any => http.Put(`/website/${id}`, data),
|
||||
// 清空日志
|
||||
clearLog: (id: number): any => http.Delete('/website/' + id + '/log'),
|
||||
// 更新备注
|
||||
updateRemark: (id: number, remark: string): any =>
|
||||
http.Post(`/website/${id}` + '/update_remark', { remark }),
|
||||
|
||||
@@ -203,6 +203,14 @@ onMounted(async () => {
|
||||
onUnmounted(() => {
|
||||
closeTerminal()
|
||||
})
|
||||
|
||||
// 由父组件在清空源文件/日志成功后调用,清掉终端 buffer 里残留的旧内容
|
||||
const clear = () => {
|
||||
term.value?.clear()
|
||||
term.value?.reset()
|
||||
}
|
||||
|
||||
defineExpose({ clear })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -18,8 +18,13 @@ const props = defineProps({
|
||||
path: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits<{ clear: [] }>()
|
||||
|
||||
const terminalRef = ref<HTMLElement | null>(null)
|
||||
const term = ref<Terminal | null>(null)
|
||||
@@ -180,6 +185,14 @@ watch([() => props.path, () => show.value], async () => {
|
||||
onUnmounted(() => {
|
||||
closeTerminal()
|
||||
})
|
||||
|
||||
// 由父组件在清空源文件成功后调用,清掉终端 buffer 里残留的旧日志
|
||||
const clear = () => {
|
||||
term.value?.clear()
|
||||
term.value?.reset()
|
||||
}
|
||||
|
||||
defineExpose({ clear })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -193,6 +206,16 @@ onUnmounted(() => {
|
||||
:segmented="false"
|
||||
@after-leave="closeTerminal"
|
||||
>
|
||||
<template v-if="clearable" #header-extra>
|
||||
<n-popconfirm @positive-click="emit('clear')">
|
||||
<template #trigger>
|
||||
<n-button size="small" type="warning">
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
{{ $gettext('Are you sure you want to clear the log?') }}
|
||||
</n-popconfirm>
|
||||
</template>
|
||||
<div
|
||||
ref="terminalRef"
|
||||
class="realtime-log-terminal"
|
||||
|
||||
@@ -4,6 +4,8 @@ import { NButton, NDataTable, NInput } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import mysql from '@/api/apps/mysql'
|
||||
import file from '@/api/panel/file'
|
||||
import systemctl from '@/api/panel/systemctl'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import MysqlConfigTuneView from './MysqlConfigTuneView.vue'
|
||||
|
||||
@@ -18,6 +20,8 @@ const setRootPasswordLoading = ref(false)
|
||||
const saveConfigLoading = ref(false)
|
||||
const clearLogLoading = ref(false)
|
||||
const clearSlowLogLoading = ref(false)
|
||||
const runLogRef = ref<{ clear: () => void } | null>(null)
|
||||
const slowLogRef = ref<{ clear: () => void } | null>(null)
|
||||
|
||||
const { data: rootPassword } = useRequest(props.api.rootPassword, {
|
||||
initialData: ''
|
||||
@@ -67,8 +71,9 @@ const handleSaveConfig = () => {
|
||||
|
||||
const handleClearLog = () => {
|
||||
clearLogLoading.value = true
|
||||
useRequest(props.api.clearLog())
|
||||
useRequest(systemctl.clearLog('mysqld'))
|
||||
.onSuccess(() => {
|
||||
runLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
@@ -78,8 +83,9 @@ const handleClearLog = () => {
|
||||
|
||||
const handleClearSlowLog = () => {
|
||||
clearSlowLogLoading.value = true
|
||||
useRequest(props.api.clearSlowLog())
|
||||
useRequest(file.truncate(slowLog.value))
|
||||
.onSuccess(() => {
|
||||
slowLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
@@ -170,13 +176,13 @@ const handleCopyRootPassword = () => {
|
||||
<n-button type="primary" :loading="clearLogLoading" :disabled="clearLogLoading" @click="handleClearLog">
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
<realtime-log service="mysqld" />
|
||||
<realtime-log ref="runLogRef" service="mysqld" />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="slow-log" :tab="$gettext('Slow Query Log')">
|
||||
<n-button type="primary" :loading="clearSlowLogLoading" :disabled="clearSlowLogLoading" @click="handleClearSlowLog">
|
||||
{{ $gettext('Clear Slow Log') }}
|
||||
</n-button>
|
||||
<realtime-log :path="slowLog" />
|
||||
<realtime-log ref="slowLogRef" :path="slowLog" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
|
||||
@@ -8,6 +8,8 @@ import { NButton, NDataTable } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import postgresql from '@/api/apps/postgresql'
|
||||
import file from '@/api/panel/file'
|
||||
import systemctl from '@/api/panel/systemctl'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import PostgresqlConfigTuneView from './PostgresqlConfigTuneView.vue'
|
||||
|
||||
@@ -17,13 +19,33 @@ const setPostgresPasswordLoading = ref(false)
|
||||
const saveConfigLoading = ref(false)
|
||||
const saveUserConfigLoading = ref(false)
|
||||
const clearLogLoading = ref(false)
|
||||
const clearAppLogLoading = ref(false)
|
||||
const runLogRef = ref<{ clear: () => void } | null>(null)
|
||||
const appLogRef = ref<{ clear: () => void } | null>(null)
|
||||
|
||||
const { data: postgresPassword } = useRequest(postgresql.postgresPassword, {
|
||||
initialData: ''
|
||||
})
|
||||
const { data: log } = useRequest(postgresql.log, {
|
||||
initialData: ''
|
||||
const selectedLog = ref('')
|
||||
const { data: logList } = useRequest(postgresql.log, {
|
||||
initialData: []
|
||||
}).onSuccess(({ data }) => {
|
||||
if (!data?.length) {
|
||||
selectedLog.value = ''
|
||||
return
|
||||
}
|
||||
// log_filename='postgresql-%a.log' 按 weekday 缩写循环;优先选中今天对应的文件
|
||||
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
const today = `postgresql-${weekdays[new Date().getDay()]}.log`
|
||||
selectedLog.value = data.find((p: string) => p.endsWith(today)) ?? data[data.length - 1]
|
||||
})
|
||||
|
||||
const logOptions = computed(() =>
|
||||
(logList.value as string[]).map((p) => ({
|
||||
label: p.split('/').pop() ?? p,
|
||||
value: p
|
||||
}))
|
||||
)
|
||||
const { data: config, send: refreshConfig } = useRequest(postgresql.config, {
|
||||
initialData: ''
|
||||
})
|
||||
@@ -82,8 +104,9 @@ const handleSaveUserConfig = () => {
|
||||
|
||||
const handleClearLog = () => {
|
||||
clearLogLoading.value = true
|
||||
useRequest(postgresql.clearLog())
|
||||
useRequest(systemctl.clearLog('postgresql'))
|
||||
.onSuccess(() => {
|
||||
runLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
@@ -91,6 +114,19 @@ const handleClearLog = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleClearAppLog = () => {
|
||||
if (!selectedLog.value) return
|
||||
clearAppLogLoading.value = true
|
||||
useRequest(file.truncate(selectedLog.value))
|
||||
.onSuccess(() => {
|
||||
appLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
clearAppLogLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const handleSetPostgresPassword = () => {
|
||||
setPostgresPasswordLoading.value = true
|
||||
useRequest(postgresql.setPostgresPassword(postgresPassword.value))
|
||||
@@ -197,11 +233,19 @@ const handleCopyPostgresPassword = () => {
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<realtime-log service="postgresql" />
|
||||
<realtime-log ref="runLogRef" service="postgresql" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="slow-log" :tab="$gettext('Slow Logs')">
|
||||
<realtime-log :path="log" />
|
||||
<n-tab-pane name="app-log" :tab="$gettext('Application Logs')">
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-select v-model:value="selectedLog" :options="logOptions" style="width: 240px" />
|
||||
<n-button type="primary" :loading="clearAppLogLoading" :disabled="clearAppLogLoading || !selectedLog" @click="handleClearAppLog">
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<realtime-log ref="appLogRef" :path="selectedLog" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { NButton, NDataTable, NInput, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import supervisor from '@/api/apps/supervisor'
|
||||
import file from '@/api/panel/file'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
@@ -16,6 +17,9 @@ const saveProcessConfigLoading = ref(false)
|
||||
const clearLogLoading = ref(false)
|
||||
const createProcessLoading = ref(false)
|
||||
const processLog = ref('')
|
||||
const processLogModalRef = ref<{ clear: () => void } | null>(null)
|
||||
const daemonLogRef = ref<{ clear: () => void } | null>(null)
|
||||
const daemonLogPath = '/var/log/supervisor/supervisord.log'
|
||||
|
||||
const { data: serviceName } = useRequest(supervisor.service, {
|
||||
initialData: ''
|
||||
@@ -231,8 +235,9 @@ const handleSaveConfig = () => {
|
||||
|
||||
const handleClearLog = () => {
|
||||
clearLogLoading.value = true
|
||||
useRequest(supervisor.clearLog())
|
||||
useRequest(file.truncate(daemonLogPath))
|
||||
.onSuccess(() => {
|
||||
daemonLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
@@ -286,6 +291,13 @@ const handleShowProcessLog = async (row: any) => {
|
||||
processLogModal.value = true
|
||||
}
|
||||
|
||||
const handleClearProcessLog = () => {
|
||||
useRequest(file.truncate(processLog.value)).onSuccess(() => {
|
||||
processLogModalRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleEditProcess = async (name: string) => {
|
||||
await getProcessConfig(name)
|
||||
editProcessModal.value = true
|
||||
@@ -380,7 +392,7 @@ onUnmounted(() => {
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<realtime-log path="/var/log/supervisor/supervisord.log" />
|
||||
<realtime-log ref="daemonLogRef" :path="daemonLogPath" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
@@ -434,7 +446,13 @@ onUnmounted(() => {
|
||||
</n-form>
|
||||
<n-button type="info" block :loading="createProcessLoading" :disabled="createProcessLoading" @click="handleCreateProcess">{{ $gettext('Submit') }}</n-button>
|
||||
</n-modal>
|
||||
<realtime-log-modal v-model:show="processLogModal" :path="processLog" />
|
||||
<realtime-log-modal
|
||||
ref="processLogModalRef"
|
||||
v-model:show="processLogModal"
|
||||
:path="processLog"
|
||||
clearable
|
||||
@clear="handleClearProcessLog"
|
||||
/>
|
||||
<n-modal
|
||||
v-model:show="editProcessModal"
|
||||
preset="card"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import php from '@/api/panel/environment/php'
|
||||
import file from '@/api/panel/file'
|
||||
|
||||
const route = useRoute()
|
||||
const slug = Number(route.params.slug)
|
||||
@@ -32,6 +33,9 @@ watch(currentTab, (val) => {
|
||||
refreshFpmConfig()
|
||||
}
|
||||
})
|
||||
const logRef = ref<{ clear: () => void } | null>(null)
|
||||
const slowLogRef = ref<{ clear: () => void } | null>(null)
|
||||
|
||||
const { data: log } = useRequest(php.log(slug), {
|
||||
initialData: ''
|
||||
})
|
||||
@@ -171,13 +175,15 @@ const handleSaveFPMConfig = async () => {
|
||||
}
|
||||
|
||||
const handleClearLog = async () => {
|
||||
useRequest(php.clearLog(slug)).onSuccess(() => {
|
||||
useRequest(file.truncate(log.value)).onSuccess(() => {
|
||||
logRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleClearSlowLog = async () => {
|
||||
useRequest(php.clearSlowLog(slug)).onSuccess(() => {
|
||||
useRequest(file.truncate(slowLog.value)).onSuccess(() => {
|
||||
slowLogRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
}
|
||||
@@ -290,7 +296,7 @@ const handleUninstallModule = async (module: string) => {
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<realtime-log :path="log" />
|
||||
<realtime-log ref="logRef" :path="log" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="slow-log" :tab="$gettext('Slow Logs')">
|
||||
@@ -300,7 +306,7 @@ const handleUninstallModule = async (module: string) => {
|
||||
{{ $gettext('Clear Slow Log') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<realtime-log :path="slowLog" />
|
||||
<realtime-log ref="slowLogRef" :path="slowLog" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
|
||||
@@ -12,6 +12,15 @@ import CreateModal from '@/views/task/CreateModal.vue'
|
||||
const { $gettext } = useGettext()
|
||||
const logPath = ref('')
|
||||
const logModal = ref(false)
|
||||
const logModalRef = ref<{ clear: () => void } | null>(null)
|
||||
|
||||
const handleClearLog = () => {
|
||||
if (!logPath.value) return
|
||||
useRequest(file.truncate(logPath.value)).onSuccess(() => {
|
||||
logModalRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
}
|
||||
const shellEditModal = ref(false)
|
||||
const visualEditModal = ref(false)
|
||||
const saveTaskEditLoading = ref(false)
|
||||
@@ -305,7 +314,13 @@ onUnmounted(() => {
|
||||
pageSizes: [20, 50, 100, 200]
|
||||
}"
|
||||
/>
|
||||
<realtime-log-modal v-model:show="logModal" :path="logPath" />
|
||||
<realtime-log-modal
|
||||
ref="logModalRef"
|
||||
v-model:show="logModal"
|
||||
:path="logPath"
|
||||
clearable
|
||||
@clear="handleClearLog"
|
||||
/>
|
||||
<!-- Shell 脚本编辑模态框 -->
|
||||
<n-modal
|
||||
v-model:show="shellEditModal"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import file from '@/api/panel/file'
|
||||
import task from '@/api/panel/task'
|
||||
import RealtimeLogModal from '@/components/common/RealtimeLogModal.vue'
|
||||
import { formatDateTime } from '@/utils'
|
||||
@@ -9,6 +10,15 @@ import { formatDateTime } from '@/utils'
|
||||
const { $gettext } = useGettext()
|
||||
const logModal = ref(false)
|
||||
const logPath = ref('')
|
||||
const logModalRef = ref<{ clear: () => void } | null>(null)
|
||||
|
||||
const handleClearLog = () => {
|
||||
if (!logPath.value) return
|
||||
useRequest(file.truncate(logPath.value)).onSuccess(() => {
|
||||
logModalRef.value?.clear()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const columns: any = [
|
||||
{
|
||||
@@ -154,5 +164,11 @@ onMounted(() => {
|
||||
}"
|
||||
/>
|
||||
</n-flex>
|
||||
<realtime-log-modal v-model:show="logModal" :path="logPath" />
|
||||
<realtime-log-modal
|
||||
ref="logModalRef"
|
||||
v-model:show="logModal"
|
||||
:path="logPath"
|
||||
clearable
|
||||
@clear="handleClearLog"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useGettext } from 'vue3-gettext'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
import cert from '@/api/panel/cert'
|
||||
import file from '@/api/panel/file'
|
||||
import home from '@/api/panel/home'
|
||||
import website from '@/api/panel/website'
|
||||
import KeyValueEditor from '@/components/common/KeyValueEditor.vue'
|
||||
@@ -246,10 +247,11 @@ const handleSelectCert = (value: number) => {
|
||||
}
|
||||
|
||||
const clearLog = async () => {
|
||||
const path = current.value === 'error_log' ? setting.value.error_log : setting.value.access_log
|
||||
if (!path || path === 'off') return
|
||||
clearLogLoading.value = true
|
||||
useRequest(website.clearLog(id.value))
|
||||
useRequest(file.truncate(path))
|
||||
.onSuccess(() => {
|
||||
fetchSetting()
|
||||
window.$message.success($gettext('Cleared successfully'))
|
||||
})
|
||||
.onComplete(() => {
|
||||
@@ -2030,7 +2032,7 @@ const removeCustomConfig = (index: number) => {
|
||||
</n-spin>
|
||||
<template #footer>
|
||||
<n-flex justify="end">
|
||||
<n-popconfirm v-if="current == 'log'" @positive-click="clearLog">
|
||||
<n-popconfirm v-if="current === 'log' || current === 'error_log'" @positive-click="clearLog">
|
||||
<template #trigger>
|
||||
<n-button type="primary" :loading="clearLogLoading" :disabled="clearLogLoading">
|
||||
{{ $gettext('Clear Logs') }}
|
||||
|
||||
Reference in New Issue
Block a user