backup: pre-hardening baseline
This commit is contained in:
commit
9e4c612dcb
57 changed files with 10393 additions and 0 deletions
146
backend/internal/api/handlers/config_file.go
Normal file
146
backend/internal/api/handlers/config_file.go
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/traefik/traefik-gui/backend/internal/api/middleware"
|
||||
"github.com/traefik/traefik-gui/backend/internal/config/file"
|
||||
)
|
||||
|
||||
type FileConfigHandler struct {
|
||||
svc *file.Service
|
||||
}
|
||||
|
||||
func NewFileConfigHandler(svc *file.Service) *FileConfigHandler {
|
||||
return &FileConfigHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) ListFiles(c *gin.Context) {
|
||||
files, err := h.svc.ListFilesWithMeta()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if files == nil {
|
||||
files = []file.FileMeta{}
|
||||
}
|
||||
c.JSON(http.StatusOK, files)
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) GetFile(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
content, err := h.svc.ReadFile(name)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"filename": name, "content": content})
|
||||
}
|
||||
|
||||
type PreviewRequest struct {
|
||||
Filename string `json:"filename" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) Preview(c *gin.Context) {
|
||||
var req PreviewRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "filename and content required"})
|
||||
return
|
||||
}
|
||||
result := h.svc.Preview(req.Filename, req.Content)
|
||||
if !result.Valid {
|
||||
c.JSON(http.StatusBadRequest, result)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
type ApplyRequest struct {
|
||||
Filename string `json:"filename" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
Confirm bool `json:"confirm"`
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) Apply(c *gin.Context) {
|
||||
user := middleware.GetUser(c)
|
||||
if user == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
if user.Role != "admin" && user.Role != "operator" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "operator or admin required"})
|
||||
return
|
||||
}
|
||||
var req ApplyRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "filename, content, and confirm required"})
|
||||
return
|
||||
}
|
||||
if !req.Confirm {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "confirmation required: set confirm:true"})
|
||||
return
|
||||
}
|
||||
result, err := h.svc.Apply(req.Filename, req.Content, user.ID, true)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if !result.Valid {
|
||||
c.JSON(http.StatusBadRequest, result)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "applied", "diff": result.Diff})
|
||||
}
|
||||
|
||||
type RollbackRequest struct {
|
||||
Filename string `json:"filename" binding:"required"`
|
||||
BackupID string `json:"backupId"`
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) Rollback(c *gin.Context) {
|
||||
user := middleware.GetUser(c)
|
||||
if user == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
if user.Role != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "admin required for rollback"})
|
||||
return
|
||||
}
|
||||
var req RollbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "filename required"})
|
||||
return
|
||||
}
|
||||
result, err := h.svc.Rollback(req.Filename, req.BackupID, user.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "rolled back", "diff": result.Diff})
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) History(c *gin.Context) {
|
||||
filename := c.Query("filename")
|
||||
history, err := h.svc.History(filename)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if history == nil {
|
||||
history = []file.BackupInfo{}
|
||||
}
|
||||
c.JSON(http.StatusOK, history)
|
||||
}
|
||||
|
||||
func (h *FileConfigHandler) Validate(c *gin.Context) {
|
||||
var req PreviewRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "filename and content required"})
|
||||
return
|
||||
}
|
||||
result := h.svc.Preview(req.Filename, req.Content)
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue