diff --git a/backend/src/Squidex.Shared/Permissions.cs b/backend/src/Squidex.Shared/Permissions.cs index f723e82d6..bc762bdbb 100644 --- a/backend/src/Squidex.Shared/Permissions.cs +++ b/backend/src/Squidex.Shared/Permissions.cs @@ -112,6 +112,7 @@ namespace Squidex.Shared public const string AppBackupsRead = "squidex.apps.{app}.backups.read"; public const string AppBackupsCreate = "squidex.apps.{app}.backups.create"; public const string AppBackupsDelete = "squidex.apps.{app}.backups.delete"; + public const string AppBackupsDownload = "squidex.apps.{app}.backups.download"; // Plans public const string AppPlans = "squidex.apps.{app}.plans"; diff --git a/backend/src/Squidex.Web/Resources.cs b/backend/src/Squidex.Web/Resources.cs index 0bcf96f2f..d87f99999 100644 --- a/backend/src/Squidex.Web/Resources.cs +++ b/backend/src/Squidex.Web/Resources.cs @@ -169,9 +169,15 @@ namespace Squidex.Web [Lazy] public bool CanDeleteBackup => IsAllowed(Permissions.AppBackupsDelete); + [Lazy] + public bool CanDownloadBackup => IsAllowed(Permissions.AppBackupsDownload); + [Lazy] public string? App => GetAppName(); + [Lazy] + public DomainId AppId => GetAppId(); + public ApiController Controller { get; } public Context Context { get; set; } @@ -244,5 +250,10 @@ namespace Squidex.Web { return Controller.HttpContext.Context().App?.Name; } + + private DomainId GetAppId() + { + return Controller.HttpContext.Context().App?.Id ?? default; + } } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs index c2a00c9c3..20dfb5532 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs @@ -36,7 +36,7 @@ namespace Squidex.Areas.Api.Controllers.Backups /// Get the backup content. /// /// The name of the app. - /// The id of the asset. + /// The id of the backup. /// /// 200 => Backup found and content returned. /// 404 => Backup or app not found. @@ -47,9 +47,35 @@ namespace Squidex.Areas.Api.Controllers.Backups [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] [ApiCosts(0)] [AllowAnonymous] - public async Task GetBackupContent(string app, DomainId id) + public Task GetBackupContent(string app, DomainId id) { - var backup = await backupservice.GetBackupAsync(AppId, id, HttpContext.RequestAborted); + return BackBackupAsync(AppId, app, id); + } + + /// + /// Get the backup content. + /// + /// The id of the backup. + /// The id of the app. + /// The name of the app. + /// + /// 200 => Backup found and content returned. + /// 404 => Backup or app not found. + /// + [HttpGet] + [Route("apps/backups/{id}")] + [ResponseCache(Duration = 3600 * 24 * 30)] + [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] + [ApiCosts(0)] + [AllowAnonymous] + public Task GetBackupContentV2(DomainId id, [FromQuery] DomainId appId = default, [FromQuery] string app = "") + { + return BackBackupAsync(appId, app, id); + } + + private async Task BackBackupAsync(DomainId appId, string app, DomainId id) + { + var backup = await backupservice.GetBackupAsync(appId, id, HttpContext.RequestAborted); if (backup == null || backup.Status != JobStatus.Completed) { @@ -65,7 +91,9 @@ namespace Squidex.Areas.Api.Controllers.Backups return new FileCallbackResult("application/zip", callback) { - FileDownloadName = fileName + FileDownloadName = fileName, + FileSize = null, + ErrorAs404 = true }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobDto.cs index b961244a0..726e754ca 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobDto.cs @@ -54,14 +54,20 @@ namespace Squidex.Areas.Api.Controllers.Backups.Models private BackupJobDto CreateLinks(Resources resources) { - var values = new { app = resources.App, id = Id }; - if (resources.CanDeleteBackup) { + var values = new { app = resources.App, id = Id }; + AddDeleteLink("delete", resources.Url(x => nameof(x.DeleteBackup), values)); } - AddGetLink("download", resources.Url(x => nameof(x.GetBackupContent), values)); + if (resources.CanDownloadBackup) + { + var values = new { app = resources.App, appId = resources.AppId, id = Id }; + + AddGetLink("download", resources.Url(x => nameof(x.GetBackupContentV2), values)); + } + return this; }