Browse Source

Restore fixes.

pull/361/head
Sebastian 8 years ago
parent
commit
ec02459cc4
  1. 2
      extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs
  2. 2
      extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs
  3. 4
      src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs
  4. 54
      src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs
  5. 2
      src/Squidex.Domain.Apps.Entities/Backup/BackupHandler.cs
  6. 4
      src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs
  7. 14
      src/Squidex.Domain.Apps.Entities/Backup/Helpers/Safe.cs
  8. 5
      src/Squidex.Domain.Apps.Entities/Backup/IBackupArchiveLocation.cs
  9. 46
      src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs
  10. 7
      src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs
  11. 2
      src/Squidex/app/features/settings/pages/backups/backups-page.component.html
  12. 6
      src/Squidex/appsettings.json
  13. 10
      src/Squidex/package-lock.json
  14. 2
      tools/Migrate_00/Program.cs
  15. 2
      tools/Migrate_01/RebuildOptions.cs
  16. 11
      tools/Migrate_01/RebuildRunner.cs

2
extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs

@ -15,7 +15,7 @@ namespace Squidex.Extensions.Actions.Algolia
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M16 .842C7.633.842.842 7.625.842 16S7.625 31.158 16 31.158c8.374 0 15.158-6.791 15.158-15.166S24.375.842 16 .842zm0 25.83c-5.898 0-10.68-4.781-10.68-10.68S10.101 5.313 16 5.313s10.68 4.781 10.68 10.679-4.781 10.68-10.68 10.68zm0-19.156v7.956c0 .233.249.388.458.279l7.055-3.663a.312.312 0 0 0 .124-.434 8.807 8.807 0 0 0-7.319-4.447z'/></svg>", IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M16 .842C7.633.842.842 7.625.842 16S7.625 31.158 16 31.158c8.374 0 15.158-6.791 15.158-15.166S24.375.842 16 .842zm0 25.83c-5.898 0-10.68-4.781-10.68-10.68S10.101 5.313 16 5.313s10.68 4.781 10.68 10.679-4.781 10.68-10.68 10.68zm0-19.156v7.956c0 .233.249.388.458.279l7.055-3.663a.312.312 0 0 0 .124-.434 8.807 8.807 0 0 0-7.319-4.447z'/></svg>",
IconColor = "#0d9bf9", IconColor = "#0d9bf9",
Display = "Populate Algolia index", Display = "Populate Algolia index",
Description = "Populate and synchronize indices in Algolia for full text search.", Description = "Populate and synchronize indexes in Algolia for full text search.",
ReadMore = "https://www.algolia.com/")] ReadMore = "https://www.algolia.com/")]
public sealed class AlgoliaAction : RuleAction public sealed class AlgoliaAction : RuleAction
{ {

2
extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs

@ -17,7 +17,7 @@ namespace Squidex.Extensions.Actions.ElasticSearch
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 28'><path d='M13.427 17.436H4.163C3.827 16.354 3.636 15.2 3.636 14s.182-2.355.527-3.436h15.245c1.891 0 3.418 1.545 3.418 3.445a3.421 3.421 0 0 1-3.418 3.427h-5.982zm-.436 1.146H4.6a11.508 11.508 0 0 0 4.2 4.982 11.443 11.443 0 0 0 15.827-3.209 5.793 5.793 0 0 0-4.173-1.773H12.99zm7.464-9.164a5.794 5.794 0 0 0 4.173-1.773 11.45 11.45 0 0 0-9.536-5.1c-2.327 0-4.491.7-6.3 1.891a11.554 11.554 0 0 0-4.2 4.982h15.864z'/></svg>", IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 28'><path d='M13.427 17.436H4.163C3.827 16.354 3.636 15.2 3.636 14s.182-2.355.527-3.436h15.245c1.891 0 3.418 1.545 3.418 3.445a3.421 3.421 0 0 1-3.418 3.427h-5.982zm-.436 1.146H4.6a11.508 11.508 0 0 0 4.2 4.982 11.443 11.443 0 0 0 15.827-3.209 5.793 5.793 0 0 0-4.173-1.773H12.99zm7.464-9.164a5.794 5.794 0 0 0 4.173-1.773 11.45 11.45 0 0 0-9.536-5.1c-2.327 0-4.491.7-6.3 1.891a11.554 11.554 0 0 0-4.2 4.982h15.864z'/></svg>",
IconColor = "#1e5470", IconColor = "#1e5470",
Display = "Populate ElasticSearch index", Display = "Populate ElasticSearch index",
Description = "Populate and synchronize indices in ElasticSearch for full text search.", Description = "Populate and synchronize indexes in ElasticSearch for full text search.",
ReadMore = "https://www.elastic.co/")] ReadMore = "https://www.elastic.co/")]
public sealed class ElasticSearchAction : RuleAction public sealed class ElasticSearchAction : RuleAction
{ {

4
src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs

@ -127,11 +127,11 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
if (!(isReserved = await appsByNameIndex.ReserveAppAsync(appId, appName))) if (!(isReserved = await appsByNameIndex.ReserveAppAsync(appId, appName)))
{ {
throw new BackupRestoreException("vThe app id or name is not available."); throw new BackupRestoreException("The app id or name is not available.");
} }
} }
public override async Task CleanupRestoreAsync(Guid appId) public override async Task CleanupRestoreErrorAsync(Guid appId)
{ {
if (isReserved) if (isReserved)
{ {

54
src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using NodaTime; using NodaTime;
using Orleans.Concurrency; using Orleans.Concurrency;
using Squidex.Domain.Apps.Entities.Backup.Helpers; using Squidex.Domain.Apps.Entities.Backup.Helpers;
@ -34,8 +35,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IAssetStore assetStore; private readonly IAssetStore assetStore;
private readonly IBackupArchiveLocation backupArchiveLocation; private readonly IBackupArchiveLocation backupArchiveLocation;
private readonly IClock clock; private readonly IClock clock;
private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer; private readonly IJsonSerializer serializer;
private readonly IServiceProvider serviceProvider;
private readonly IEventDataFormatter eventDataFormatter; private readonly IEventDataFormatter eventDataFormatter;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
private readonly ISemanticLog log; private readonly ISemanticLog log;
@ -48,8 +49,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
IClock clock, IClock clock,
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer, IJsonSerializer serializer,
IServiceProvider serviceProvider,
ISemanticLog log, ISemanticLog log,
IStore<Guid> store) IStore<Guid> store)
: base(store) : base(store)
@ -59,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(clock, nameof(clock)); Guard.NotNull(clock, nameof(clock));
Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers)); Guard.NotNull(serviceProvider, nameof(serviceProvider));
Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(log, nameof(log)); Guard.NotNull(log, nameof(log));
@ -68,8 +69,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.clock = clock; this.clock = clock;
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers;
this.serializer = serializer; this.serializer = serializer;
this.serviceProvider = serviceProvider;
this.log = log; this.log = log;
} }
@ -86,10 +87,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
if (!job.Stopped.HasValue) if (!job.Stopped.HasValue)
{ {
var jobId = job.Id.ToString();
job.Stopped = clock.GetCurrentInstant(); job.Stopped = clock.GetCurrentInstant();
await Safe.DeleteAsync(backupArchiveLocation, job.Id, log); await Safe.DeleteAsync(backupArchiveLocation, jobId, log);
await Safe.DeleteAsync(assetStore, job.Id, log); await Safe.DeleteAsync(assetStore, jobId, log);
job.Status = JobStatus.Failed; job.Status = JobStatus.Failed;
@ -120,15 +123,29 @@ namespace Squidex.Domain.Apps.Entities.Backup
currentTask = new CancellationTokenSource(); currentTask = new CancellationTokenSource();
currentJob = job; currentJob = job;
var lastTimestamp = job.Started;
State.Jobs.Insert(0, job); State.Jobs.Insert(0, job);
await WriteStateAsync(); await WriteStateAsync();
Process(job, currentTask.Token);
}
private void Process(BackupStateJob job, CancellationToken ct)
{
ProcessAsync(job, ct).Forget();
}
private async Task ProcessAsync(BackupStateJob job, CancellationToken ct)
{
var jobId = job.Id.ToString();
var handlers = CreateHandlers();
var lastTimestamp = job.Started;
try try
{ {
using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) using (var stream = await backupArchiveLocation.OpenStreamAsync(jobId))
{ {
using (var writer = new BackupWriter(serializer, stream, true)) using (var writer = new BackupWriter(serializer, stream, true))
{ {
@ -162,16 +179,16 @@ namespace Squidex.Domain.Apps.Entities.Backup
stream.Position = 0; stream.Position = 0;
currentTask.Token.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
await assetStore.UploadAsync(job.Id.ToString(), 0, null, stream, false, currentTask.Token); await assetStore.UploadAsync(jobId, 0, null, stream, false, currentTask.Token);
} }
job.Status = JobStatus.Completed; job.Status = JobStatus.Completed;
} }
catch (Exception ex) catch (Exception ex)
{ {
log.LogError(ex, job.Id.ToString(), (ctx, w) => w log.LogError(ex, jobId, (ctx, w) => w
.WriteProperty("action", "makeBackup") .WriteProperty("action", "makeBackup")
.WriteProperty("status", "failed") .WriteProperty("status", "failed")
.WriteProperty("backupId", ctx)); .WriteProperty("backupId", ctx));
@ -180,7 +197,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
} }
finally finally
{ {
await Safe.DeleteAsync(backupArchiveLocation, job.Id, log); await Safe.DeleteAsync(backupArchiveLocation, jobId, log);
job.Stopped = clock.GetCurrentInstant(); job.Stopped = clock.GetCurrentInstant();
@ -220,8 +237,10 @@ namespace Squidex.Domain.Apps.Entities.Backup
} }
else else
{ {
await Safe.DeleteAsync(backupArchiveLocation, job.Id, log); var jobId = job.Id.ToString();
await Safe.DeleteAsync(assetStore, job.Id, log);
await Safe.DeleteAsync(backupArchiveLocation, jobId, log);
await Safe.DeleteAsync(assetStore, jobId, log);
State.Jobs.Remove(job); State.Jobs.Remove(job);
@ -229,6 +248,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
} }
} }
private IEnumerable<BackupHandler> CreateHandlers()
{
return serviceProvider.GetRequiredService<IEnumerable<BackupHandler>>();
}
public Task<J<List<IBackupJob>>> GetStateAsync() public Task<J<List<IBackupJob>>> GetStateAsync()
{ {
return J.AsTask(State.Jobs.OfType<IBackupJob>().ToList()); return J.AsTask(State.Jobs.OfType<IBackupJob>().ToList());

2
src/Squidex.Domain.Apps.Entities/Backup/BackupHandler.cs

@ -37,7 +37,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
return TaskHelper.Done; return TaskHelper.Done;
} }
public virtual Task CleanupRestoreAsync(Guid appId) public virtual Task CleanupRestoreErrorAsync(Guid appId)
{ {
return TaskHelper.Done; return TaskHelper.Done;
} }

4
src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs

@ -15,7 +15,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
{ {
public static class Downloader public static class Downloader
{ {
public static async Task DownloadAsync(this IBackupArchiveLocation backupArchiveLocation, Uri url, Guid id) public static async Task DownloadAsync(this IBackupArchiveLocation backupArchiveLocation, Uri url, string id)
{ {
if (string.Equals(url.Scheme, "file")) if (string.Equals(url.Scheme, "file"))
{ {
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
} }
} }
public static async Task<BackupReader> OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, Guid id, IJsonSerializer serializer) public static async Task<BackupReader> OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, string id, IJsonSerializer serializer)
{ {
Stream stream = null; Stream stream = null;

14
src/Squidex.Domain.Apps.Entities/Backup/Helpers/Safe.cs

@ -14,7 +14,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
{ {
public static class Safe public static class Safe
{ {
public static async Task DeleteAsync(IBackupArchiveLocation backupArchiveLocation, Guid id, ISemanticLog log) public static async Task DeleteAsync(IBackupArchiveLocation backupArchiveLocation, string id, ISemanticLog log)
{ {
try try
{ {
@ -22,33 +22,33 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
log.LogError(ex, id.ToString(), (logOperationId, w) => w log.LogError(ex, id, (logOperationId, w) => w
.WriteProperty("action", "deleteArchive") .WriteProperty("action", "deleteArchive")
.WriteProperty("status", "failed") .WriteProperty("status", "failed")
.WriteProperty("operationId", logOperationId)); .WriteProperty("operationId", logOperationId));
} }
} }
public static async Task DeleteAsync(IAssetStore assetStore, Guid id, ISemanticLog log) public static async Task DeleteAsync(IAssetStore assetStore, string id, ISemanticLog log)
{ {
try try
{ {
await assetStore.DeleteAsync(id.ToString(), 0, null); await assetStore.DeleteAsync(id, 0, null);
} }
catch (Exception ex) catch (Exception ex)
{ {
log.LogError(ex, id.ToString(), (logOperationId, w) => w log.LogError(ex, id, (logOperationId, w) => w
.WriteProperty("action", "deleteBackup") .WriteProperty("action", "deleteBackup")
.WriteProperty("status", "failed") .WriteProperty("status", "failed")
.WriteProperty("operationId", logOperationId)); .WriteProperty("operationId", logOperationId));
} }
} }
public static async Task CleanupRestoreAsync(BackupHandler handler, Guid appId, Guid id, ISemanticLog log) public static async Task CleanupRestoreErrorAsync(BackupHandler handler, Guid appId, Guid id, ISemanticLog log)
{ {
try try
{ {
await handler.CleanupRestoreAsync(appId); await handler.CleanupRestoreErrorAsync(appId);
} }
catch (Exception ex) catch (Exception ex)
{ {

5
src/Squidex.Domain.Apps.Entities/Backup/IBackupArchiveLocation.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -13,8 +12,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
public interface IBackupArchiveLocation public interface IBackupArchiveLocation
{ {
Task<Stream> OpenStreamAsync(Guid backupId); Task<Stream> OpenStreamAsync(string backupId);
Task DeleteArchiveAsync(Guid backupId); Task DeleteArchiveAsync(string backupId);
} }
} }

46
src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -31,11 +32,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IBackupArchiveLocation backupArchiveLocation; private readonly IBackupArchiveLocation backupArchiveLocation;
private readonly IClock clock; private readonly IClock clock;
private readonly ICommandBus commandBus; private readonly ICommandBus commandBus;
private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer; private readonly IJsonSerializer serializer;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter; private readonly IEventDataFormatter eventDataFormatter;
private readonly ISemanticLog log; private readonly ISemanticLog log;
private readonly IServiceProvider serviceProvider;
private readonly IStreamNameResolver streamNameResolver; private readonly IStreamNameResolver streamNameResolver;
private RestoreStateJob CurrentJob private RestoreStateJob CurrentJob
@ -48,9 +49,9 @@ namespace Squidex.Domain.Apps.Entities.Backup
ICommandBus commandBus, ICommandBus commandBus,
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer, IJsonSerializer serializer,
ISemanticLog log, ISemanticLog log,
IServiceProvider serviceProvider,
IStreamNameResolver streamNameResolver, IStreamNameResolver streamNameResolver,
IStore<string> store) IStore<string> store)
: base(store) : base(store)
@ -60,8 +61,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(commandBus, nameof(commandBus)); Guard.NotNull(commandBus, nameof(commandBus));
Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(serviceProvider, nameof(serviceProvider));
Guard.NotNull(store, nameof(store)); Guard.NotNull(store, nameof(store));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver)); Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
Guard.NotNull(log, nameof(log)); Guard.NotNull(log, nameof(log));
@ -71,8 +72,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.commandBus = commandBus; this.commandBus = commandBus;
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers;
this.serializer = serializer; this.serializer = serializer;
this.serviceProvider = serviceProvider;
this.streamNameResolver = streamNameResolver; this.streamNameResolver = streamNameResolver;
this.log = log; this.log = log;
} }
@ -88,16 +89,18 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
if (CurrentJob?.Status == JobStatus.Started) if (CurrentJob?.Status == JobStatus.Started)
{ {
var handlers = CreateHandlers();
Log("Failed due application restart"); Log("Failed due application restart");
CurrentJob.Status = JobStatus.Failed; CurrentJob.Status = JobStatus.Failed;
await CleanupAsync(); await CleanupAsync(handlers);
await WriteStateAsync(); await WriteStateAsync();
} }
} }
public Task RestoreAsync(Uri url, RefToken actor, string newAppName) public async Task RestoreAsync(Uri url, RefToken actor, string newAppName)
{ {
Guard.NotNull(url, nameof(url)); Guard.NotNull(url, nameof(url));
Guard.NotNull(actor, nameof(actor)); Guard.NotNull(actor, nameof(actor));
@ -122,9 +125,9 @@ namespace Squidex.Domain.Apps.Entities.Backup
Url = url Url = url
}; };
Process(); await WriteStateAsync();
return TaskHelper.Done; Process();
} }
private void Process() private void Process()
@ -134,6 +137,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
private async Task ProcessAsync() private async Task ProcessAsync()
{ {
var handlers = CreateHandlers();
var logContext = (jobId: CurrentJob.Id.ToString(), jobUrl: CurrentJob.Url.ToString()); var logContext = (jobId: CurrentJob.Id.ToString(), jobUrl: CurrentJob.Url.ToString());
using (Profiler.StartSession()) using (Profiler.StartSession())
@ -157,11 +162,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
await DownloadAsync(); await DownloadAsync();
} }
using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id, serializer)) using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id.ToString(), serializer))
{ {
using (Profiler.Trace("ReadEvents")) using (Profiler.Trace("ReadEvents"))
{ {
await ReadEventsAsync(reader); await ReadEventsAsync(reader, handlers);
} }
foreach (var handler in handlers) foreach (var handler in handlers)
@ -212,7 +217,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Log("Failed with internal error"); Log("Failed with internal error");
} }
await CleanupAsync(); await CleanupAsync(handlers);
CurrentJob.Status = JobStatus.Failed; CurrentJob.Status = JobStatus.Failed;
@ -258,15 +263,15 @@ namespace Squidex.Domain.Apps.Entities.Backup
} }
} }
private async Task CleanupAsync() private async Task CleanupAsync(IEnumerable<BackupHandler> handlers)
{ {
await Safe.DeleteAsync(backupArchiveLocation, CurrentJob.Id, log); await Safe.DeleteAsync(backupArchiveLocation, CurrentJob.Id.ToString(), log);
if (CurrentJob.AppId != Guid.Empty) if (CurrentJob.AppId != Guid.Empty)
{ {
foreach (var handler in handlers) foreach (var handler in handlers)
{ {
await Safe.CleanupRestoreAsync(handler, CurrentJob.AppId, CurrentJob.Id, log); await Safe.CleanupRestoreErrorAsync(handler, CurrentJob.AppId, CurrentJob.Id, log);
} }
} }
} }
@ -275,22 +280,22 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
Log("Downloading Backup"); Log("Downloading Backup");
await backupArchiveLocation.DownloadAsync(CurrentJob.Url, CurrentJob.Id); await backupArchiveLocation.DownloadAsync(CurrentJob.Url, CurrentJob.Id.ToString());
Log("Downloaded Backup"); Log("Downloaded Backup");
} }
private async Task ReadEventsAsync(BackupReader reader) private async Task ReadEventsAsync(BackupReader reader, IEnumerable<BackupHandler> handlers)
{ {
await reader.ReadEventsAsync(streamNameResolver, eventDataFormatter, async storedEvent => await reader.ReadEventsAsync(streamNameResolver, eventDataFormatter, async storedEvent =>
{ {
await HandleEventAsync(reader, storedEvent.Stream, storedEvent.Event); await HandleEventAsync(reader, handlers, storedEvent.Stream, storedEvent.Event);
}); });
Log($"Reading {reader.ReadEvents} events and {reader.ReadAttachments} attachments completed.", true); Log($"Reading {reader.ReadEvents} events and {reader.ReadAttachments} attachments completed.", true);
} }
private async Task HandleEventAsync(BackupReader reader, string stream, Envelope<IEvent> @event) private async Task HandleEventAsync(BackupReader reader, IEnumerable<BackupHandler> handlers, string stream, Envelope<IEvent> @event)
{ {
if (@event.Payload is SquidexEvent squidexEvent) if (@event.Payload is SquidexEvent squidexEvent)
{ {
@ -340,6 +345,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
} }
} }
private IEnumerable<BackupHandler> CreateHandlers()
{
return serviceProvider.GetRequiredService<IEnumerable<BackupHandler>>();
}
public Task<J<IRestoreJob>> GetJobAsync() public Task<J<IRestoreJob>> GetJobAsync()
{ {
return Task.FromResult<J<IRestoreJob>>(CurrentJob); return Task.FromResult<J<IRestoreJob>>(CurrentJob);

7
src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
@ -14,14 +13,14 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
public sealed class TempFolderBackupArchiveLocation : IBackupArchiveLocation public sealed class TempFolderBackupArchiveLocation : IBackupArchiveLocation
{ {
public Task<Stream> OpenStreamAsync(Guid backupId) public Task<Stream> OpenStreamAsync(string backupId)
{ {
var tempFile = GetTempFile(backupId); var tempFile = GetTempFile(backupId);
return Task.FromResult<Stream>(new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.ReadWrite)); return Task.FromResult<Stream>(new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.ReadWrite));
} }
public Task DeleteArchiveAsync(Guid backupId) public Task DeleteArchiveAsync(string backupId)
{ {
var tempFile = GetTempFile(backupId); var tempFile = GetTempFile(backupId);
@ -36,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
return TaskHelper.Done; return TaskHelper.Done;
} }
private static string GetTempFile(Guid backupId) private static string GetTempFile(string backupId)
{ {
return Path.Combine(Path.GetTempPath(), backupId + ".zip"); return Path.Combine(Path.GetTempPath(), backupId + ".zip");
} }

2
src/Squidex/app/features/settings/pages/backups/backups-page.component.html

@ -40,7 +40,7 @@
<div *ngSwitchCase="'Completed'" class="backup-status backup-status-success"> <div *ngSwitchCase="'Completed'" class="backup-status backup-status-success">
<i class="icon-checkmark"></i> <i class="icon-checkmark"></i>
</div> </div>
<div *NgSwitchDefault class="backup-status backup-status-pending spin"> <div *ngSwitchDefault class="backup-status backup-status-pending spin">
<i class="icon-hour-glass"></i> <i class="icon-hour-glass"></i>
</div> </div>
</div> </div>

6
src/Squidex/appsettings.json

@ -430,6 +430,10 @@
/* /*
* Set to true to rebuild schemas. * Set to true to rebuild schemas.
*/ */
"schemas": false "schemas": false,
/*
* Set to true to rebuild indexes.
*/
"indexes": false
} }
} }

10
src/Squidex/package-lock.json

@ -2037,7 +2037,7 @@
"dependencies": { "dependencies": {
"jsesc": { "jsesc": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true "dev": true
}, },
@ -9249,7 +9249,7 @@
}, },
"node-fetch": { "node-fetch": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
}, },
"node-forge": { "node-forge": {
@ -9639,7 +9639,7 @@
}, },
"onetime": { "onetime": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true "dev": true
}, },
@ -13298,7 +13298,7 @@
"dependencies": { "dependencies": {
"json5": { "json5": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true, "dev": true,
"requires": { "requires": {
@ -15408,7 +15408,7 @@
}, },
"whatwg-fetch": { "whatwg-fetch": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
}, },
"which": { "which": {

2
tools/Migrate_00/Program.cs

@ -22,7 +22,7 @@ namespace Migrate_00
var collection = mongoDatabase.GetCollection<BsonDocument>("Events"); var collection = mongoDatabase.GetCollection<BsonDocument>("Events");
Console.Write("Migrate Indices....."); Console.Write("Migrate indexes.....");
collection.Indexes.DropAll(); collection.Indexes.DropAll();

2
tools/Migrate_01/RebuildOptions.cs

@ -15,6 +15,8 @@ namespace Migrate_01
public bool Contents { get; set; } public bool Contents { get; set; }
public bool Indexes { get; set; }
public bool Rules { get; set; } public bool Rules { get; set; }
public bool Schemas { get; set; } public bool Schemas { get; set; }

11
tools/Migrate_01/RebuildRunner.cs

@ -8,6 +8,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Migrate_01.Migrations;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Migrate_01 namespace Migrate_01
@ -15,15 +16,18 @@ namespace Migrate_01
public sealed class RebuildRunner public sealed class RebuildRunner
{ {
private readonly Rebuilder rebuilder; private readonly Rebuilder rebuilder;
private readonly PopulateGrainIndexes populateGrainIndexes;
private readonly RebuildOptions rebuildOptions; private readonly RebuildOptions rebuildOptions;
public RebuildRunner(Rebuilder rebuilder, IOptions<RebuildOptions> rebuildOptions) public RebuildRunner(Rebuilder rebuilder, IOptions<RebuildOptions> rebuildOptions, PopulateGrainIndexes populateGrainIndexes)
{ {
Guard.NotNull(rebuilder, nameof(rebuilder)); Guard.NotNull(rebuilder, nameof(rebuilder));
Guard.NotNull(rebuildOptions, nameof(rebuildOptions)); Guard.NotNull(rebuildOptions, nameof(rebuildOptions));
Guard.NotNull(populateGrainIndexes, nameof(populateGrainIndexes));
this.rebuilder = rebuilder; this.rebuilder = rebuilder;
this.rebuildOptions = rebuildOptions.Value; this.rebuildOptions = rebuildOptions.Value;
this.populateGrainIndexes = populateGrainIndexes;
} }
public async Task RunAsync(CancellationToken ct) public async Task RunAsync(CancellationToken ct)
@ -52,6 +56,11 @@ namespace Migrate_01
{ {
await rebuilder.RebuildContentAsync(ct); await rebuilder.RebuildContentAsync(ct);
} }
if (rebuildOptions.Indexes)
{
await populateGrainIndexes.UpdateAsync();
}
} }
} }
} }

Loading…
Cancel
Save