BackupController.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. using System.IO;
  2. using System.Threading.Tasks;
  3. using Jellyfin.Server.Implementations.SystemBackupService;
  4. using MediaBrowser.Common.Api;
  5. using MediaBrowser.Common.Configuration;
  6. using MediaBrowser.Controller.SystemBackupService;
  7. using Microsoft.AspNetCore.Authentication.OAuth.Claims;
  8. using Microsoft.AspNetCore.Authorization;
  9. using Microsoft.AspNetCore.Http;
  10. using Microsoft.AspNetCore.Mvc;
  11. using Microsoft.AspNetCore.Mvc.ModelBinding;
  12. namespace Jellyfin.Api.Controllers;
  13. /// <summary>
  14. /// The backup controller.
  15. /// </summary>
  16. [Authorize(Policy = Policies.RequiresElevation)]
  17. public class BackupController : BaseJellyfinApiController
  18. {
  19. private readonly IBackupService _backupService;
  20. private readonly IApplicationPaths _applicationPaths;
  21. /// <summary>
  22. /// Initializes a new instance of the <see cref="BackupController"/> class.
  23. /// </summary>
  24. /// <param name="backupService">Instance of the <see cref="IBackupService"/> interface.</param>
  25. /// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
  26. public BackupController(IBackupService backupService, IApplicationPaths applicationPaths)
  27. {
  28. _backupService = backupService;
  29. _applicationPaths = applicationPaths;
  30. }
  31. /// <summary>
  32. /// Creates a new Backup.
  33. /// </summary>
  34. /// <param name="backupOptions">The backup options.</param>
  35. /// <response code="200">Backup created.</response>
  36. /// <response code="403">User does not have permission to retrieve information.</response>
  37. /// <returns>The created backup manifest.</returns>
  38. [HttpPost("Create")]
  39. [ProducesResponseType(StatusCodes.Status200OK)]
  40. [ProducesResponseType(StatusCodes.Status403Forbidden)]
  41. public async Task<ActionResult<BackupManifestDto>> CreateBackup([FromBody] BackupOptionsDto backupOptions)
  42. {
  43. return Ok(await _backupService.CreateBackupAsync(backupOptions ?? new()).ConfigureAwait(false));
  44. }
  45. /// <summary>
  46. /// Restores to a backup by restarting the server and applying the backup.
  47. /// </summary>
  48. /// <param name="archiveRestoreDto">The data to start a restore process.</param>
  49. /// <response code="204">Backup restore started.</response>
  50. /// <response code="403">User does not have permission to retrieve information.</response>
  51. /// <returns>No-Content.</returns>
  52. [HttpPost("Restore")]
  53. [ProducesResponseType(StatusCodes.Status204NoContent)]
  54. [ProducesResponseType(StatusCodes.Status404NotFound)]
  55. [ProducesResponseType(StatusCodes.Status403Forbidden)]
  56. public IActionResult StartRestoreBackup([FromBody, BindRequired] BackupRestoreRequestDto archiveRestoreDto)
  57. {
  58. var archivePath = SanitizePath(archiveRestoreDto.ArchiveFileName);
  59. if (!System.IO.File.Exists(archivePath))
  60. {
  61. return NotFound();
  62. }
  63. _backupService.ScheduleRestoreAndRestartServer(archivePath);
  64. return NoContent();
  65. }
  66. /// <summary>
  67. /// Gets a list of all currently present backups in the backup directory.
  68. /// </summary>
  69. /// <response code="200">Backups available.</response>
  70. /// <response code="403">User does not have permission to retrieve information.</response>
  71. /// <returns>The list of backups.</returns>
  72. [HttpGet]
  73. [ProducesResponseType(StatusCodes.Status200OK)]
  74. [ProducesResponseType(StatusCodes.Status403Forbidden)]
  75. public async Task<ActionResult<BackupManifestDto[]>> ListBackups()
  76. {
  77. return Ok(await _backupService.EnumerateBackups().ConfigureAwait(false));
  78. }
  79. /// <summary>
  80. /// Gets the descriptor from an existing archive is present.
  81. /// </summary>
  82. /// <param name="path">The data to start a restore process.</param>
  83. /// <response code="200">Backup archive manifest.</response>
  84. /// <response code="204">Not a valid jellyfin Archive.</response>
  85. /// <response code="404">Not a valid path.</response>
  86. /// <response code="403">User does not have permission to retrieve information.</response>
  87. /// <returns>The backup manifest.</returns>
  88. [HttpGet("Manifest")]
  89. [ProducesResponseType(StatusCodes.Status200OK)]
  90. [ProducesResponseType(StatusCodes.Status204NoContent)]
  91. [ProducesResponseType(StatusCodes.Status404NotFound)]
  92. [ProducesResponseType(StatusCodes.Status403Forbidden)]
  93. public async Task<ActionResult<BackupManifestDto>> GetBackup([BindRequired] string path)
  94. {
  95. var backupPath = SanitizePath(path);
  96. if (!System.IO.File.Exists(backupPath))
  97. {
  98. return NotFound();
  99. }
  100. var manifest = await _backupService.GetBackupManifest(backupPath).ConfigureAwait(false);
  101. if (manifest is null)
  102. {
  103. return NoContent();
  104. }
  105. return Ok(manifest);
  106. }
  107. [NonAction]
  108. private string SanitizePath(string path)
  109. {
  110. // sanitize path
  111. var archiveRestorePath = Path.GetFileName(Path.GetFullPath(path));
  112. var archivePath = Path.Combine(_applicationPaths.BackupPath, archiveRestorePath);
  113. return archivePath;
  114. }
  115. }