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;
}