From 80dc7c12a664a87a9cc5e1f6917288798cfbdf97 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 12 Feb 2018 11:46:51 +0100 Subject: [PATCH] Continued with basic setup. --- .../EventSourcing/MongoEvent.cs | 5 +- .../EventSourcing/MongoEventStore_Reader.cs | 2 +- .../MongoDb/BsonJsonConvention.cs | 12 --- .../Commands/DomainObjectBase.cs | 2 +- .../Grains/EventConsumerBootstrap.cs | 12 ++- .../Grains/EventConsumerGrain.cs | 7 +- .../Grains/EventConsumerManagerGrain.cs | 4 +- .../Grains/OrleansEventNotifier.cs | 12 ++- src/Squidex.Infrastructure/States/IStore.cs | 6 +- .../States/Persistence.cs | 6 +- ...Key}.cs => Persistence{TSnapshot,TKey}.cs} | 16 ++-- .../States/StateFactory.cs | 4 +- src/Squidex.Infrastructure/States/Store.cs | 18 ++-- .../States/StoreExtensions.cs | 43 +++++++-- src/Squidex/AppConfiguration.cs | 26 ------ .../IdentityServer/Config/LazyClientStore.cs | 3 +- ...rleansDashboardAuthenticationMiddleware.cs | 43 +++++++++ src/Squidex/Areas/OrleansDashboard/Startup.cs | 27 ++++++ src/Squidex/Config/Constants.cs | 2 + .../Config/Domain/InfrastructureServices.cs | 2 + src/Squidex/Config/Domain/ReadServices.cs | 8 +- src/Squidex/Config/Orleans/ClientServices.cs | 41 --------- src/Squidex/Config/Orleans/ClientWrapper.cs | 50 +++++++++++ src/Squidex/Config/Orleans/OrleansServices.cs | 40 +++++++++ src/Squidex/Config/Orleans/SiloServices.cs | 32 +++++-- src/Squidex/Config/Orleans/SiloWrapper.cs | 90 +++++++++++++++++++ src/Squidex/Program.cs | 59 +++--------- src/Squidex/Squidex.csproj | 1 + src/Squidex/WebStartup.cs | 5 +- src/Squidex/appsettings.json | 10 ++- 30 files changed, 402 insertions(+), 186 deletions(-) rename src/Squidex.Infrastructure/States/{Persistence{TOwner,TSnapshot,TKey}.cs => Persistence{TSnapshot,TKey}.cs} (93%) delete mode 100644 src/Squidex/AppConfiguration.cs create mode 100644 src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs create mode 100644 src/Squidex/Areas/OrleansDashboard/Startup.cs delete mode 100644 src/Squidex/Config/Orleans/ClientServices.cs create mode 100644 src/Squidex/Config/Orleans/ClientWrapper.cs create mode 100644 src/Squidex/Config/Orleans/OrleansServices.cs create mode 100644 src/Squidex/Config/Orleans/SiloWrapper.cs diff --git a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs index 62d15ca20..22fb105dc 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs @@ -7,6 +7,7 @@ using MongoDB.Bson.Serialization.Attributes; using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.MongoDb; namespace Squidex.Infrastructure.EventSourcing { @@ -16,7 +17,7 @@ namespace Squidex.Infrastructure.EventSourcing [BsonRequired] public string Payload { get; set; } - [BsonElement] + [BsonJson] [BsonRequired] public JToken Metadata { get; set; } @@ -26,7 +27,7 @@ namespace Squidex.Infrastructure.EventSourcing public static MongoEvent FromEventData(EventData data) { - return new MongoEvent { Type = data.Type, Metadata = data.Metadata, Payload = data.ToString() }; + return new MongoEvent { Type = data.Type, Metadata = data.Metadata, Payload = data.Payload.ToString() }; } public EventData ToEventData() diff --git a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs index eed2d0bce..8160252f3 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs @@ -27,7 +27,7 @@ namespace Squidex.Infrastructure.EventSourcing Guard.NotNull(subscriber, nameof(subscriber)); Guard.NotNullOrEmpty(streamFilter, nameof(streamFilter)); - return new PollingSubscription(this, notifier, subscriber, streamFilter, position); + return new PollingSubscription(this, subscriber, streamFilter, position); } public async Task> QueryAsync(string streamName, long streamPosition = 0) diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs index 2a8d6e572..4326838e1 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs @@ -32,18 +32,6 @@ namespace Squidex.Infrastructure.MongoDb memberMap.SetSerializer((IBsonSerializer)bsonSerializer); } - else if (memberMap.MemberType == typeof(JToken)) - { - memberMap.SetSerializer(JTokenSerializer.Instance); - } - else if (memberMap.MemberType == typeof(JObject)) - { - memberMap.SetSerializer(JTokenSerializer.Instance); - } - else if (memberMap.MemberType == typeof(JValue)) - { - memberMap.SetSerializer(JTokenSerializer.Instance); - } }); ConventionRegistry.Register("json", pack, t => true); diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs b/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs index c4d0e0ef4..bfd50b19c 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs @@ -34,7 +34,7 @@ namespace Squidex.Infrastructure.Commands { id = key; - persistence = store.WithSnapshotsAndEventSourcing(key, ApplySnapshot, ApplyEvent); + persistence = store.WithSnapshotsAndEventSourcing(GetType(), key, ApplySnapshot, ApplyEvent); return persistence.ReadAsync(); } diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerBootstrap.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerBootstrap.cs index 40425f5e7..030da02e9 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerBootstrap.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerBootstrap.cs @@ -7,10 +7,11 @@ using Orleans; using Orleans.Runtime; +using Squidex.Infrastructure.Tasks; namespace Squidex.Infrastructure.EventSourcing.Grains { - public sealed class EventConsumerBootstrap : ILifecycleParticipant + public sealed class EventConsumerBootstrap : IRunnable { private readonly IGrainFactory grainFactory; @@ -21,14 +22,11 @@ namespace Squidex.Infrastructure.EventSourcing.Grains this.grainFactory = grainFactory; } - public void Participate(ISiloLifecycle lifecycle) + public void Run() { - lifecycle.Subscribe(SiloLifecycleStage.SiloActive, ct => - { - var grain = grainFactory.GetGrain("Default"); + var grain = grainFactory.GetGrain("Default"); - return grain.ActivateAsync(); - }); + grain.ActivateAsync().Forget(); } } } diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs index 343462025..9635cd146 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs @@ -21,11 +21,12 @@ namespace Squidex.Infrastructure.EventSourcing.Grains public class EventConsumerGrain : Grain, IEventConsumerGrain { private readonly EventConsumerFactory eventConsumerFactory; + private readonly IStore store; private readonly IEventDataFormatter eventDataFormatter; private readonly IEventStore eventStore; private readonly ISemanticLog log; - private readonly IPersistence persistence; private TaskScheduler scheduler; + private IPersistence persistence; private IEventSubscription currentSubscription; private IEventConsumer eventConsumer; private EventConsumerState state = new EventConsumerState(); @@ -61,7 +62,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains this.eventDataFormatter = eventDataFormatter; this.eventConsumerFactory = eventConsumerFactory; - persistence = store.WithSnapshots(this.GetPrimaryKeyString(), s => state = s); + this.store = store; } public override Task OnActivateAsync() @@ -70,6 +71,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains eventConsumer = eventConsumerFactory(this.GetPrimaryKeyString()); + persistence = store.WithSnapshots(this.GetPrimaryKeyString(), s => state = s); + return persistence.ReadAsync(); } diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs index a18f79078..77d3f40a9 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs @@ -46,8 +46,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { DelayDeactivation(TimeSpan.FromDays(1)); - RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(10)); - RegisterTimer(x => ActivateAsync(), null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); + // RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(10)); + // RegisterTimer(x => ActivateAsync(), null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); return Task.FromResult(true); } diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/OrleansEventNotifier.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/OrleansEventNotifier.cs index 4ce33f916..b68abde53 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/OrleansEventNotifier.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/OrleansEventNotifier.cs @@ -10,20 +10,26 @@ using Orleans; namespace Squidex.Infrastructure.EventSourcing.Grains { - public sealed class OrleansEventNotifier : IEventNotifier + public sealed class OrleansEventNotifier : IEventNotifier, IInitializable { - private readonly IEventConsumerManagerGrain eventConsumerManagerGrain; + private readonly IGrainFactory factory; + private IEventConsumerManagerGrain eventConsumerManagerGrain; public OrleansEventNotifier(IGrainFactory factory) { Guard.NotNull(factory, nameof(factory)); + this.factory = factory; + } + + public void Initialize() + { eventConsumerManagerGrain = factory.GetGrain("Default"); } public void NotifyEventsStored(string streamName) { - eventConsumerManagerGrain.WakeUpAsync(streamName); + eventConsumerManagerGrain?.WakeUpAsync(streamName); } public IDisposable Subscribe(Action handler) diff --git a/src/Squidex.Infrastructure/States/IStore.cs b/src/Squidex.Infrastructure/States/IStore.cs index 7ac2c6dea..1ba437fde 100644 --- a/src/Squidex.Infrastructure/States/IStore.cs +++ b/src/Squidex.Infrastructure/States/IStore.cs @@ -13,10 +13,10 @@ namespace Squidex.Infrastructure.States { public interface IStore { - IPersistence WithEventSourcing(TKey key, Func, Task> applyEvent); + IPersistence WithEventSourcing(Type owner, TKey key, Func, Task> applyEvent); - IPersistence WithSnapshots(TKey key, Func applySnapshot); + IPersistence WithSnapshots(Type owner, TKey key, Func applySnapshot); - IPersistence WithSnapshotsAndEventSourcing(TKey key, Func applySnapshot, Func, Task> applyEvent); + IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, Func applySnapshot, Func, Task> applyEvent); } } diff --git a/src/Squidex.Infrastructure/States/Persistence.cs b/src/Squidex.Infrastructure/States/Persistence.cs index 92dd38738..5c0d79037 100644 --- a/src/Squidex.Infrastructure/States/Persistence.cs +++ b/src/Squidex.Infrastructure/States/Persistence.cs @@ -11,15 +11,15 @@ using Squidex.Infrastructure.EventSourcing; namespace Squidex.Infrastructure.States { - internal sealed class Persistence : Persistence, IPersistence + internal sealed class Persistence : Persistence, IPersistence { - public Persistence(TKey ownerKey, + public Persistence(TKey ownerKey, Type ownerType, IEventStore eventStore, IEventDataFormatter eventDataFormatter, ISnapshotStore snapshotStore, IStreamNameResolver streamNameResolver, Func, Task> applyEvent) - : base(ownerKey, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) + : base(ownerKey, ownerType, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) { } } diff --git a/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs b/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs similarity index 93% rename from src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs rename to src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs index cb0900aed..ea8b50d1e 100644 --- a/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs +++ b/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs @@ -15,9 +15,10 @@ using Squidex.Infrastructure.EventSourcing; namespace Squidex.Infrastructure.States { - internal class Persistence : IPersistence + internal class Persistence : IPersistence { private readonly TKey ownerKey; + private readonly Type ownerType; private readonly ISnapshotStore snapshotStore; private readonly IStreamNameResolver streamNameResolver; private readonly IEventStore eventStore; @@ -34,7 +35,7 @@ namespace Squidex.Infrastructure.States get { return version; } } - public Persistence(TKey ownerKey, + public Persistence(TKey ownerKey, Type ownerType, IEventStore eventStore, IEventDataFormatter eventDataFormatter, ISnapshotStore snapshotStore, @@ -44,6 +45,7 @@ namespace Squidex.Infrastructure.States Func, Task> applyEvent) { this.ownerKey = ownerKey; + this.ownerType = ownerType; this.applyState = applyState; this.applyEvent = applyEvent; this.eventStore = eventStore; @@ -67,11 +69,11 @@ namespace Squidex.Infrastructure.States { if (version == EtagVersion.Empty) { - throw new DomainObjectNotFoundException(ownerKey.ToString(), typeof(TOwner)); + throw new DomainObjectNotFoundException(ownerKey.ToString(), ownerType); } else { - throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), version, expectedVersion); + throw new DomainObjectVersionException(ownerKey.ToString(), ownerType, version, expectedVersion); } } } @@ -134,7 +136,7 @@ namespace Squidex.Infrastructure.States } catch (InconsistentStateException ex) { - throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); + throw new DomainObjectVersionException(ownerKey.ToString(), ownerType, ex.CurrentVersion, ex.ExpectedVersion); } versionSnapshot = newVersion; @@ -164,7 +166,7 @@ namespace Squidex.Infrastructure.States } catch (WrongEventVersionException ex) { - throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); + throw new DomainObjectVersionException(ownerKey.ToString(), ownerType, ex.CurrentVersion, ex.ExpectedVersion); } versionEvents += eventArray.Length; @@ -180,7 +182,7 @@ namespace Squidex.Infrastructure.States private string GetStreamName() { - return streamNameResolver.GetStreamName(typeof(TOwner), ownerKey.ToString()); + return streamNameResolver.GetStreamName(ownerType, ownerKey.ToString()); } private bool UseSnapshots() diff --git a/src/Squidex.Infrastructure/States/StateFactory.cs b/src/Squidex.Infrastructure/States/StateFactory.cs index 85b9977b4..9aea78534 100644 --- a/src/Squidex.Infrastructure/States/StateFactory.cs +++ b/src/Squidex.Infrastructure/States/StateFactory.cs @@ -94,7 +94,7 @@ namespace Squidex.Infrastructure.States { Guard.NotNull(key, nameof(key)); - var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver); + var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver); var state = (T)services.GetService(typeof(T)); await state.ActivateAsync(key, stateStore); @@ -124,7 +124,7 @@ namespace Squidex.Infrastructure.States } var state = (T)services.GetService(typeof(T)); - var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver); + var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver); stateObj = new ObjectHolder(state, key, stateStore); diff --git a/src/Squidex.Infrastructure/States/Store.cs b/src/Squidex.Infrastructure/States/Store.cs index 2d1daa69c..a3d6b1bc6 100644 --- a/src/Squidex.Infrastructure/States/Store.cs +++ b/src/Squidex.Infrastructure/States/Store.cs @@ -11,7 +11,7 @@ using Squidex.Infrastructure.EventSourcing; namespace Squidex.Infrastructure.States { - internal sealed class Store : IStore + public sealed class Store : IStore { private readonly IServiceProvider services; private readonly IStreamNameResolver streamNameResolver; @@ -30,32 +30,32 @@ namespace Squidex.Infrastructure.States this.streamNameResolver = streamNameResolver; } - public IPersistence WithSnapshots(TKey key, Func applySnapshot) + public IPersistence WithSnapshots(Type owner, TKey key, Func applySnapshot) { - return CreatePersistence(key, PersistenceMode.Snapshots, applySnapshot, null); + return CreatePersistence(owner, key, PersistenceMode.Snapshots, applySnapshot, null); } - public IPersistence WithSnapshotsAndEventSourcing(TKey key, Func applySnapshot, Func, Task> applyEvent) + public IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, Func applySnapshot, Func, Task> applyEvent) { - return CreatePersistence(key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent); + return CreatePersistence(owner, key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent); } - public IPersistence WithEventSourcing(TKey key, Func, Task> applyEvent) + public IPersistence WithEventSourcing(Type owner, TKey key, Func, Task> applyEvent) { Guard.NotDefault(key, nameof(key)); var snapshotStore = (ISnapshotStore)services.GetService(typeof(ISnapshotStore)); - return new Persistence(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); + return new Persistence(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); } - private IPersistence CreatePersistence(TKey key, PersistenceMode mode, Func applySnapshot, Func, Task> applyEvent) + private IPersistence CreatePersistence(Type owner, TKey key, PersistenceMode mode, Func applySnapshot, Func, Task> applyEvent) { Guard.NotDefault(key, nameof(key)); var snapshotStore = (ISnapshotStore)services.GetService(typeof(ISnapshotStore)); - return new Persistence(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent); + return new Persistence(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent); } } } diff --git a/src/Squidex.Infrastructure/States/StoreExtensions.cs b/src/Squidex.Infrastructure/States/StoreExtensions.cs index 5a4dec34b..3cee24593 100644 --- a/src/Squidex.Infrastructure/States/StoreExtensions.cs +++ b/src/Squidex.Infrastructure/States/StoreExtensions.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Threading.Tasks; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Tasks; @@ -13,19 +14,49 @@ namespace Squidex.Infrastructure.States { public static class StoreExtensions { - public static IPersistence WithEventSourcing(this IStore store, TKey key, Action> applyEvent) + public static IPersistence WithEventSourcing(this IStore store, TKey key, Func, Task> applyEvent) { - return store.WithEventSourcing(key, applyEvent.ToAsync()); + return store.WithEventSourcing(typeof(TOwner), key, applyEvent); } - public static IPersistence WithSnapshots(this IStore store, TKey key, Action applySnapshot) + public static IPersistence WithSnapshots(this IStore store, TKey key, Func applySnapshot) { - return store.WithSnapshots(key, applySnapshot.ToAsync()); + return store.WithSnapshots(typeof(TOwner), key, applySnapshot); } - public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, TKey key, Action applySnapshot, Action> applyEvent) + public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, TKey key, Func applySnapshot, Func, Task> applyEvent) { - return store.WithSnapshotsAndEventSourcing(key, applySnapshot.ToAsync(), applyEvent.ToAsync()); + return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot, applyEvent); + } + + public static IPersistence WithEventSourcing(this IStore store, Type owner, TKey key, Action> applyEvent) + { + return store.WithEventSourcing(owner, key, applyEvent.ToAsync()); + } + + public static IPersistence WithSnapshots(this IStore store, Type owner, TKey key, Action applySnapshot) + { + return store.WithSnapshots(owner, key, applySnapshot.ToAsync()); + } + + public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, Type owner, TKey key, Action applySnapshot, Action> applyEvent) + { + return store.WithSnapshotsAndEventSourcing(owner, key, applySnapshot.ToAsync(), applyEvent.ToAsync()); + } + + public static IPersistence WithEventSourcing(this IStore store, TKey key, Action> applyEvent) + { + return store.WithEventSourcing(typeof(TOwner), key, applyEvent.ToAsync()); + } + + public static IPersistence WithSnapshots(this IStore store, TKey key, Action applySnapshot) + { + return store.WithSnapshots(typeof(TOwner), key, applySnapshot.ToAsync()); + } + + public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, TKey key, Action applySnapshot, Action> applyEvent) + { + return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot.ToAsync(), applyEvent.ToAsync()); } } } diff --git a/src/Squidex/AppConfiguration.cs b/src/Squidex/AppConfiguration.cs deleted file mode 100644 index 6c707bafb..000000000 --- a/src/Squidex/AppConfiguration.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.Extensions.Configuration; - -namespace Squidex -{ - public static class AppConfiguration - { - public static void AddAppConfiguration(this IConfigurationBuilder builder, string environmentName, string[] args) - { - builder.Sources.Clear(); - - builder.AddJsonFile("appsettings.json", true, true); - builder.AddJsonFile($"appsettings.{environmentName}.json", true); - - builder.AddEnvironmentVariables(); - - builder.AddCommandLine(args); - } - } -} diff --git a/src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs b/src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs index 24b5e1e93..aeea54e9c 100644 --- a/src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs +++ b/src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs @@ -130,7 +130,8 @@ namespace Squidex.Areas.IdentityServer.Config ClientSecrets = new List { new Secret(Constants.InternalClientSecret) }, RedirectUris = new List { - urlsOptions.BuildUrl($"{Constants.PortalPrefix}/signin-oidc", false) + urlsOptions.BuildUrl($"{Constants.PortalPrefix}/signin-oidc", false), + urlsOptions.BuildUrl($"{Constants.OrleansPrefix}/signin-oidc", false) }, AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds, AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, diff --git a/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs new file mode 100644 index 000000000..db67abdd3 --- /dev/null +++ b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs @@ -0,0 +1,43 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Http; +using Squidex.Shared.Identity; + +namespace Squidex.Areas.OrleansDashboard.Middlewares +{ + public sealed class OrleansDashboardAuthenticationMiddleware + { + private readonly RequestDelegate next; + + public OrleansDashboardAuthenticationMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task Invoke(HttpContext context) + { + var authentication = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + + if (!authentication.Succeeded || !authentication.Principal.IsInRole(SquidexRoles.Administrator)) + { + await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties + { + RedirectUri = context.Request.PathBase + context.Request.Path + }); + } + else + { + await next(context); + } + } + } +} diff --git a/src/Squidex/Areas/OrleansDashboard/Startup.cs b/src/Squidex/Areas/OrleansDashboard/Startup.cs new file mode 100644 index 000000000..d8b5da6f8 --- /dev/null +++ b/src/Squidex/Areas/OrleansDashboard/Startup.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.AspNetCore.Builder; +using Orleans; +using Squidex.Areas.OrleansDashboard.Middlewares; +using Squidex.Config; + +namespace Squidex.Areas.OrleansDashboard +{ + public static class Startup + { + public static void ConfigureOrleansDashboard(this IApplicationBuilder app) + { + app.Map(Constants.OrleansPrefix, orleansApp => + { + orleansApp.UseAuthentication(); + orleansApp.UseMiddleware(); + orleansApp.UseOrleansDashboard(); + }); + } + } +} diff --git a/src/Squidex/Config/Constants.cs b/src/Squidex/Config/Constants.cs index 9c97c3d20..bd08faef8 100644 --- a/src/Squidex/Config/Constants.cs +++ b/src/Squidex/Config/Constants.cs @@ -17,6 +17,8 @@ namespace Squidex.Config public static readonly string ApiScope = "squidex-api"; + public static readonly string OrleansPrefix = "/orleans"; + public static readonly string PortalPrefix = "/portal"; public static readonly string RoleScope = "role"; diff --git a/src/Squidex/Config/Domain/InfrastructureServices.cs b/src/Squidex/Config/Domain/InfrastructureServices.cs index 9a182bf87..0b9e57ac2 100644 --- a/src/Squidex/Config/Domain/InfrastructureServices.cs +++ b/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -94,6 +94,8 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .AsSelf(); + + services.AddSingleton(typeof(IStore<>), typeof(Store<>)); } } } diff --git a/src/Squidex/Config/Domain/ReadServices.cs b/src/Squidex/Config/Domain/ReadServices.cs index 1ce8db5b4..f1d5c81bf 100644 --- a/src/Squidex/Config/Domain/ReadServices.cs +++ b/src/Squidex/Config/Domain/ReadServices.cs @@ -44,6 +44,9 @@ namespace Squidex.Config.Domain .As(); services.AddSingletonAs() .As(); + + services.AddSingletonAs() + .As(); } var exposeSourceUrl = config.GetOptionalValue("assetStore:exposeSourceUrl", true); @@ -55,8 +58,7 @@ namespace Squidex.Config.Domain .As(); services.AddSingletonAs() - .As() - .As(); + .As().As(); services.AddSingletonAs(c => c.GetService>()?.Value?.Plans.OrEmpty()); @@ -112,7 +114,7 @@ namespace Squidex.Config.Domain .As(); services.AddSingletonAs() - .As(); + .As().As(); services.AddSingletonAs() .As(); diff --git a/src/Squidex/Config/Orleans/ClientServices.cs b/src/Squidex/Config/Orleans/ClientServices.cs deleted file mode 100644 index 60dd40a81..000000000 --- a/src/Squidex/Config/Orleans/ClientServices.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Microsoft.Extensions.DependencyInjection; -using Orleans; -using Squidex.Infrastructure.EventSourcing.Grains; - -namespace Squidex.Config.Orleans -{ - public static class ClientServices - { - public static void AddAppClient(this IServiceCollection services) - { - services.AddSingletonAs(c => c.GetRequiredService()) - .As(); - - services.AddSingletonAs(c => - { - var client = new ClientBuilder() - .ConfigureApplicationParts(builder => - { - builder.AddApplicationPart(typeof(EventConsumerGrain).Assembly); - }) - .UseStaticGatewayListProvider(options => - { - options.Gateways.Add(new Uri("gwy.tcp://127.0.0.1:40000/0")); - }) - .Build(); - - client.Connect().Wait(); - - return client; - }); - } - } -} diff --git a/src/Squidex/Config/Orleans/ClientWrapper.cs b/src/Squidex/Config/Orleans/ClientWrapper.cs new file mode 100644 index 000000000..59635c2e1 --- /dev/null +++ b/src/Squidex/Config/Orleans/ClientWrapper.cs @@ -0,0 +1,50 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Orleans; +using Orleans.Runtime.Configuration; +using Squidex.Infrastructure; + +namespace Squidex.Config.Orleans +{ + public sealed class ClientWrapper : IInitializable, IDisposable + { + private readonly IClusterClient client; + + public IClusterClient Client + { + get { return client; } + } + + public ClientWrapper() + { + client = new ClientBuilder() + .UseConfiguration(ClientConfiguration.LocalhostSilo()) + .UseDashboard() + .ConfigureApplicationParts(builder => + { + builder.AddApplicationPart(SquidexInfrastructure.Assembly); + }) + .UseStaticGatewayListProvider(options => + { + options.Gateways.Add(new Uri("gwy.tcp://127.0.0.1:40000/0")); + }) + .Build(); + } + + public void Initialize() + { + client.Connect().Wait(); + } + + public void Dispose() + { + client.Close().Wait(); + } + } +} diff --git a/src/Squidex/Config/Orleans/OrleansServices.cs b/src/Squidex/Config/Orleans/OrleansServices.cs new file mode 100644 index 000000000..8fa4f34c0 --- /dev/null +++ b/src/Squidex/Config/Orleans/OrleansServices.cs @@ -0,0 +1,40 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; +using Orleans; +using Squidex.Infrastructure; + +namespace Squidex.Config.Orleans +{ + public static class OrleansServices + { + public static void AddOrleansSilo(this IServiceCollection services) + { + services.AddSingletonAs() + .As(); + } + + public static void AddOrleansClient(this IServiceCollection services) + { + services.AddServicesForSelfHostedDashboard(null, options => + { + options.HideTrace = true; + }); + + services.AddSingletonAs() + .As() + .AsSelf(); + + services.AddSingletonAs(c => c.GetRequiredService().Client) + .As(); + + services.AddSingletonAs(c => c.GetRequiredService().Client) + .As(); + } + } +} diff --git a/src/Squidex/Config/Orleans/SiloServices.cs b/src/Squidex/Config/Orleans/SiloServices.cs index 577246eff..10c27282e 100644 --- a/src/Squidex/Config/Orleans/SiloServices.cs +++ b/src/Squidex/Config/Orleans/SiloServices.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection; using Orleans; using Orleans.Runtime; using Orleans.Runtime.Configuration; +using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing.Grains; namespace Squidex.Config.Orleans @@ -21,10 +22,6 @@ namespace Squidex.Config.Orleans { public static void AddAppSiloServices(this IServiceCollection services, IConfiguration config) { - services.AddSingletonAs() - .As>(); - - /* var clusterConfiguration = services.Where(x => x.ServiceType == typeof(ClusterConfiguration)) .Select(x => x.ImplementationInstance) @@ -33,8 +30,6 @@ namespace Squidex.Config.Orleans if (clusterConfiguration != null) { - clusterConfiguration.Globals.RegisterBootstrapProvider("EventConsumers"); - var ipConfig = config.GetRequiredValue("orleans:hostNameOrIPAddress"); if (ipConfig.Equals("Host", StringComparison.OrdinalIgnoreCase)) @@ -51,7 +46,30 @@ namespace Squidex.Config.Orleans clusterConfiguration.Defaults.PropagateActivityId = true; clusterConfiguration.Defaults.ProxyGatewayEndpoint = new IPEndPoint(IPAddress.Any, 40000); clusterConfiguration.Defaults.HostNameOrIPAddress = ipConfig; - }*/ + } + + config.ConfigureByOption("store:type", new Options + { + ["MongoDB"] = () => + { + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + + services.AddMongoDBMembershipTable(c => + { + c.ConnectionString = mongoConfiguration; + c.CollectionPrefix = "Orleans_"; + c.DatabaseName = mongoDatabaseName; + }); + + services.AddMongoDBReminders(c => + { + c.ConnectionString = mongoConfiguration; + c.CollectionPrefix = "Orleans_"; + c.DatabaseName = mongoDatabaseName; + }); + } + }); } } } \ No newline at end of file diff --git a/src/Squidex/Config/Orleans/SiloWrapper.cs b/src/Squidex/Config/Orleans/SiloWrapper.cs new file mode 100644 index 000000000..63f25c758 --- /dev/null +++ b/src/Squidex/Config/Orleans/SiloWrapper.cs @@ -0,0 +1,90 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Orleans; +using Orleans.Hosting; +using Orleans.Runtime.Configuration; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Log.Adapter; + +namespace Squidex.Config.Orleans +{ + public class SiloWrapper : IInitializable, IDisposable + { + private readonly ISiloHost silo; + + internal sealed class Source : IConfigurationSource + { + private readonly IConfigurationProvider configurationProvider; + + public Source(IConfigurationProvider configurationProvider) + { + this.configurationProvider = configurationProvider; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return configurationProvider; + } + } + + public SiloWrapper(IConfiguration configuration) + { + silo = SiloHostBuilder.CreateDefault() + .UseConfiguration(ClusterConfiguration.LocalhostPrimarySilo(33333)) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseDashboard(options => + { + options.HostSelf = false; + }) + .ConfigureApplicationParts(builder => + { + builder.AddApplicationPart(SquidexInfrastructure.Assembly); + }) + .ConfigureLogging(builder => + { + builder.AddSemanticLog(); + }) + .ConfigureServices((context, services) => + { + services.AddAppSiloServices(context.Configuration); + services.AddAppServices(context.Configuration); + }) + .ConfigureAppConfiguration((hostContext, builder) => + { + if (configuration is IConfigurationRoot root) + { + foreach (var provider in root.Providers) + { + builder.Add(new Source(provider)); + } + } + }) + .Build(); + } + + public void Initialize() + { + silo.StartAsync().Wait(); + } + + public void Dispose() + { + silo.StopAsync().Wait(); + } + + private static string GetEnvironment() + { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + + return environment ?? "Development"; + } + } +} diff --git a/src/Squidex/Program.cs b/src/Squidex/Program.cs index 5e7392532..d2f22ecfe 100644 --- a/src/Squidex/Program.cs +++ b/src/Squidex/Program.cs @@ -5,14 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.IO; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Orleans; using Orleans.Hosting; -using Orleans.Runtime.Configuration; using Squidex.Config.Orleans; -using Squidex.Infrastructure.EventSourcing.Grains; using Squidex.Infrastructure.Log.Adapter; namespace Squidex @@ -21,59 +19,28 @@ namespace Squidex { public static void Main(string[] args) { - var silo = new SiloHostBuilder() - .UseConfiguration(ClusterConfiguration.LocalhostPrimarySilo(33333)) + new WebHostBuilder() + .UseKestrel(k => { k.AddServerHeader = false; }) .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureServices((context, services) => - { - services.AddAppSiloServices(context.Configuration); - services.AddAppServices(context.Configuration); - }) - .ConfigureApplicationParts(builder => - { - builder.AddApplicationPart(typeof(EventConsumerManagerGrain).Assembly); - }) + .UseIISIntegration() + .UseStartup() .ConfigureLogging(builder => { builder.AddSemanticLog(); }) .ConfigureAppConfiguration((hostContext, builder) => { - builder.AddAppConfiguration(GetEnvironment(), args); - }) - .Build(); + builder.Sources.Clear(); - silo.StartAsync().Wait(); + builder.AddJsonFile("appsettings.json", true, true); + builder.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true); - try - { - new WebHostBuilder() - .UseKestrel(k => { k.AddServerHeader = false; }) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup() - .ConfigureLogging(builder => - { - builder.AddSemanticLog(); - }) - .ConfigureAppConfiguration((hostContext, builder) => - { - builder.AddAppConfiguration(hostContext.HostingEnvironment.EnvironmentName, args); - }) - .Build() - .Run(); - } - finally - { - silo.StopAsync().Wait(); - } - } - - private static string GetEnvironment() - { - var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + builder.AddEnvironmentVariables(); - return environment ?? "Development"; + builder.AddCommandLine(args); + }) + .Build() + .Run(); } } } diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 1f408129b..970206238 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -79,6 +79,7 @@ + diff --git a/src/Squidex/WebStartup.cs b/src/Squidex/WebStartup.cs index 08d81bf1b..7288f00a5 100644 --- a/src/Squidex/WebStartup.cs +++ b/src/Squidex/WebStartup.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection; using Squidex.Areas.Api; using Squidex.Areas.Frontend; using Squidex.Areas.IdentityServer; +using Squidex.Areas.OrleansDashboard; using Squidex.Areas.Portal; using Squidex.Config.Domain; using Squidex.Config.Orleans; @@ -31,7 +32,8 @@ namespace Squidex public IServiceProvider ConfigureServices(IServiceCollection services) { - services.AddAppClient(); + services.AddOrleansSilo(); + services.AddOrleansClient(); services.AddAppServices(configuration); return services.BuildServiceProvider(); @@ -50,6 +52,7 @@ namespace Squidex app.ConfigureApi(); app.ConfigurePortal(); + app.ConfigureOrleansDashboard(); app.ConfigureIdentityServer(); app.ConfigureFrontend(); diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index 7edf55f44..be34052ab 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -42,12 +42,20 @@ } }, + "orleans": { + /* + * Define the IP address or host name that is used for inter-silo communication. + * + * Special values: FirstIPAddressOfHost, Host + */ + "hostNameOrIPAddress": "localhost" + }, "logging": { /* * Setting the flag to true, enables well formatteds json logs. */ - "human": true + "human": true }, /*