mirror of https://github.com/Squidex/squidex.git
26 changed files with 759 additions and 47 deletions
@ -0,0 +1,55 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public abstract class HandlerBase |
||||
|
{ |
||||
|
private readonly IStore<Guid> store; |
||||
|
|
||||
|
protected HandlerBase(IStore<Guid> store) |
||||
|
{ |
||||
|
Guard.NotNull(store, nameof(store)); |
||||
|
|
||||
|
this.store = store; |
||||
|
} |
||||
|
|
||||
|
protected async Task RebuildManyAsync(IEnumerable<Guid> ids, Func<Guid, Task> action) |
||||
|
{ |
||||
|
foreach (var id in ids) |
||||
|
{ |
||||
|
await action(id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected async Task RebuildAsync<TState, TGrain>(Guid key, Func<Envelope<IEvent>, TState, TState> func) where TState : IDomainState, new() |
||||
|
{ |
||||
|
var state = new TState |
||||
|
{ |
||||
|
Version = EtagVersion.Empty |
||||
|
}; |
||||
|
|
||||
|
var persistence = store.WithSnapshotsAndEventSourcing<TState, Guid>(typeof(TGrain), key, s => state = s, e => |
||||
|
{ |
||||
|
state = func(e, state); |
||||
|
|
||||
|
state.Version++; |
||||
|
}); |
||||
|
|
||||
|
await persistence.ReadAsync(); |
||||
|
await persistence.WriteSnapshotAsync(state); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Events.Apps; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public sealed class RestoreApp : HandlerBase, IRestoreHandler |
||||
|
{ |
||||
|
private NamedId<Guid> appId; |
||||
|
|
||||
|
public string Name { get; } = "App"; |
||||
|
|
||||
|
public RestoreApp(IStore<Guid> store) |
||||
|
: base(store) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Task HandleAsync(Envelope<IEvent> @event, Stream attachment) |
||||
|
{ |
||||
|
if (@event.Payload is AppCreated appCreated) |
||||
|
{ |
||||
|
appId = appCreated.AppId; |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task ProcessAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task CompleteAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Entities.Assets; |
||||
|
using Squidex.Domain.Apps.Entities.Assets.State; |
||||
|
using Squidex.Domain.Apps.Events.Assets; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Assets; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public sealed class RestoreAssets : HandlerBase, IRestoreHandler |
||||
|
{ |
||||
|
private readonly HashSet<Guid> assetIds = new HashSet<Guid>(); |
||||
|
private readonly IAssetStore assetStore; |
||||
|
|
||||
|
public string Name { get; } = "Assets"; |
||||
|
|
||||
|
public RestoreAssets(IStore<Guid> store, IAssetStore assetStore) |
||||
|
: base(store) |
||||
|
{ |
||||
|
Guard.NotNull(assetStore, nameof(assetStore)); |
||||
|
|
||||
|
this.assetStore = assetStore; |
||||
|
} |
||||
|
|
||||
|
public async Task HandleAsync(Envelope<IEvent> @event, Stream attachment) |
||||
|
{ |
||||
|
var assetVersion = 0L; |
||||
|
var assetId = Guid.Empty; |
||||
|
|
||||
|
switch (@event.Payload) |
||||
|
{ |
||||
|
case AssetCreated assetCreated: |
||||
|
assetId = assetCreated.AssetId; |
||||
|
assetVersion = assetCreated.FileVersion; |
||||
|
assetIds.Add(assetCreated.AssetId); |
||||
|
break; |
||||
|
case AssetUpdated asetUpdated: |
||||
|
assetId = asetUpdated.AssetId; |
||||
|
assetVersion = asetUpdated.FileVersion; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (attachment != null) |
||||
|
{ |
||||
|
await assetStore.UploadAsync(assetId.ToString(), assetVersion, null, attachment); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Task ProcessAsync() |
||||
|
{ |
||||
|
return RebuildManyAsync(assetIds, id => RebuildAsync<AssetState, AssetGrain>(id, (e, s) => s.Apply(e))); |
||||
|
} |
||||
|
|
||||
|
public Task CompleteAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Entities.Contents; |
||||
|
using Squidex.Domain.Apps.Entities.Contents.State; |
||||
|
using Squidex.Domain.Apps.Events.Contents; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public sealed class RestoreContents : HandlerBase, IRestoreHandler |
||||
|
{ |
||||
|
private readonly HashSet<Guid> contentIds = new HashSet<Guid>(); |
||||
|
|
||||
|
public string Name { get; } = "Contents"; |
||||
|
|
||||
|
public RestoreContents(IStore<Guid> store) |
||||
|
: base(store) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Task HandleAsync(Envelope<IEvent> @event, Stream attachment) |
||||
|
{ |
||||
|
switch (@event.Payload) |
||||
|
{ |
||||
|
case ContentCreated contentCreated: |
||||
|
contentIds.Add(contentCreated.ContentId); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task ProcessAsync() |
||||
|
{ |
||||
|
return RebuildManyAsync(contentIds, id => RebuildAsync<ContentState, ContentGrain>(id, (e, s) => s.Apply(e))); |
||||
|
} |
||||
|
|
||||
|
public Task CompleteAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Entities.Rules; |
||||
|
using Squidex.Domain.Apps.Entities.Rules.State; |
||||
|
using Squidex.Domain.Apps.Events.Apps; |
||||
|
using Squidex.Domain.Apps.Events.Rules; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public sealed class RestoreRules : HandlerBase, IRestoreHandler |
||||
|
{ |
||||
|
private readonly HashSet<Guid> ruleIds = new HashSet<Guid>(); |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
private Guid appId; |
||||
|
|
||||
|
public string Name { get; } = "Rules"; |
||||
|
|
||||
|
public RestoreRules(IStore<Guid> store, IGrainFactory grainFactory) |
||||
|
: base(store) |
||||
|
{ |
||||
|
Guard.NotNull(grainFactory, nameof(grainFactory)); |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public Task HandleAsync(Envelope<IEvent> @event, Stream attachment) |
||||
|
{ |
||||
|
switch (@event.Payload) |
||||
|
{ |
||||
|
case AppCreated appCreated: |
||||
|
appId = appCreated.AppId.Id; |
||||
|
break; |
||||
|
case RuleCreated ruleCreated: |
||||
|
ruleIds.Add(ruleCreated.RuleId); |
||||
|
break; |
||||
|
case RuleDeleted ruleDeleted: |
||||
|
ruleIds.Remove(ruleDeleted.RuleId); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public async Task ProcessAsync() |
||||
|
{ |
||||
|
await RebuildManyAsync(ruleIds, id => RebuildAsync<RuleState, RuleGrain>(id, (e, s) => s.Apply(e))); |
||||
|
|
||||
|
await grainFactory.GetGrain<IRulesByAppIndex>(appId).RebuildAsync(ruleIds); |
||||
|
} |
||||
|
|
||||
|
public Task CompleteAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas.State; |
||||
|
using Squidex.Domain.Apps.Events.Apps; |
||||
|
using Squidex.Domain.Apps.Events.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.Handlers |
||||
|
{ |
||||
|
public sealed class RestoreSchemas : HandlerBase, IRestoreHandler |
||||
|
{ |
||||
|
private readonly HashSet<NamedId<Guid>> schemaIds = new HashSet<NamedId<Guid>>(); |
||||
|
private readonly Dictionary<string, Guid> schemasByName = new Dictionary<string, Guid>(); |
||||
|
private readonly FieldRegistry fieldRegistry; |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
private Guid appId; |
||||
|
|
||||
|
public string Name { get; } = "Schemas"; |
||||
|
|
||||
|
public RestoreSchemas(IStore<Guid> store, FieldRegistry fieldRegistry, IGrainFactory grainFactory) |
||||
|
: base(store) |
||||
|
{ |
||||
|
Guard.NotNull(fieldRegistry, nameof(fieldRegistry)); |
||||
|
Guard.NotNull(grainFactory, nameof(grainFactory)); |
||||
|
|
||||
|
this.fieldRegistry = fieldRegistry; |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public Task HandleAsync(Envelope<IEvent> @event, Stream attachment) |
||||
|
{ |
||||
|
switch (@event.Payload) |
||||
|
{ |
||||
|
case AppCreated appCreated: |
||||
|
appId = appCreated.AppId.Id; |
||||
|
break; |
||||
|
case SchemaCreated schemaCreated: |
||||
|
schemaIds.Add(schemaCreated.SchemaId); |
||||
|
schemasByName[schemaCreated.SchemaId.Name] = schemaCreated.SchemaId.Id; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public async Task ProcessAsync() |
||||
|
{ |
||||
|
await RebuildManyAsync(schemaIds.Select(x => x.Id), id => RebuildAsync<SchemaState, SchemaGrain>(id, (e, s) => s.Apply(e, fieldRegistry))); |
||||
|
|
||||
|
await grainFactory.GetGrain<ISchemasByAppIndex>(appId).RebuildAsync(schemasByName); |
||||
|
} |
||||
|
|
||||
|
public Task CompleteAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup |
||||
|
{ |
||||
|
public interface IRestoreGrain |
||||
|
{ |
||||
|
Task RestoreAsync(Uri url, RefToken user); |
||||
|
|
||||
|
Task<J<IRestoreJob>> GetStateAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup |
||||
|
{ |
||||
|
public interface IRestoreHandler |
||||
|
{ |
||||
|
string Name { get; } |
||||
|
|
||||
|
Task HandleAsync(Envelope<IEvent> @event, Stream attachment); |
||||
|
|
||||
|
Task ProcessAsync(); |
||||
|
|
||||
|
Task CompleteAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using NodaTime; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup |
||||
|
{ |
||||
|
public interface IRestoreJob |
||||
|
{ |
||||
|
Uri Uri { get; } |
||||
|
|
||||
|
Instant Started { get; } |
||||
|
|
||||
|
bool IsFailed { get; } |
||||
|
|
||||
|
string Status { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.State |
||||
|
{ |
||||
|
public class RestoreState |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public RestoreStateJob Job { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Newtonsoft.Json; |
||||
|
using NodaTime; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup.State |
||||
|
{ |
||||
|
public sealed class RestoreStateJob : IRestoreJob |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public Guid Id { get; set; } = Guid.NewGuid(); |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Guid AppId { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public RefToken User { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Uri Uri { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Instant Started { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string Status { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public bool IsFailed { get; set; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue