Browse Source

Continued with basic setup.

pull/249/head
Sebastian Stehle 9 years ago
parent
commit
80dc7c12a6
  1. 5
      src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs
  2. 2
      src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs
  3. 12
      src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs
  4. 2
      src/Squidex.Infrastructure/Commands/DomainObjectBase.cs
  5. 12
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerBootstrap.cs
  6. 7
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  7. 4
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs
  8. 12
      src/Squidex.Infrastructure/EventSourcing/Grains/OrleansEventNotifier.cs
  9. 6
      src/Squidex.Infrastructure/States/IStore.cs
  10. 6
      src/Squidex.Infrastructure/States/Persistence.cs
  11. 16
      src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs
  12. 4
      src/Squidex.Infrastructure/States/StateFactory.cs
  13. 18
      src/Squidex.Infrastructure/States/Store.cs
  14. 43
      src/Squidex.Infrastructure/States/StoreExtensions.cs
  15. 26
      src/Squidex/AppConfiguration.cs
  16. 3
      src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs
  17. 43
      src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs
  18. 27
      src/Squidex/Areas/OrleansDashboard/Startup.cs
  19. 2
      src/Squidex/Config/Constants.cs
  20. 2
      src/Squidex/Config/Domain/InfrastructureServices.cs
  21. 8
      src/Squidex/Config/Domain/ReadServices.cs
  22. 41
      src/Squidex/Config/Orleans/ClientServices.cs
  23. 50
      src/Squidex/Config/Orleans/ClientWrapper.cs
  24. 40
      src/Squidex/Config/Orleans/OrleansServices.cs
  25. 32
      src/Squidex/Config/Orleans/SiloServices.cs
  26. 90
      src/Squidex/Config/Orleans/SiloWrapper.cs
  27. 59
      src/Squidex/Program.cs
  28. 1
      src/Squidex/Squidex.csproj
  29. 5
      src/Squidex/WebStartup.cs
  30. 10
      src/Squidex/appsettings.json

5
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()

2
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<IReadOnlyList<StoredEvent>> QueryAsync(string streamName, long streamPosition = 0)

12
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<JToken>.Instance);
}
else if (memberMap.MemberType == typeof(JObject))
{
memberMap.SetSerializer(JTokenSerializer<JObject>.Instance);
}
else if (memberMap.MemberType == typeof(JValue))
{
memberMap.SetSerializer(JTokenSerializer<JValue>.Instance);
}
});
ConventionRegistry.Register("json", pack, t => true);

2
src/Squidex.Infrastructure/Commands/DomainObjectBase.cs

@ -34,7 +34,7 @@ namespace Squidex.Infrastructure.Commands
{
id = key;
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(key, ApplySnapshot, ApplyEvent);
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), key, ApplySnapshot, ApplyEvent);
return persistence.ReadAsync();
}

12
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<ISiloLifecycle>
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<IEventConsumerManagerGrain>("Default");
var grain = grainFactory.GetGrain<IEventConsumerManagerGrain>("Default");
return grain.ActivateAsync();
});
grain.ActivateAsync().Forget();
}
}
}

7
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<string> store;
private readonly IEventDataFormatter eventDataFormatter;
private readonly IEventStore eventStore;
private readonly ISemanticLog log;
private readonly IPersistence<EventConsumerState> persistence;
private TaskScheduler scheduler;
private IPersistence<EventConsumerState> 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<EventConsumerState, string>(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<EventConsumerGrain, EventConsumerState, string>(this.GetPrimaryKeyString(), s => state = s);
return persistence.ReadAsync();
}

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

12
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<IEventConsumerManagerGrain>("Default");
}
public void NotifyEventsStored(string streamName)
{
eventConsumerManagerGrain.WakeUpAsync(streamName);
eventConsumerManagerGrain?.WakeUpAsync(streamName);
}
public IDisposable Subscribe(Action<string> handler)

6
src/Squidex.Infrastructure/States/IStore.cs

@ -13,10 +13,10 @@ namespace Squidex.Infrastructure.States
{
public interface IStore<TKey>
{
IPersistence WithEventSourcing(TKey key, Func<Envelope<IEvent>, Task> applyEvent);
IPersistence WithEventSourcing(Type owner, TKey key, Func<Envelope<IEvent>, Task> applyEvent);
IPersistence<T> WithSnapshots<T>(TKey key, Func<T, Task> applySnapshot);
IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot);
IPersistence<T> WithSnapshotsAndEventSourcing<T>(TKey key, Func<T, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent);
IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent);
}
}

6
src/Squidex.Infrastructure/States/Persistence.cs

@ -11,15 +11,15 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
internal sealed class Persistence<TOwner, TKey> : Persistence<TOwner, object, TKey>, IPersistence
internal sealed class Persistence<TKey> : Persistence<object, TKey>, IPersistence
{
public Persistence(TKey ownerKey,
public Persistence(TKey ownerKey, Type ownerType,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISnapshotStore<object, TKey> snapshotStore,
IStreamNameResolver streamNameResolver,
Func<Envelope<IEvent>, Task> applyEvent)
: base(ownerKey, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent)
: base(ownerKey, ownerType, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent)
{
}
}

16
src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs → src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs

@ -15,9 +15,10 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
internal class Persistence<TOwner, TSnapshot, TKey> : IPersistence<TSnapshot>
internal class Persistence<TSnapshot, TKey> : IPersistence<TSnapshot>
{
private readonly TKey ownerKey;
private readonly Type ownerType;
private readonly ISnapshotStore<TSnapshot, TKey> 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<TSnapshot, TKey> snapshotStore,
@ -44,6 +45,7 @@ namespace Squidex.Infrastructure.States
Func<Envelope<IEvent>, 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()

4
src/Squidex.Infrastructure/States/StateFactory.cs

@ -94,7 +94,7 @@ namespace Squidex.Infrastructure.States
{
Guard.NotNull(key, nameof(key));
var stateStore = new Store<T, TKey>(eventStore, eventDataFormatter, services, streamNameResolver);
var stateStore = new Store<TKey>(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<T, TKey>(eventStore, eventDataFormatter, services, streamNameResolver);
var stateStore = new Store<TKey>(eventStore, eventDataFormatter, services, streamNameResolver);
stateObj = new ObjectHolder<T, TKey>(state, key, stateStore);

18
src/Squidex.Infrastructure/States/Store.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
internal sealed class Store<TOwner, TKey> : IStore<TKey>
public sealed class Store<TKey> : IStore<TKey>
{
private readonly IServiceProvider services;
private readonly IStreamNameResolver streamNameResolver;
@ -30,32 +30,32 @@ namespace Squidex.Infrastructure.States
this.streamNameResolver = streamNameResolver;
}
public IPersistence<TState> WithSnapshots<TState>(TKey key, Func<TState, Task> applySnapshot)
public IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot)
{
return CreatePersistence(key, PersistenceMode.Snapshots, applySnapshot, null);
return CreatePersistence<TState>(owner, key, PersistenceMode.Snapshots, applySnapshot, null);
}
public IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
public IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
return CreatePersistence(key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent);
return CreatePersistence<TState>(owner, key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent);
}
public IPersistence WithEventSourcing(TKey key, Func<Envelope<IEvent>, Task> applyEvent)
public IPersistence WithEventSourcing(Type owner, TKey key, Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotDefault(key, nameof(key));
var snapshotStore = (ISnapshotStore<object, TKey>)services.GetService(typeof(ISnapshotStore<object, TKey>));
return new Persistence<TOwner, TKey>(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent);
return new Persistence<TKey>(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent);
}
private IPersistence<TState> CreatePersistence<TState>(TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
private IPersistence<TState> CreatePersistence<TState>(Type owner, TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotDefault(key, nameof(key));
var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));
return new Persistence<TOwner, TState, TKey>(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent);
return new Persistence<TState, TKey>(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent);
}
}
}

43
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<TKey>(this IStore<TKey> store, TKey key, Action<Envelope<IEvent>> applyEvent)
public static IPersistence WithEventSourcing<TOwner, TKey>(this IStore<TKey> store, TKey key, Func<Envelope<IEvent>, Task> applyEvent)
{
return store.WithEventSourcing(key, applyEvent.ToAsync());
return store.WithEventSourcing(typeof(TOwner), key, applyEvent);
}
public static IPersistence<TState> WithSnapshots<TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot)
public static IPersistence<TState> WithSnapshots<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Func<TState, Task> applySnapshot)
{
return store.WithSnapshots(key, applySnapshot.ToAsync());
return store.WithSnapshots<TState>(typeof(TOwner), key, applySnapshot);
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot, Action<Envelope<IEvent>> applyEvent)
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
return store.WithSnapshotsAndEventSourcing(key, applySnapshot.ToAsync(), applyEvent.ToAsync());
return store.WithSnapshotsAndEventSourcing<TState>(typeof(TOwner), key, applySnapshot, applyEvent);
}
public static IPersistence WithEventSourcing<TKey>(this IStore<TKey> store, Type owner, TKey key, Action<Envelope<IEvent>> applyEvent)
{
return store.WithEventSourcing(owner, key, applyEvent.ToAsync());
}
public static IPersistence<TState> WithSnapshots<TState, TKey>(this IStore<TKey> store, Type owner, TKey key, Action<TState> applySnapshot)
{
return store.WithSnapshots<TState>(owner, key, applySnapshot.ToAsync());
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TState, TKey>(this IStore<TKey> store, Type owner, TKey key, Action<TState> applySnapshot, Action<Envelope<IEvent>> applyEvent)
{
return store.WithSnapshotsAndEventSourcing<TState>(owner, key, applySnapshot.ToAsync(), applyEvent.ToAsync());
}
public static IPersistence WithEventSourcing<TOwner, TKey>(this IStore<TKey> store, TKey key, Action<Envelope<IEvent>> applyEvent)
{
return store.WithEventSourcing(typeof(TOwner), key, applyEvent.ToAsync());
}
public static IPersistence<TState> WithSnapshots<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot)
{
return store.WithSnapshots<TState>(typeof(TOwner), key, applySnapshot.ToAsync());
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot, Action<Envelope<IEvent>> applyEvent)
{
return store.WithSnapshotsAndEventSourcing<TState>(typeof(TOwner), key, applySnapshot.ToAsync(), applyEvent.ToAsync());
}
}
}

26
src/Squidex/AppConfiguration.cs

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

3
src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs

@ -130,7 +130,8 @@ namespace Squidex.Areas.IdentityServer.Config
ClientSecrets = new List<Secret> { new Secret(Constants.InternalClientSecret) },
RedirectUris = new List<string>
{
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,

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

27
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<OrleansDashboardAuthenticationMiddleware>();
orleansApp.UseOrleansDashboard();
});
}
}
}

2
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";

2
src/Squidex/Config/Domain/InfrastructureServices.cs

@ -94,6 +94,8 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<Migrator>()
.AsSelf();
services.AddSingleton(typeof(IStore<>), typeof(Store<>));
}
}
}

8
src/Squidex/Config/Domain/ReadServices.cs

@ -44,6 +44,9 @@ namespace Squidex.Config.Domain
.As<IRunnable>();
services.AddSingletonAs<ContentScheduler>()
.As<IRunnable>();
services.AddSingletonAs<EventConsumerBootstrap>()
.As<IRunnable>();
}
var exposeSourceUrl = config.GetOptionalValue("assetStore:exposeSourceUrl", true);
@ -55,8 +58,7 @@ namespace Squidex.Config.Domain
.As<IGraphQLUrlGenerator>();
services.AddSingletonAs<StateFactory>()
.As<IInitializable>()
.As<IStateFactory>();
.As<IStateFactory>().As<IInitializable>();
services.AddSingletonAs(c => c.GetService<IOptions<MyUsageOptions>>()?.Value?.Plans.OrEmpty());
@ -112,7 +114,7 @@ namespace Squidex.Config.Domain
.As<IRuleActionHandler>();
services.AddSingletonAs<OrleansEventNotifier>()
.As<IEventNotifier>();
.As<IEventNotifier>().As<IInitializable>();
services.AddSingletonAs<RuleEnqueuer>()
.As<IEventConsumer>();

41
src/Squidex/Config/Orleans/ClientServices.cs

@ -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<IClusterClient>())
.As<IGrainFactory>();
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;
});
}
}
}

50
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();
}
}
}

40
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<SiloWrapper>()
.As<IInitializable>();
}
public static void AddOrleansClient(this IServiceCollection services)
{
services.AddServicesForSelfHostedDashboard(null, options =>
{
options.HideTrace = true;
});
services.AddSingletonAs<ClientWrapper>()
.As<IInitializable>()
.AsSelf();
services.AddSingletonAs(c => c.GetRequiredService<ClientWrapper>().Client)
.As<IClusterClient>();
services.AddSingletonAs(c => c.GetRequiredService<ClientWrapper>().Client)
.As<IGrainFactory>();
}
}
}

32
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<EventConsumerBootstrap>()
.As<ILifecycleParticipant<ISiloLifecycle>>();
/*
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<EventConsumerBootstrap>("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;
});
}
});
}
}
}

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

59
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<WebStartup>()
.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<WebStartup>()
.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();
}
}
}

1
src/Squidex/Squidex.csproj

@ -79,6 +79,7 @@
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.0.0" />
<PackageReference Include="NSwag.AspNetCore" Version="11.12.16" />
<PackageReference Include="OpenCover" Version="4.6.519" />
<PackageReference Include="Orleans.Providers.MongoDB" Version="2.0.0-preview2" />
<PackageReference Include="OrleansDashboard" Version="2.0.0-beta4" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" />
<PackageReference Include="ReportGenerator" Version="3.1.1" />

5
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();

10
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
},
/*

Loading…
Cancel
Save