package server import ( "errors" "net/http" "os" "strconv" "strings" "time" "github.com/cdnutter/canon/internal/backup" "github.com/cdnutter/canon/internal/store" ) func (s *Server) apiBackups(w http.ResponseWriter, r *http.Request) { set, err := s.store.Settings(r.Context()) if err != nil { s.writeErr(w, err) return } list, err := s.backups.List() if err != nil { s.writeErr(w, err) return } cron, keep := backup.Settings(set) zone, _ := time.Now().Zone() if tz := os.Getenv("TZ"); tz != "" && tz != zone { zone = tz + ", " + zone } writeJSON(w, http.StatusOK, map[string]any{ "backups": list, "status": s.backups.Status(), "next": backup.NextRun(cron, time.Now()), "cron": cron, "keep": keep, "dir": s.backups.Dir(), "timezone": zone, }) } func (s *Server) apiCreateBackup(w http.ResponseWriter, r *http.Request) { info, err := s.backups.Create(r.Context(), true) if err != nil { s.writeErr(w, err) return } writeJSON(w, http.StatusCreated, info) } func (s *Server) apiBackupSchedule(w http.ResponseWriter, r *http.Request) { var in struct { Cron string `json:"cron"` Keep int `json:"keep"` } if err := decodeJSON(r, &in); err != nil { s.writeErr(w, err) return } cron := strings.Join(strings.Fields(in.Cron), " ") if strings.EqualFold(cron, "off") || cron == "" { cron = "off" } else { sched, err := backup.ParseCron(cron) if err != nil { s.writeErr(w, store.ValidationError{Msg: "Schedule: " + err.Error()}) return } if sched.Next(time.Now()).IsZero() { s.writeErr(w, store.ValidationError{Msg: "That schedule never runs"}) return } } if in.Keep < 1 || in.Keep > backup.MaxKeep { s.writeErr(w, store.ValidationError{Msg: "Keep between 1 and " + strconv.Itoa(backup.MaxKeep) + " backups"}) return } if err := s.store.SetSettings(r.Context(), map[string]string{ store.SettingBackupCron: cron, store.SettingBackupKeep: strconv.Itoa(in.Keep), }); err != nil { s.writeErr(w, err) return } s.backups.Reschedule() s.apiBackups(w, r) } func (s *Server) apiDeleteBackup(w http.ResponseWriter, r *http.Request) { err := s.backups.Delete(r.PathValue("name")) if errors.Is(err, backup.ErrNotFound) { writeError(w, http.StatusNotFound, "Not found") return } if err != nil { s.writeErr(w, err) return } w.WriteHeader(http.StatusNoContent) } func (s *Server) handleBackupDownload(w http.ResponseWriter, r *http.Request) { name := r.PathValue("name") path, err := s.backups.Path(name) if err != nil { s.notFound(w, r) return } w.Header().Set("Content-Type", "application/gzip") w.Header().Set("Content-Disposition", `attachment; filename="`+name+`"`) w.Header().Set("Cache-Control", "no-store") http.ServeFile(w, r, path) }