diff --git a/backend/src/Migrations/RebuildOptions.cs b/backend/src/Migrations/RebuildOptions.cs index e16e0a9df..345d3808f 100644 --- a/backend/src/Migrations/RebuildOptions.cs +++ b/backend/src/Migrations/RebuildOptions.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; + namespace Migrations { public sealed class RebuildOptions @@ -22,5 +24,12 @@ namespace Migrations public bool Rules { get; set; } public bool Schemas { get; set; } + + public int BatchSize { get; set; } = 100; + + public int CalculateBatchSize() + { + return Math.Max(10, Math.Min(1000, BatchSize)); + } } } diff --git a/backend/src/Migrations/RebuildRunner.cs b/backend/src/Migrations/RebuildRunner.cs index b4c15a569..e56944ba2 100644 --- a/backend/src/Migrations/RebuildRunner.cs +++ b/backend/src/Migrations/RebuildRunner.cs @@ -41,25 +41,27 @@ namespace Migrations public async Task RunAsync(CancellationToken ct) { + var batchSize = rebuildOptions.CalculateBatchSize(); + if (rebuildOptions.Apps) { - await rebuilder.RebuildAppsAsync(ct); + await rebuilder.RebuildAppsAsync(batchSize, ct); } if (rebuildOptions.Schemas) { - await rebuilder.RebuildSchemasAsync(ct); + await rebuilder.RebuildSchemasAsync(batchSize, ct); } if (rebuildOptions.Rules) { - await rebuilder.RebuildRulesAsync(ct); + await rebuilder.RebuildRulesAsync(batchSize, ct); } if (rebuildOptions.Assets) { - await rebuilder.RebuildAssetsAsync(ct); - await rebuilder.RebuildAssetFoldersAsync(ct); + await rebuilder.RebuildAssetsAsync(batchSize, ct); + await rebuilder.RebuildAssetFoldersAsync(batchSize, ct); } if (rebuildOptions.AssetFiles) @@ -69,7 +71,7 @@ namespace Migrations if (rebuildOptions.Contents) { - await rebuilder.RebuildContentAsync(ct); + await rebuilder.RebuildContentAsync(batchSize, ct); } if (rebuildOptions.Indexes) diff --git a/backend/src/Migrations/RebuilderExtensions.cs b/backend/src/Migrations/RebuilderExtensions.cs index b7260c5be..d6e3847c2 100644 --- a/backend/src/Migrations/RebuilderExtensions.cs +++ b/backend/src/Migrations/RebuilderExtensions.cs @@ -18,34 +18,34 @@ namespace Migrations { public static class RebuilderExtensions { - public static Task RebuildAppsAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildAppsAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^app\\-", ct); + return rebuilder.RebuildAsync("^app\\-", batchSize, ct); } - public static Task RebuildSchemasAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildSchemasAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^schema\\-", ct); + return rebuilder.RebuildAsync("^schema\\-", batchSize, ct); } - public static Task RebuildRulesAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildRulesAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^rule\\-", ct); + return rebuilder.RebuildAsync("^rule\\-", batchSize, ct); } - public static Task RebuildAssetsAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildAssetsAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^asset\\-", ct); + return rebuilder.RebuildAsync("^asset\\-", batchSize, ct); } - public static Task RebuildAssetFoldersAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildAssetFoldersAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^assetFolder\\-", ct); + return rebuilder.RebuildAsync("^assetFolder\\-", batchSize, ct); } - public static Task RebuildContentAsync(this Rebuilder rebuilder, CancellationToken ct = default) + public static Task RebuildContentAsync(this Rebuilder rebuilder, int batchSize, CancellationToken ct = default) { - return rebuilder.RebuildAsync("^content\\-", ct); + return rebuilder.RebuildAsync("^content\\-", batchSize, ct); } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs index 65921207b..cd5b8bd6a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs @@ -21,6 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Assets { public sealed class BackupAssets : IBackupHandler { + private const int BatchSize = 100; private const string TagsFile = "AssetTags.json"; private readonly HashSet assetIds = new HashSet(); private readonly HashSet assetFolderIds = new HashSet(); @@ -101,12 +102,12 @@ namespace Squidex.Domain.Apps.Entities.Assets if (assetIds.Count > 0) { - await rebuilder.InsertManyAsync(assetIds); + await rebuilder.InsertManyAsync(assetIds, BatchSize); } if (assetFolderIds.Count > 0) { - await rebuilder.InsertManyAsync(assetFolderIds); + await rebuilder.InsertManyAsync(assetFolderIds, BatchSize); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index db70680b4..747a6fb7d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -356,7 +356,7 @@ namespace Squidex.Domain.Apps.Entities.Backup var batchBlock = new BatchBlock<(string, Envelope)>(BatchSize, new GroupingDataflowBlockOptions { - BoundedCapacity = BatchSize + BoundedCapacity = BatchSize * 2 }); batchBlock.LinkTo(writeBlock, new DataflowLinkOptions diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs index 8d04a2742..e79ad9a0e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs @@ -24,6 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents { public sealed class BackupContents : IBackupHandler { + private const int BatchSize = 100; private delegate void ObjectSetter(IReadOnlyDictionary obj, string key, IJsonValue value); private const string UrlsFile = "Urls.json"; @@ -216,7 +217,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (ids.Any()) { - await rebuilder.InsertManyAsync(ids); + await rebuilder.InsertManyAsync(ids, BatchSize); } } diff --git a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs index 22e506198..454c44fec 100644 --- a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs +++ b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs @@ -19,8 +19,6 @@ using Squidex.Infrastructure.States; namespace Squidex.Infrastructure.Commands { - public delegate Task IdSource(Func add); - public class Rebuilder { private readonly ILocalCache localCache; @@ -51,7 +49,7 @@ namespace Squidex.Infrastructure.Commands this.localCache = localCache; } - public virtual async Task RebuildAsync(string filter, CancellationToken ct) where T : DomainObject where TState : class, IDomainState, new() + public virtual async Task RebuildAsync(string filter, int batchSize, CancellationToken ct = default) where T : DomainObject where TState : class, IDomainState, new() { var store = serviceProvider.GetRequiredService>(); @@ -65,12 +63,13 @@ namespace Squidex.Infrastructure.Commands await target(id); } - }, ct); + }, batchSize, ct); } - public virtual async Task InsertManyAsync(IEnumerable source, CancellationToken ct = default) where T : DomainObject where TState : class, IDomainState, new() + public virtual async Task InsertManyAsync(IEnumerable source, int batchSize, CancellationToken ct = default) where T : DomainObject where TState : class, IDomainState, new() { Guard.NotNull(source, nameof(source)); + Guard.Between(batchSize, 1, 1000, nameof(batchSize)); var store = serviceProvider.GetRequiredService>(); @@ -80,15 +79,13 @@ namespace Squidex.Infrastructure.Commands { await target(id); } - }, ct); + }, batchSize, ct); } - private async Task InsertManyAsync(IStore store, IdSource source, CancellationToken ct = default) where T : DomainObject where TState : class, IDomainState, new() + private async Task InsertManyAsync(IStore store, Func, Task> source, int batchSize, CancellationToken ct = default) where T : DomainObject where TState : class, IDomainState, new() { var parallelism = Environment.ProcessorCount; - const int BatchSize = 100; - var workerBlock = new ActionBlock(async ids => { try @@ -127,9 +124,9 @@ namespace Squidex.Infrastructure.Commands BoundedCapacity = parallelism }); - var batchBlock = new BatchBlock(BatchSize, new GroupingDataflowBlockOptions + var batchBlock = new BatchBlock(batchSize, new GroupingDataflowBlockOptions { - BoundedCapacity = BatchSize + BoundedCapacity = batchSize }); batchBlock.LinkTo(workerBlock, new DataflowLinkOptions @@ -157,4 +154,4 @@ namespace Squidex.Infrastructure.Commands } } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml index e158ff8b1..519cc0630 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml @@ -1,6 +1,4 @@ @{ - ViewBag.ThemeColor = "white"; - ViewBag.Title = T.Get("users.accessDenied.title"); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml index ae8c1f74c..563d65e59 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml @@ -1,9 +1,6 @@ @model Squidex.Areas.IdentityServer.Controllers.Account.ConsentVM @{ - ViewBag.ThemeColor = "white"; - ViewBag.ThemeSize = "profile-lg"; - ViewBag.Title = T.Get("users.consent.title"); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Account/LockedOut.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Account/LockedOut.cshtml index bd2cb8400..d11e8d3c0 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Account/LockedOut.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Account/LockedOut.cshtml @@ -1,6 +1,4 @@ @{ - ViewBag.ThemeColor = "white"; - ViewBag.Title = T.Get("users.lockedOutTitle"); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml index 4707bea2b..c614e8f9c 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml @@ -1,8 +1,6 @@ @model Squidex.Areas.IdentityServer.Controllers.Account.LoginVM @{ - ViewBag.ThemeColor = "white"; - var action = Model.IsLogin ? T.Get("common.login") : T.Get("common.signup"); ViewBag.Title = action; diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml index 07082b292..f7f882186 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml @@ -1,6 +1,4 @@ @{ - ViewBag.ThemeColor = "white"; - ViewBag.Title = T.Get("users.logout.title"); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml index 846bb03b5..388c1f086 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml @@ -1,8 +1,6 @@ @model Squidex.Areas.IdentityServer.Controllers.Error.ErrorVM @{ - ViewBag.ThemeColor = "white"; - ViewBag.Title = T.Get("users.error.title"); } @@ -11,9 +9,9 @@

@T.Get("users.error.headline")

- @if (Model.ErrorMessage != null) + @if (Model.Error?.ErrorDescription != null) { - @Model.ErrorMessage + @Model.Error.ErrorDescription } else { diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml index 98505dac7..ff1dcf9d5 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml @@ -1,9 +1,6 @@ @model Squidex.Areas.IdentityServer.Controllers.Profile.ProfileVM @{ - ViewBag.ThemeColor = "white"; - ViewBag.ThemeSize = "profile-lg"; - ViewBag.Title = T.Get("users.profile.title"); void RenderValidation(string field) @@ -279,7 +276,7 @@ } document.addEventListener('click', - function(event) { + function (event) { if (event.target.className.indexOf('remove-item') >= 0) { event.target.parentNode.parentNode.remove(); diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml index 69a853003..bf83a2182 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml @@ -1,9 +1,6 @@ @model Squidex.Areas.IdentityServer.Controllers.Setup.SetupVM @{ - ViewBag.ThemeColor = "gray"; - ViewBag.ThemeSize = "profile-lg"; - ViewBag.Title = T.Get("setup.title"); void RenderValidation(string field) @@ -68,134 +65,124 @@ } } -

-
-

@T.Get("setup.headline")

- - - - @T.Get("setup.hint") - -
-

@T.Get("setup.rules.headline")

- - @if (Model.IsValidHttps) - { - RenderRuleAsSuccess(T.Get("setup.ruleHttps.success")); - } - else - { - RenderRuleAsCritical(T.Get("setup.ruleHttps.failure")); - } - - @if (Model.BaseUrlConfigured == Model.BaseUrlCurrent) - { - RenderRuleAsSuccess(T.Get("setup.ruleUrl.success")); - } - else - { - RenderRuleAsCritical(T.Get("setup.ruleUrl.failure", new { actual = Model.BaseUrlCurrent, configured = Model.BaseUrlConfigured })); - } - - @if (Model.EverybodyCanCreateApps) - { - RenderRuleAsWarning(T.Get("setup.ruleAppCreation.warningAdmins")); - } - else - { - RenderRuleAsWarning(T.Get("setup.ruleAppCreation.warningAll")); - } - - @if (Model.IsAssetStoreFtp) - { - RenderRuleAsWarning(T.Get("setup.ruleFtp.warning")); - } - - @if (Model.IsAssetStoreFile) - { - RenderRuleAsWarning(T.Get("setup.ruleFolder.warning")); - } -
+

@T.Get("setup.headline")

-
+ -
-

@T.Get("setup.createUser.headline")

+@T.Get("setup.hint") - @if (Model.HasExternalLogin) - { -
- @T.Get("setup.createUser.loginHint") +
+

@T.Get("setup.rules.headline")

- -
- } + @if (Model.IsValidHttps) + { + RenderRuleAsSuccess(T.Get("setup.ruleHttps.success")); + } + else + { + RenderRuleAsCritical(T.Get("setup.ruleHttps.failure")); + } - @if (Model.HasExternalLogin && Model.HasPasswordAuth) - { -
-
@T.Get("setup.createUser.separator")
-
- } + @if (Model.BaseUrlConfigured == Model.BaseUrlCurrent) + { + RenderRuleAsSuccess(T.Get("setup.ruleUrl.success")); + } + else + { + RenderRuleAsCritical(T.Get("setup.ruleUrl.failure", new { actual = Model.BaseUrlCurrent, configured = Model.BaseUrlConfigured })); + } - @if (Model.HasPasswordAuth) - { -

@T.Get("setup.createUser.headlineCreate")

+ @if (Model.EverybodyCanCreateApps) + { + RenderRuleAsWarning(T.Get("setup.ruleAppCreation.warningAdmins")); + } + else + { + RenderRuleAsWarning(T.Get("setup.ruleAppCreation.warningAll")); + } - @if (!string.IsNullOrWhiteSpace(Model.ErrorMessage)) - { -
- @Model.ErrorMessage -
- } + @if (Model.IsAssetStoreFtp) + { + RenderRuleAsWarning(T.Get("setup.ruleFtp.warning")); + } -
-
- + @if (Model.IsAssetStoreFile) + { + RenderRuleAsWarning(T.Get("setup.ruleFolder.warning")); + } +
- @{ RenderValidation("Email"); } +
- -
+
+

@T.Get("setup.createUser.headline")

-
- + @if (Model.HasExternalLogin) + { +
+ @T.Get("setup.createUser.loginHint") - @{ RenderValidation("Password"); } + +
+ } - -
+ @if (Model.HasExternalLogin && Model.HasPasswordAuth) + { +
+
@T.Get("setup.createUser.separator")
+
+ } -
- + @if (Model.HasPasswordAuth) + { +

@T.Get("setup.createUser.headlineCreate")

- @{ RenderValidation("PasswordConfirm"); } + @if (!string.IsNullOrWhiteSpace(Model.ErrorMessage)) + { +
+ @Model.ErrorMessage +
+ } - -
+ +
+ -
- -
- - } + @{ RenderValidation("Email"); } - @if (!Model.HasExternalLogin && !Model.HasPasswordAuth) - { -
- @T.Get("setup.createUser.failure") -
- } -
-
-
+ +
-
- - @T.Get("setup.madeBy")
@T.Get("setup.madeByCopyright") -
-
+
+ + + @{ RenderValidation("Password"); } + + +
+ +
+ + + @{ RenderValidation("PasswordConfirm"); } + + +
+ +
+ +
+ + } + + @if (!Model.HasExternalLogin && !Model.HasPasswordAuth) + { +
+ @T.Get("setup.createUser.failure") +
+ } +
\ No newline at end of file diff --git a/backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml b/backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml index 437769bf1..1a81215d9 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml +++ b/backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml @@ -17,11 +17,21 @@ @await RenderSectionAsync("header") } - -
+ +
- @RenderBody() +
+
+ @RenderBody() +
+
+ +