restore.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package system
  2. import (
  3. "encoding/base64"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "time"
  9. "github.com/0xJacky/Nginx-UI/internal/backup"
  10. "github.com/gin-gonic/gin"
  11. "github.com/jpillora/overseer"
  12. "github.com/uozi-tech/cosy"
  13. "github.com/uozi-tech/cosy/logger"
  14. )
  15. // RestoreResponse contains the response data for restore operation
  16. type RestoreResponse struct {
  17. NginxUIRestored bool `json:"nginx_ui_restored"`
  18. NginxRestored bool `json:"nginx_restored"`
  19. HashMatch bool `json:"hash_match"`
  20. }
  21. // RestoreBackup restores from uploaded backup and security info
  22. func RestoreBackup(c *gin.Context) {
  23. // Get restore options
  24. restoreNginx := c.PostForm("restore_nginx") == "true"
  25. restoreNginxUI := c.PostForm("restore_nginx_ui") == "true"
  26. verifyHash := c.PostForm("verify_hash") == "true"
  27. securityToken := c.PostForm("security_token") // Get concatenated key and IV
  28. logger.Debug("restoreNginx", restoreNginx)
  29. logger.Debug("restoreNginxUI", restoreNginxUI)
  30. logger.Debug("verifyHash", verifyHash)
  31. logger.Debug("securityToken", securityToken)
  32. // Get backup file
  33. backupFile, err := c.FormFile("backup_file")
  34. if err != nil {
  35. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrBackupFileNotFound, err.Error()))
  36. return
  37. }
  38. logger.Debug("backupFile", backupFile.Size)
  39. // Validate security token
  40. if securityToken == "" {
  41. cosy.ErrHandler(c, backup.ErrInvalidSecurityToken)
  42. return
  43. }
  44. // Split security token to get Key and IV
  45. parts := strings.Split(securityToken, ":")
  46. if len(parts) != 2 {
  47. cosy.ErrHandler(c, backup.ErrInvalidSecurityToken)
  48. return
  49. }
  50. aesKey := parts[0]
  51. aesIv := parts[1]
  52. // Decode Key and IV from base64
  53. key, err := base64.StdEncoding.DecodeString(aesKey)
  54. if err != nil {
  55. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESKey, err.Error()))
  56. return
  57. }
  58. iv, err := base64.StdEncoding.DecodeString(aesIv)
  59. if err != nil {
  60. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESIV, err.Error()))
  61. return
  62. }
  63. // Create temporary directory for files
  64. tempDir, err := os.MkdirTemp("", "nginx-ui-restore-upload-*")
  65. if err != nil {
  66. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateTempDir, err.Error()))
  67. return
  68. }
  69. defer os.RemoveAll(tempDir)
  70. // Save backup file
  71. backupPath := filepath.Join(tempDir, backupFile.Filename)
  72. if err := c.SaveUploadedFile(backupFile, backupPath); err != nil {
  73. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateBackupFile, err.Error()))
  74. return
  75. }
  76. // Create temporary directory for restore operation
  77. restoreDir, err := os.MkdirTemp("", "nginx-ui-restore-*")
  78. if err != nil {
  79. cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateRestoreDir, err.Error()))
  80. return
  81. }
  82. // Set restore options
  83. options := backup.RestoreOptions{
  84. BackupPath: backupPath,
  85. AESKey: key,
  86. AESIv: iv,
  87. RestoreDir: restoreDir,
  88. RestoreNginx: restoreNginx,
  89. RestoreNginxUI: restoreNginxUI,
  90. VerifyHash: verifyHash,
  91. }
  92. // Perform restore
  93. result, err := backup.Restore(options)
  94. if err != nil {
  95. // Clean up temporary directory on error
  96. os.RemoveAll(restoreDir)
  97. cosy.ErrHandler(c, err)
  98. return
  99. }
  100. // If not actually restoring anything, clean up directory to avoid disk space waste
  101. if !restoreNginx && !restoreNginxUI {
  102. defer os.RemoveAll(restoreDir)
  103. }
  104. if restoreNginxUI {
  105. go func() {
  106. time.Sleep(3 * time.Second)
  107. // gracefully restart
  108. overseer.Restart()
  109. }()
  110. }
  111. c.JSON(http.StatusOK, RestoreResponse{
  112. NginxUIRestored: result.NginxUIRestored,
  113. NginxRestored: result.NginxRestored,
  114. HashMatch: result.HashMatch,
  115. })
  116. }