using System.IO; using System.Threading.Tasks; using Jellyfin.Server.Implementations.SystemBackupService; using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.SystemBackupService; using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Jellyfin.Api.Controllers; /// /// The backup controller. /// [Authorize(Policy = Policies.RequiresElevation)] public class BackupController : BaseJellyfinApiController { private readonly IBackupService _backupService; private readonly IApplicationPaths _applicationPaths; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. public BackupController(IBackupService backupService, IApplicationPaths applicationPaths) { _backupService = backupService; _applicationPaths = applicationPaths; } /// /// Creates a new Backup. /// /// The backup options. /// Backup created. /// User does not have permission to retrieve information. /// The created backup manifest. [HttpPost("Create")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> CreateBackup([FromBody] BackupOptionsDto backupOptions) { return Ok(await _backupService.CreateBackupAsync(backupOptions ?? new()).ConfigureAwait(false)); } /// /// Restores to a backup by restarting the server and applying the backup. /// /// The data to start a restore process. /// Backup restore started. /// User does not have permission to retrieve information. /// No-Content. [HttpPost("Restore")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public IActionResult StartRestoreBackup([FromBody, BindRequired] BackupRestoreRequestDto archiveRestoreDto) { var archivePath = SanitizePath(archiveRestoreDto.ArchiveFileName); if (!System.IO.File.Exists(archivePath)) { return NotFound(); } _backupService.ScheduleRestoreAndRestartServer(archivePath); return NoContent(); } /// /// Gets a list of all currently present backups in the backup directory. /// /// Backups available. /// User does not have permission to retrieve information. /// The list of backups. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> ListBackups() { return Ok(await _backupService.EnumerateBackups().ConfigureAwait(false)); } /// /// Gets the descriptor from an existing archive is present. /// /// The data to start a restore process. /// Backup archive manifest. /// Not a valid jellyfin Archive. /// Not a valid path. /// User does not have permission to retrieve information. /// The backup manifest. [HttpGet("Manifest")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> GetBackup([BindRequired] string path) { var backupPath = SanitizePath(path); if (!System.IO.File.Exists(backupPath)) { return NotFound(); } var manifest = await _backupService.GetBackupManifest(backupPath).ConfigureAwait(false); if (manifest is null) { return NoContent(); } return Ok(manifest); } [NonAction] private string SanitizePath(string path) { // sanitize path var archiveRestorePath = Path.GetFileName(Path.GetFullPath(path)); var archivePath = Path.Combine(_applicationPaths.BackupPath, archiveRestorePath); return archivePath; } }