package system import ( "encoding/base64" "net/http" "os" "path/filepath" "strings" "time" "github.com/0xJacky/Nginx-UI/internal/backup" "github.com/gin-gonic/gin" "github.com/jpillora/overseer" "github.com/uozi-tech/cosy" "github.com/uozi-tech/cosy/logger" ) // RestoreResponse contains the response data for restore operation type RestoreResponse struct { NginxUIRestored bool `json:"nginx_ui_restored"` NginxRestored bool `json:"nginx_restored"` HashMatch bool `json:"hash_match"` } // RestoreBackup restores from uploaded backup and security info func RestoreBackup(c *gin.Context) { // Get restore options restoreNginx := c.PostForm("restore_nginx") == "true" restoreNginxUI := c.PostForm("restore_nginx_ui") == "true" verifyHash := c.PostForm("verify_hash") == "true" securityToken := c.PostForm("security_token") // Get concatenated key and IV logger.Debug("restoreNginx", restoreNginx) logger.Debug("restoreNginxUI", restoreNginxUI) logger.Debug("verifyHash", verifyHash) logger.Debug("securityToken", securityToken) // Get backup file backupFile, err := c.FormFile("backup_file") if err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrBackupFileNotFound, err.Error())) return } logger.Debug("backupFile", backupFile.Size) // Validate security token if securityToken == "" { cosy.ErrHandler(c, backup.ErrInvalidSecurityToken) return } // Split security token to get Key and IV parts := strings.Split(securityToken, ":") if len(parts) != 2 { cosy.ErrHandler(c, backup.ErrInvalidSecurityToken) return } aesKey := parts[0] aesIv := parts[1] // Decode Key and IV from base64 key, err := base64.StdEncoding.DecodeString(aesKey) if err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESKey, err.Error())) return } iv, err := base64.StdEncoding.DecodeString(aesIv) if err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESIV, err.Error())) return } // Create temporary directory for files tempDir, err := os.MkdirTemp("", "nginx-ui-restore-upload-*") if err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateTempDir, err.Error())) return } defer os.RemoveAll(tempDir) // Save backup file backupPath := filepath.Join(tempDir, backupFile.Filename) if err := c.SaveUploadedFile(backupFile, backupPath); err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateBackupFile, err.Error())) return } // Create temporary directory for restore operation restoreDir, err := os.MkdirTemp("", "nginx-ui-restore-*") if err != nil { cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateRestoreDir, err.Error())) return } // Set restore options options := backup.RestoreOptions{ BackupPath: backupPath, AESKey: key, AESIv: iv, RestoreDir: restoreDir, RestoreNginx: restoreNginx, RestoreNginxUI: restoreNginxUI, VerifyHash: verifyHash, } // Perform restore result, err := backup.Restore(options) if err != nil { // Clean up temporary directory on error os.RemoveAll(restoreDir) cosy.ErrHandler(c, err) return } // If not actually restoring anything, clean up directory to avoid disk space waste if !restoreNginx && !restoreNginxUI { defer os.RemoveAll(restoreDir) } if restoreNginxUI { go func() { time.Sleep(3 * time.Second) // gracefully restart overseer.Restart() }() } c.JSON(http.StatusOK, RestoreResponse{ NginxUIRestored: result.NginxUIRestored, NginxRestored: result.NginxRestored, HashMatch: result.HashMatch, }) }