package controllers import ( "encoding/json" "fmt" "nodemaster/models" "strings" "time" beego "github.com/beego/beego/v2/server/web" ) // ServicesController manages services on this host. type ServicesController struct { beego.Controller } // @Title GetAllServices // @Description list all services // @Success 200 {object} []models.Service // @router / [get] func (c *ServicesController) GetAll() { c.Data["json"] = models.GetAllServices() c.ServeJSON() } // @Title AddService // @Description register a new service // @Param body body models.Service true "service definition" // @Success 201 {object} models.Service // @Failure 400 invalid body // @router / [post] func (c *ServicesController) Post() { var s models.Service if err := json.Unmarshal(c.Ctx.Input.RequestBody, &s); err != nil { c.Ctx.Output.SetStatus(400) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } created, err := models.AddService(s) if err != nil { c.Ctx.Output.SetStatus(500) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Ctx.Output.SetStatus(201) c.Data["json"] = created c.ServeJSON() } // @Title GetService // @Description get a service by id // @Param id path string true "service id" // @Success 200 {object} models.Service // @Failure 404 not found // @router /:id [get] func (c *ServicesController) Get() { id := c.Ctx.Input.Param(":id") s, err := models.GetService(id) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = s c.ServeJSON() } // @Title UpdateService // @Description update a service definition // @Param id path string true "service id" // @Param body body models.Service true "updated service data" // @Success 200 {object} models.Service // @Failure 400 invalid body // @Failure 404 not found // @router /:id [put] func (c *ServicesController) Put() { id := c.Ctx.Input.Param(":id") var s models.Service if err := json.Unmarshal(c.Ctx.Input.RequestBody, &s); err != nil { c.Ctx.Output.SetStatus(400) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } updated, err := models.UpdateService(id, s) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = updated c.ServeJSON() } // @Title DeleteService // @Description delete a service // @Param id path string true "service id" // @Success 200 {string} delete success // @Failure 404 not found // @router /:id [delete] func (c *ServicesController) Delete() { id := c.Ctx.Input.Param(":id") if err := models.DeleteService(id); err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = map[string]string{"message": "delete success"} c.ServeJSON() } // @Title StartService // @Description start a service via its compose file // @Param id path string true "service id" // @Success 200 {string} started // @Failure 404 not found // @router /:id/start [post] func (c *ServicesController) Start() { id := c.Ctx.Input.Param(":id") svc, err := models.GetService(id) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } if err := models.StartService(svc); err != nil { c.Ctx.Output.SetStatus(500) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } _ = models.UpdateServiceStatus(id, "up") c.Data["json"] = map[string]string{"message": "service started"} c.ServeJSON() } // @Title StopService // @Description stop a service via its compose file // @Param id path string true "service id" // @Success 200 {string} stopped // @Failure 404 not found // @router /:id/stop [post] func (c *ServicesController) Stop() { id := c.Ctx.Input.Param(":id") svc, err := models.GetService(id) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } if err := models.StopService(svc); err != nil { _ = models.UpdateServiceStatus(id, "down") c.Ctx.Output.SetStatus(500) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } _ = models.UpdateServiceStatus(id, "down") c.Data["json"] = map[string]string{"message": "service stopped"} c.ServeJSON() } // @Title BackupService // @Description trigger an immediate backup for a service across all targets (asynchronous) // @Param id path string true "service id" // @Success 202 {string} backup started // @Failure 400 no backup configured // @Failure 404 not found // @router /:id/backup [post] func (c *ServicesController) Backup() { id := c.Ctx.Input.Param(":id") svc, err := models.GetService(id) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } if svc.Backup == nil || len(svc.Backup.Targets) == 0 { c.Ctx.Output.SetStatus(400) c.Data["json"] = map[string]string{"error": "no backup targets configured for this service"} c.ServeJSON() return } c.Ctx.Output.SetStatus(202) c.Data["json"] = map[string]string{ "message": "backup started", "started": fmt.Sprintf("%s", time.Now().UTC().Format(time.RFC3339)), } c.ServeJSON() go func() { _ = models.RunServiceBackup(id, nil) }() } // @Title GetServiceBackupTargets // @Description list a service's backup target definitions // @Param id path string true "service id" // @Success 200 {object} []models.BackupTarget // @Failure 404 not found // @router /:id/backup/targets [get] func (c *ServicesController) GetBackupTargets() { id := c.Ctx.Input.Param(":id") targets, err := models.GetServiceBackupTargets(id) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = targets c.ServeJSON() } // @Title AddServiceBackupTarget // @Description define a new backup target for a service // @Param id path string true "service id" // @Param body body models.BackupTarget true "backup target definition" // @Success 201 {object} models.BackupTarget // @Failure 400 invalid body // @Failure 404 not found // @router /:id/backup/targets [post] func (c *ServicesController) AddBackupTarget() { id := c.Ctx.Input.Param(":id") var t models.BackupTarget if err := json.Unmarshal(c.Ctx.Input.RequestBody, &t); err != nil { c.Ctx.Output.SetStatus(400) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } created, err := models.AddServiceBackupTarget(id, t) if err != nil { status := 400 if err.Error() == "service not found" { status = 404 } c.Ctx.Output.SetStatus(status) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Ctx.Output.SetStatus(201) c.Data["json"] = created c.ServeJSON() } // @Title GetServiceBackupTarget // @Description get a service backup target by id // @Param id path string true "service id" // @Param tid path string true "backup target id" // @Success 200 {object} models.BackupTarget // @Failure 404 not found // @router /:id/backup/targets/:tid [get] func (c *ServicesController) GetBackupTarget() { id := c.Ctx.Input.Param(":id") tid := c.Ctx.Input.Param(":tid") t, err := models.GetServiceBackupTarget(id, tid) if err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = t c.ServeJSON() } // @Title PutServiceBackupTarget // @Description update a service backup target definition // @Param id path string true "service id" // @Param tid path string true "backup target id" // @Param body body models.BackupTarget true "updated backup target" // @Success 200 {object} models.BackupTarget // @Failure 400 invalid body // @Failure 404 not found // @router /:id/backup/targets/:tid [put] func (c *ServicesController) PutBackupTarget() { id := c.Ctx.Input.Param(":id") tid := c.Ctx.Input.Param(":tid") var t models.BackupTarget if err := json.Unmarshal(c.Ctx.Input.RequestBody, &t); err != nil { c.Ctx.Output.SetStatus(400) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } updated, err := models.UpdateServiceBackupTarget(id, tid, t) if err != nil { status := 400 if strings.Contains(err.Error(), "not found") { status = 404 } c.Ctx.Output.SetStatus(status) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = updated c.ServeJSON() } // @Title DeleteServiceBackupTarget // @Description delete a service backup target definition // @Param id path string true "service id" // @Param tid path string true "backup target id" // @Success 200 {string} delete success // @Failure 404 not found // @router /:id/backup/targets/:tid [delete] func (c *ServicesController) DeleteBackupTarget() { id := c.Ctx.Input.Param(":id") tid := c.Ctx.Input.Param(":tid") if err := models.DeleteServiceBackupTarget(id, tid); err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = map[string]string{"message": "delete success"} c.ServeJSON() } // @Title RunServiceBackupTarget // @Description trigger an immediate backup for a single service target (asynchronous) // @Param id path string true "service id" // @Param tid path string true "backup target id" // @Success 202 {string} backup started // @Failure 404 not found // @router /:id/backup/targets/:tid/run [post] func (c *ServicesController) RunBackupTarget() { id := c.Ctx.Input.Param(":id") tid := c.Ctx.Input.Param(":tid") if _, err := models.GetServiceBackupTarget(id, tid); err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Ctx.Output.SetStatus(202) c.Data["json"] = map[string]string{"message": "backup started"} c.ServeJSON() go func() { _ = models.RunServiceBackup(id, []string{tid}) }() } // @Title GetServiceBackupTargetProgress // @Description get the current/last run state for a service backup target // @Param id path string true "service id" // @Param tid path string true "backup target id" // @Success 200 {object} models.RunState // @Failure 404 not found // @router /:id/backup/targets/:tid/progress [get] func (c *ServicesController) GetBackupTargetProgress() { id := c.Ctx.Input.Param(":id") tid := c.Ctx.Input.Param(":tid") if _, err := models.GetServiceBackupTarget(id, tid); err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } state, ok := models.GetProgress("svc:"+id, tid) if !ok { state = models.RunState{TargetID: tid} } c.Data["json"] = state c.ServeJSON() } // @Title GetServiceBackupProgress // @Description get the current/last run state for every backup target of a service // @Param id path string true "service id" // @Success 200 {object} []models.RunState // @Failure 404 not found // @router /:id/backup/progress [get] func (c *ServicesController) GetBackupProgress() { id := c.Ctx.Input.Param(":id") if _, err := models.GetService(id); err != nil { c.Ctx.Output.SetStatus(404) c.Data["json"] = map[string]string{"error": err.Error()} c.ServeJSON() return } c.Data["json"] = models.GetProgressForScope("svc:" + id) c.ServeJSON() }