Files
Novault-backend/internal/handler/backup_handler.go
2026-01-25 21:59:00 +08:00

139 lines
4.3 KiB
Go

package handler
import (
"net/http"
"accounting-app/pkg/api"
"accounting-app/internal/service"
"github.com/gin-gonic/gin"
)
// BackupHandler handles HTTP requests for backup operations
type BackupHandler struct {
service *service.BackupService
}
// NewBackupHandler creates a new BackupHandler instance
func NewBackupHandler(service *service.BackupService) *BackupHandler {
return &BackupHandler{
service: service,
}
}
// ExportBackup handles POST /api/v1/backup/export
// @Summary Export encrypted database backup
// @Description Creates an encrypted backup of the database with SHA256 integrity checking
// @Tags backup
// @Accept json
// @Produce json
// @Param request body service.BackupRequest true "Backup request"
// @Success 200 {object} service.BackupResponse
// @Failure 400 {object} api.ErrorResponse
// @Failure 500 {object} api.ErrorResponse
// @Router /api/v1/backup/export [post]
func (h *BackupHandler) ExportBackup(c *gin.Context) {
var req service.BackupRequest
if err := c.ShouldBindJSON(&req); err != nil {
api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request: "+err.Error())
return
}
response, err := h.service.ExportBackup(req)
if err != nil {
if err == service.ErrInvalidPassword {
api.Error(c, http.StatusBadRequest, "INVALID_PASSWORD", err.Error())
return
}
api.Error(c, http.StatusInternalServerError, "BACKUP_FAILED", "Failed to create backup: "+err.Error())
return
}
api.Success(c, response)
}
// ImportBackup handles POST /api/v1/backup/import
// @Summary Import and restore from encrypted backup
// @Description Restores the database from an encrypted backup file with integrity verification
// @Tags backup
// @Accept json
// @Produce json
// @Param request body service.RestoreRequest true "Restore request"
// @Success 200 {object} api.SuccessResponse
// @Failure 400 {object} api.ErrorResponse
// @Failure 500 {object} api.ErrorResponse
// @Router /api/v1/backup/import [post]
func (h *BackupHandler) ImportBackup(c *gin.Context) {
var req service.RestoreRequest
if err := c.ShouldBindJSON(&req); err != nil {
api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request: "+err.Error())
return
}
err := h.service.ImportBackup(req)
if err != nil {
switch err {
case service.ErrInvalidBackupFile:
api.Error(c, http.StatusBadRequest, "INVALID_BACKUP_FILE", "Invalid backup file format")
return
case service.ErrChecksumMismatch:
api.Error(c, http.StatusBadRequest, "CHECKSUM_MISMATCH", "Backup file integrity check failed")
return
case service.ErrDecryptionFailed:
api.Error(c, http.StatusBadRequest, "DECRYPTION_FAILED", "Failed to decrypt backup - incorrect password")
return
default:
api.Error(c, http.StatusInternalServerError, "RESTORE_FAILED", "Failed to restore backup: "+err.Error())
return
}
}
api.Success(c, gin.H{
"message": "Database restored successfully from backup",
})
}
// VerifyBackup handles POST /api/v1/backup/verify
// @Summary Verify backup file integrity
// @Description Verifies the integrity of a backup file without restoring it
// @Tags backup
// @Accept json
// @Produce json
// @Param request body map[string]string true "Verify request with file_path"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} api.ErrorResponse
// @Failure 500 {object} api.ErrorResponse
// @Router /api/v1/backup/verify [post]
func (h *BackupHandler) VerifyBackup(c *gin.Context) {
var req struct {
FilePath string `json:"file_path" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request: "+err.Error())
return
}
isValid, checksum, err := h.service.VerifyBackup(req.FilePath)
if err != nil {
if err == service.ErrInvalidBackupFile {
api.Error(c, http.StatusBadRequest, "INVALID_BACKUP_FILE", "Invalid backup file format")
return
}
api.Error(c, http.StatusInternalServerError, "VERIFY_FAILED", "Failed to verify backup: "+err.Error())
return
}
api.Success(c, gin.H{
"valid": isValid,
"checksum": checksum,
"message": getVerificationMessage(isValid),
})
}
func getVerificationMessage(isValid bool) string {
if isValid {
return "Backup file integrity verified successfully"
}
return "Backup file integrity check failed - file may be corrupted"
}