384 lines
9.9 KiB
Go
384 lines
9.9 KiB
Go
package backup
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/atlasos/calypso/internal/common/logger"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Handler handles backup-related API requests
|
|
type Handler struct {
|
|
service *Service
|
|
logger *logger.Logger
|
|
}
|
|
|
|
// NewHandler creates a new backup handler
|
|
func NewHandler(service *Service, log *logger.Logger) *Handler {
|
|
return &Handler{
|
|
service: service,
|
|
logger: log,
|
|
}
|
|
}
|
|
|
|
// ListJobs lists backup jobs with optional filters
|
|
func (h *Handler) ListJobs(c *gin.Context) {
|
|
opts := ListJobsOptions{
|
|
Status: c.Query("status"),
|
|
JobType: c.Query("job_type"),
|
|
ClientName: c.Query("client_name"),
|
|
JobName: c.Query("job_name"),
|
|
}
|
|
|
|
// Parse pagination
|
|
var limit, offset int
|
|
if limitStr := c.Query("limit"); limitStr != "" {
|
|
if _, err := fmt.Sscanf(limitStr, "%d", &limit); err == nil {
|
|
opts.Limit = limit
|
|
}
|
|
}
|
|
if offsetStr := c.Query("offset"); offsetStr != "" {
|
|
if _, err := fmt.Sscanf(offsetStr, "%d", &offset); err == nil {
|
|
opts.Offset = offset
|
|
}
|
|
}
|
|
|
|
jobs, totalCount, err := h.service.ListJobs(c.Request.Context(), opts)
|
|
if err != nil {
|
|
h.logger.Error("Failed to list jobs", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list jobs"})
|
|
return
|
|
}
|
|
|
|
if jobs == nil {
|
|
jobs = []Job{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"jobs": jobs,
|
|
"total": totalCount,
|
|
"limit": opts.Limit,
|
|
"offset": opts.Offset,
|
|
})
|
|
}
|
|
|
|
// GetJob retrieves a job by ID
|
|
func (h *Handler) GetJob(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
job, err := h.service.GetJob(c.Request.Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "job not found" {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "job not found"})
|
|
return
|
|
}
|
|
h.logger.Error("Failed to get job", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get job"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, job)
|
|
}
|
|
|
|
// CreateJob creates a new backup job
|
|
func (h *Handler) CreateJob(c *gin.Context) {
|
|
var req CreateJobRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Validate job type
|
|
validJobTypes := map[string]bool{
|
|
"Backup": true, "Restore": true, "Verify": true, "Copy": true, "Migrate": true,
|
|
}
|
|
if !validJobTypes[req.JobType] {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid job_type"})
|
|
return
|
|
}
|
|
|
|
// Validate job level
|
|
validJobLevels := map[string]bool{
|
|
"Full": true, "Incremental": true, "Differential": true, "Since": true,
|
|
}
|
|
if !validJobLevels[req.JobLevel] {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid job_level"})
|
|
return
|
|
}
|
|
|
|
job, err := h.service.CreateJob(c.Request.Context(), req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create job", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create job"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, job)
|
|
}
|
|
|
|
// ExecuteBconsoleCommand executes a bconsole command
|
|
func (h *Handler) ExecuteBconsoleCommand(c *gin.Context) {
|
|
var req struct {
|
|
Command string `json:"command" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "command is required"})
|
|
return
|
|
}
|
|
|
|
output, err := h.service.ExecuteBconsoleCommand(c.Request.Context(), req.Command)
|
|
if err != nil {
|
|
h.logger.Error("Failed to execute bconsole command", "error", err, "command", req.Command)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"error": "failed to execute command",
|
|
"output": output,
|
|
"details": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"output": output,
|
|
})
|
|
}
|
|
|
|
// ListClients lists all backup clients with optional filters
|
|
func (h *Handler) ListClients(c *gin.Context) {
|
|
opts := ListClientsOptions{}
|
|
|
|
// Parse enabled filter
|
|
if enabledStr := c.Query("enabled"); enabledStr != "" {
|
|
enabled := enabledStr == "true"
|
|
opts.Enabled = &enabled
|
|
}
|
|
|
|
// Parse search query
|
|
opts.Search = c.Query("search")
|
|
|
|
clients, err := h.service.ListClients(c.Request.Context(), opts)
|
|
if err != nil {
|
|
h.logger.Error("Failed to list clients", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"error": "failed to list clients",
|
|
"details": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
if clients == nil {
|
|
clients = []Client{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"clients": clients,
|
|
"total": len(clients),
|
|
})
|
|
}
|
|
|
|
// GetDashboardStats returns dashboard statistics
|
|
func (h *Handler) GetDashboardStats(c *gin.Context) {
|
|
stats, err := h.service.GetDashboardStats(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to get dashboard stats", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get dashboard stats"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, stats)
|
|
}
|
|
|
|
// ListStoragePools lists all storage pools
|
|
func (h *Handler) ListStoragePools(c *gin.Context) {
|
|
pools, err := h.service.ListStoragePools(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to list storage pools", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list storage pools"})
|
|
return
|
|
}
|
|
|
|
if pools == nil {
|
|
pools = []StoragePool{}
|
|
}
|
|
|
|
h.logger.Info("Listed storage pools", "count", len(pools))
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"pools": pools,
|
|
"total": len(pools),
|
|
})
|
|
}
|
|
|
|
// ListStorageVolumes lists all storage volumes
|
|
func (h *Handler) ListStorageVolumes(c *gin.Context) {
|
|
poolName := c.Query("pool_name")
|
|
|
|
volumes, err := h.service.ListStorageVolumes(c.Request.Context(), poolName)
|
|
if err != nil {
|
|
h.logger.Error("Failed to list storage volumes", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list storage volumes"})
|
|
return
|
|
}
|
|
|
|
if volumes == nil {
|
|
volumes = []StorageVolume{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"volumes": volumes,
|
|
"total": len(volumes),
|
|
})
|
|
}
|
|
|
|
// ListStorageDaemons lists all storage daemons
|
|
func (h *Handler) ListStorageDaemons(c *gin.Context) {
|
|
daemons, err := h.service.ListStorageDaemons(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to list storage daemons", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list storage daemons"})
|
|
return
|
|
}
|
|
|
|
if daemons == nil {
|
|
daemons = []StorageDaemon{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"daemons": daemons,
|
|
"total": len(daemons),
|
|
})
|
|
}
|
|
|
|
// CreateStoragePool creates a new storage pool
|
|
func (h *Handler) CreateStoragePool(c *gin.Context) {
|
|
var req CreatePoolRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
pool, err := h.service.CreateStoragePool(c.Request.Context(), req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create storage pool", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, pool)
|
|
}
|
|
|
|
// DeleteStoragePool deletes a storage pool
|
|
func (h *Handler) DeleteStoragePool(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
if idStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "pool ID is required"})
|
|
return
|
|
}
|
|
|
|
var poolID int
|
|
if _, err := fmt.Sscanf(idStr, "%d", &poolID); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid pool ID"})
|
|
return
|
|
}
|
|
|
|
err := h.service.DeleteStoragePool(c.Request.Context(), poolID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to delete storage pool", "error", err, "pool_id", poolID)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "pool deleted successfully"})
|
|
}
|
|
|
|
// CreateStorageVolume creates a new storage volume
|
|
func (h *Handler) CreateStorageVolume(c *gin.Context) {
|
|
var req CreateVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
volume, err := h.service.CreateStorageVolume(c.Request.Context(), req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create storage volume", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, volume)
|
|
}
|
|
|
|
// UpdateStorageVolume updates a storage volume
|
|
func (h *Handler) UpdateStorageVolume(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
if idStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "volume ID is required"})
|
|
return
|
|
}
|
|
|
|
var volumeID int
|
|
if _, err := fmt.Sscanf(idStr, "%d", &volumeID); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid volume ID"})
|
|
return
|
|
}
|
|
|
|
var req UpdateVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
volume, err := h.service.UpdateStorageVolume(c.Request.Context(), volumeID, req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to update storage volume", "error", err, "volume_id", volumeID)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, volume)
|
|
}
|
|
|
|
// DeleteStorageVolume deletes a storage volume
|
|
func (h *Handler) DeleteStorageVolume(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
if idStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "volume ID is required"})
|
|
return
|
|
}
|
|
|
|
var volumeID int
|
|
if _, err := fmt.Sscanf(idStr, "%d", &volumeID); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid volume ID"})
|
|
return
|
|
}
|
|
|
|
err := h.service.DeleteStorageVolume(c.Request.Context(), volumeID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to delete storage volume", "error", err, "volume_id", volumeID)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "volume deleted successfully"})
|
|
}
|
|
|
|
// ListMedia lists all media from bconsole "list media" command
|
|
func (h *Handler) ListMedia(c *gin.Context) {
|
|
media, err := h.service.ListMedia(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to list media", "error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if media == nil {
|
|
media = []Media{}
|
|
}
|
|
|
|
h.logger.Info("Listed media", "count", len(media))
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"media": media,
|
|
"total": len(media),
|
|
})
|
|
}
|