package handlers import ( "errors" "net/http" "strconv" "github.com/gin-gonic/gin" "git.netcell-it.de/projekte/edgeguard-native/internal/handlers/response" "git.netcell-it.de/projekte/edgeguard-native/internal/services/audit" "git.netcell-it.de/projekte/edgeguard-native/internal/services/backup" ) // BackupHandler exposes: // // GET /api/v1/backups — list // POST /api/v1/backups — trigger manual backup (sync) // GET /api/v1/backups/:id — single entry // GET /api/v1/backups/:id/download — sendfile tar.gz // DELETE /api/v1/backups/:id — delete tar.gz + row type BackupHandler struct { Service *backup.Service Audit *audit.Repo NodeID string Version string } func NewBackupHandler(s *backup.Service, a *audit.Repo, nodeID, version string) *BackupHandler { return &BackupHandler{Service: s, Audit: a, NodeID: nodeID, Version: version} } func (h *BackupHandler) Register(rg *gin.RouterGroup) { g := rg.Group("/backups") g.GET("", h.List) g.POST("", h.Trigger) g.GET("/:id", h.Get) g.GET("/:id/download", h.Download) g.DELETE("/:id", h.Delete) } func (h *BackupHandler) List(c *gin.Context) { out, err := h.Service.List(c.Request.Context()) if err != nil { response.Internal(c, err) return } response.OK(c, gin.H{"backups": out}) } func (h *BackupHandler) Trigger(c *gin.Context) { // Manual backup läuft synchron — der Operator wartet vor dem // Knopf. Bei echten Multi-GB-DBs wäre async besser, aber unsere // edgeguard-DB ist klein (<50 MB typisch). res, err := h.Service.Run(c.Request.Context(), backup.KindManual, h.Version) if err != nil { response.Err(c, http.StatusInternalServerError, err) return } _ = h.Audit.Log(c.Request.Context(), actorOf(c), "backup.create", res.File, gin.H{"size": res.SizeBytes, "sha256": res.SHA256}, h.NodeID) response.OK(c, gin.H{ "id": res.ID, "file": res.File, "size_bytes": res.SizeBytes, "sha256": res.SHA256, "db_dump_bytes": res.DBDumpBytes, "files_bytes": res.FilesBytes, "started_at": res.StartedAt, "finished_at": res.FinishedAt, }) } func (h *BackupHandler) Get(c *gin.Context) { id, ok := parseID(c) if !ok { return } e, _, err := h.Service.Get(c.Request.Context(), id) if err != nil { response.NotFound(c, err) return } response.OK(c, e) } func (h *BackupHandler) Download(c *gin.Context) { id, ok := parseID(c) if !ok { return } e, path, err := h.Service.Get(c.Request.Context(), id) if err != nil { response.NotFound(c, err) return } // gin.FileAttachment setzt Content-Disposition + sendet stream. c.FileAttachment(path, e.File) } func (h *BackupHandler) Delete(c *gin.Context) { id, ok := parseID(c) if !ok { return } if err := h.Service.Delete(c.Request.Context(), id); err != nil { response.Err(c, http.StatusInternalServerError, err) return } _ = h.Audit.Log(c.Request.Context(), actorOf(c), "backup.delete", strconv.FormatInt(id, 10), gin.H{"id": id}, h.NodeID) response.NoContent(c) } // Defensive — falls jemand den Pool fehlerhaft injected. var _ = errors.New