From 2a3119fe9a4a19ccd6f28d7d20b63fcf413c54a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Wed, 19 Jul 2023 00:24:41 +0800 Subject: [PATCH] feat: cron controller --- app/http/controllers/cron_controller.go | 229 +++++++++++++++++++++++- app/http/controllers/file_controller.go | 4 +- app/http/controllers/task_controller.go | 2 +- app/http/controllers/user_controller.go | 4 +- app/services/cron.go | 40 +++++ routes/web.go | 38 ++++ 6 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 app/services/cron.go diff --git a/app/http/controllers/cron_controller.go b/app/http/controllers/cron_controller.go index 05011e04..3fcc46c7 100644 --- a/app/http/controllers/cron_controller.go +++ b/app/http/controllers/cron_controller.go @@ -1,18 +1,241 @@ package controllers import ( + "strconv" + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + "github.com/goravel/framework/support/carbon" + + "panel/app/models" + "panel/app/services" + "panel/packages/helper" ) type CronController struct { - //Dependent services + cron services.Cron } func NewCronController() *CronController { return &CronController{ - //Inject services + cron: services.NewCronImpl(), } } -func (r *CronController) Index(ctx http.Context) { +func (r *CronController) List(ctx http.Context) { + limit := ctx.Request().QueryInt("limit") + page := ctx.Request().QueryInt("page") + + var crons []models.Cron + var total int64 + err := facades.Orm().Query().Paginate(page, limit, &crons, &total) + if err != nil { + facades.Log().Error("[面板][CronController] 查询计划任务列表失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + + Success(ctx, http.Json{ + "total": total, + "items": crons, + }) +} + +func (r *CronController) Add(ctx http.Context) { + validator, err := ctx.Request().Validate(map[string]string{ + "name": "required|min_len:1|max_len:255", + "time": "required|regex:^((\\*|\\d+|\\d+-\\d+|\\d+\\/\\d+|\\d+-\\d+\\/\\d+|\\*\\/\\d+)(\\,(\\*|\\d+|\\d+-\\d+|\\d+\\/\\d+|\\d+-\\d+\\/\\d+|\\*\\/\\d+))*\\s?){5}$", + "script": "required", + }) + if err != nil { + Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + Error(ctx, http.StatusBadRequest, validator.Errors().All()) + return + } + + // 写入shell + shellDir := "/www/server/cron/" + shellLogDir := "/www/server/cron/logs/" + if !helper.Exists(shellDir) { + if !helper.Mkdir(shellDir, 0644) { + facades.Log().Error("[面板][CronController] 创建计划任务目录失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + } + if !helper.Exists(shellLogDir) { + if !helper.Mkdir(shellLogDir, 0644) { + facades.Log().Error("[面板][CronController] 创建计划任务日志目录失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + } + shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + helper.RandomString(16) + if !helper.WriteFile(shellDir+shellFile+".sh", ctx.Request().Input("script"), 0644) { + facades.Log().Error("[面板][CronController] 创建计划任务脚本失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + helper.ExecShell("dos2unix " + shellDir + shellFile + ".sh") + + var cron models.Cron + cron.Name = ctx.Request().Input("name") + cron.Type = "shell" + cron.Status = true + cron.Time = ctx.Request().Input("time") + cron.Shell = shellDir + shellFile + ".sh" + cron.Log = shellLogDir + shellFile + ".log" + + err = facades.Orm().Query().Create(&cron) + if err != nil { + facades.Log().Error("[面板][CronController] 创建计划任务失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + + r.cron.AddToSystem(cron) + + Success(ctx, http.Json{ + "id": cron.ID, + }) +} + +func (r *CronController) Update(ctx http.Context) { + validator, err := ctx.Request().Validate(map[string]string{ + "id": "required|int", + "name": "required|min_len:1|max_len:255", + "time": "required|regex:^((\\*|\\d+|\\d+-\\d+|\\d+\\/\\d+|\\d+-\\d+\\/\\d+|\\*\\/\\d+)(\\,(\\*|\\d+|\\d+-\\d+|\\d+\\/\\d+|\\d+-\\d+\\/\\d+|\\*\\/\\d+))*\\s?){5}$", + "script": "required", + }) + if err != nil { + Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + Error(ctx, http.StatusBadRequest, validator.Errors().All()) + return + } + + var cron models.Cron + err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron) + if err != nil { + Error(ctx, http.StatusBadRequest, "计划任务不存在") + return + } + + if !helper.WriteFile(cron.Shell, ctx.Request().Input("script"), 0644) { + facades.Log().Error("[面板][CronController] 更新计划任务脚本失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + helper.ExecShell("dos2unix " + cron.Shell) + + r.cron.DeleteFromSystem(cron) + if cron.Status { + r.cron.AddToSystem(cron) + } + + Success(ctx, nil) +} + +func (r *CronController) Delete(ctx http.Context) { + validator, err := ctx.Request().Validate(map[string]string{ + "id": "required|int", + }) + if err != nil { + Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + Error(ctx, http.StatusBadRequest, validator.Errors().All()) + return + } + + var cron models.Cron + err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron) + if err != nil { + Error(ctx, http.StatusBadRequest, "计划任务不存在") + return + } + + _, err = facades.Orm().Query().Delete(&cron) + if err != nil { + facades.Log().Error("[面板][CronController] 删除计划任务失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + + r.cron.DeleteFromSystem(cron) + + Success(ctx, nil) +} + +func (r *CronController) Status(ctx http.Context) { + validator, err := ctx.Request().Validate(map[string]string{ + "id": "required|int", + "status": "required|in:true,false", + }) + if err != nil { + Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + Error(ctx, http.StatusBadRequest, validator.Errors().All()) + return + } + + var cron models.Cron + err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron) + if err != nil { + Error(ctx, http.StatusBadRequest, "计划任务不存在") + return + } + + cron.Status = ctx.Request().InputBool("status") + _, err = facades.Orm().Query().Update(&cron) + if err != nil { + facades.Log().Error("[面板][CronController] 更新计划任务状态失败 ", err) + Error(ctx, http.StatusInternalServerError, "系统内部错误") + return + } + + r.cron.DeleteFromSystem(cron) + if cron.Status { + r.cron.AddToSystem(cron) + } + + Success(ctx, nil) +} + +func (r *CronController) Log(ctx http.Context) { + validator, err := ctx.Request().Validate(map[string]string{ + "id": "required|int", + }) + if err != nil { + Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + Error(ctx, http.StatusBadRequest, validator.Errors().All()) + return + } + + var cron models.Cron + err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron) + if err != nil { + Error(ctx, http.StatusBadRequest, "计划任务不存在") + return + } + + if !helper.Exists(cron.Log) { + Error(ctx, http.StatusBadRequest, "日志文件不存在") + return + } + + Success(ctx, http.Json{ + "log": helper.ReadFile(cron.Log), + }) } diff --git a/app/http/controllers/file_controller.go b/app/http/controllers/file_controller.go index 3500e3ce..aaad0b9d 100644 --- a/app/http/controllers/file_controller.go +++ b/app/http/controllers/file_controller.go @@ -5,12 +5,12 @@ import ( ) type FileController struct { - //Dependent services + // Dependent services } func NewFileController() *FileController { return &FileController{ - //Inject services + // Inject services } } diff --git a/app/http/controllers/task_controller.go b/app/http/controllers/task_controller.go index 423ce31d..b6eb6601 100644 --- a/app/http/controllers/task_controller.go +++ b/app/http/controllers/task_controller.go @@ -53,7 +53,7 @@ func (r *TaskController) List(ctx http.Context) { }) } -func (r *TaskController) TaskLog(ctx http.Context) { +func (r *TaskController) Log(ctx http.Context) { var task models.Task err := facades.Orm().Query().Where("id", ctx.Request().QueryInt("id")).FirstOrFail(&task) if err != nil { diff --git a/app/http/controllers/user_controller.go b/app/http/controllers/user_controller.go index 89244529..8e8c950c 100644 --- a/app/http/controllers/user_controller.go +++ b/app/http/controllers/user_controller.go @@ -9,12 +9,12 @@ import ( ) type UserController struct { - //Dependent services + // Dependent services } func NewUserController() *UserController { return &UserController{ - //Inject services + // Inject services } } diff --git a/app/services/cron.go b/app/services/cron.go new file mode 100644 index 00000000..63019df2 --- /dev/null +++ b/app/services/cron.go @@ -0,0 +1,40 @@ +package services + +import ( + "panel/app/models" + "panel/packages/helper" +) + +type Cron interface { + AddToSystem(cron models.Cron) + DeleteFromSystem(cron models.Cron) +} + +type CronImpl struct { +} + +func NewCronImpl() *CronImpl { + return &CronImpl{} +} + +// AddToSystem 添加到系统 +func (r *CronImpl) AddToSystem(cron models.Cron) { + if helper.IsRHEL() { + helper.ExecShell("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/root") + } else { + helper.ExecShell("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/crontabs/root") + } + + helper.ExecShell("systemctl restart crond") +} + +// DeleteFromSystem 从系统中删除 +func (r *CronImpl) DeleteFromSystem(cron models.Cron) { + if helper.IsRHEL() { + helper.ExecShell("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root") + } else { + helper.ExecShell("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root") + } + + helper.ExecShell("systemctl restart crond") +} diff --git a/routes/web.go b/routes/web.go index a81a7313..8917bc1f 100644 --- a/routes/web.go +++ b/routes/web.go @@ -28,6 +28,9 @@ func Web() { r.Prefix("task").Middleware(middleware.Jwt()).Group(func(r route.Route) { taskController := controllers.NewTaskController() r.Get("status", taskController.Status) + r.Get("list", taskController.List) + r.Get("log", taskController.Log) + r.Post("delete", taskController.Delete) }) r.Prefix("website").Middleware(middleware.Jwt()).Group(func(r route.Route) { websiteController := controllers.NewWebsiteController() @@ -41,6 +44,41 @@ func Web() { r.Post("update", pluginController.Update) r.Post("updateShow", pluginController.UpdateShow) }) + r.Prefix("cron").Middleware(middleware.Jwt()).Group(func(r route.Route) { + cronController := controllers.NewCronController() + r.Get("list", cronController.List) + r.Post("add", cronController.Add) + r.Post("update", cronController.Update) + r.Post("delete", cronController.Delete) + r.Post("status", cronController.Status) + r.Get("log", cronController.Log) + }) + r.Prefix("safe").Middleware(middleware.Jwt()).Group(func(r route.Route) { + safeController := controllers.NewSafeController() + r.Get("firewallStatus", safeController.GetFirewallStatus) + r.Post("firewallStatus", safeController.SetFirewallStatus) + r.Get("firewallRules", safeController.GetFirewallRules) + r.Post("addFirewallRules", safeController.AddFirewallRule) + r.Post("deleteFirewallRules", safeController.DeleteFirewallRule) + r.Get("sshStatus", safeController.GetSshStatus) + r.Post("sshStatus", safeController.SetSshStatus) + r.Get("sshPort", safeController.GetSshPort) + r.Post("sshPort", safeController.SetSshPort) + r.Get("pingStatus", safeController.GetPingStatus) + r.Post("pingStatus", safeController.SetPingStatus) + }) + r.Prefix("monitor").Middleware(middleware.Jwt()).Group(func(r route.Route) { + monitorController := controllers.NewMonitorController() + r.Post("switch", monitorController.Switch) + r.Post("saveDays", monitorController.SaveDays) + r.Post("clear", monitorController.Clear) + r.Get("list", monitorController.List) + }) + r.Prefix("setting").Middleware(middleware.Jwt()).Group(func(r route.Route) { + settingController := controllers.NewSettingController() + r.Get("list", settingController.List) + r.Post("save", settingController.Save) + }) }) facades.Route().Fallback(func(ctx http.Context) {