From b41cec579ed67ee16bea589f04ac2462296b8856 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 11 Nov 2017 23:40:54 +0100 Subject: [PATCH 1/4] No Autofac --- .../Apps/MongoAppRepository.cs | 2 +- .../Assets/MongoAssetRepository.cs | 2 +- .../Assets/MongoAssetStatsRepository.cs | 2 +- .../Schemas/MongoSchemaRepository.cs | 2 +- .../Apps/IAppEventConsumer.cs | 16 ++ .../Assets/IAssetEventConsumer.cs | 16 ++ .../Schemas/ISchemaEventConsumer.cs | 16 ++ .../CQRS/Events/CompoundEventConsumer.cs | 17 ++ .../CollectionExtensions.cs | 10 + src/Squidex/Config/Domain/AssetServices.cs | 51 +++++ src/Squidex/Config/Domain/AssetStoreModule.cs | 91 -------- ...rsModule.cs => EventPublishersServices.cs} | 28 +-- .../{Usages.cs => EventStoreExtensions.cs} | 18 +- src/Squidex/Config/Domain/EventStoreModule.cs | 112 ---------- .../Config/Domain/EventStoreServices.cs | 64 ++++++ .../Config/Domain/InfrastructureModule.cs | 155 ------------- .../Config/Domain/InfrastructureServices.cs | 116 ++++++++++ src/Squidex/Config/Domain/PubSubModule.cs | 69 ------ src/Squidex/Config/Domain/PubSubServices.cs | 41 ++++ src/Squidex/Config/Domain/ReadModule.cs | 139 ------------ src/Squidex/Config/Domain/ReadServices.cs | 114 ++++++++++ ...erializers.cs => SerializationServices.cs} | 8 +- src/Squidex/Config/Domain/StoreModule.cs | 44 ---- .../Config/Domain/StoreMongoDbModule.cs | 208 ------------------ src/Squidex/Config/Domain/StoreServices.cs | 122 ++++++++++ src/Squidex/Config/Domain/SystemExtensions.cs | 30 +++ src/Squidex/Config/Domain/WriteModule.cs | 113 ---------- src/Squidex/Config/Domain/WriteServices.cs | 79 +++++++ ...onUsage.cs => AuthenticationExtensions.cs} | 4 +- .../Config/Identity/AuthenticationServices.cs | 6 +- ...IdentityUsage.cs => IdentityExtensions.cs} | 4 +- .../Config/Identity/IdentityServerServices.cs | 103 +++++++++ .../Config/Identity/IdentityServices.cs | 130 +---------- src/Squidex/Config/Options.cs | 21 ++ src/Squidex/Config/ServiceExtensions.cs | 94 ++++++++ .../{SwaggerUsage.cs => SwaggerExtensions.cs} | 6 +- .../Web/{WebUsages.cs => WebExtensions.cs} | 2 +- src/Squidex/Config/Web/WebModule.cs | 35 --- .../{WebDependencies.cs => WebServices.cs} | 8 +- ...{WebpackUsages.cs => WebpackExtensions.cs} | 4 +- src/Squidex/Squidex.csproj | 2 - src/Squidex/Startup.cs | 50 ++--- 42 files changed, 978 insertions(+), 1176 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Read/Apps/IAppEventConsumer.cs create mode 100644 src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs create mode 100644 src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs create mode 100644 src/Squidex/Config/Domain/AssetServices.cs delete mode 100644 src/Squidex/Config/Domain/AssetStoreModule.cs rename src/Squidex/Config/Domain/{EventPublishersModule.cs => EventPublishersServices.cs} (70%) rename src/Squidex/Config/Domain/{Usages.cs => EventStoreExtensions.cs} (73%) delete mode 100644 src/Squidex/Config/Domain/EventStoreModule.cs create mode 100644 src/Squidex/Config/Domain/EventStoreServices.cs delete mode 100644 src/Squidex/Config/Domain/InfrastructureModule.cs create mode 100644 src/Squidex/Config/Domain/InfrastructureServices.cs delete mode 100644 src/Squidex/Config/Domain/PubSubModule.cs create mode 100644 src/Squidex/Config/Domain/PubSubServices.cs delete mode 100644 src/Squidex/Config/Domain/ReadModule.cs create mode 100644 src/Squidex/Config/Domain/ReadServices.cs rename src/Squidex/Config/Domain/{Serializers.cs => SerializationServices.cs} (94%) delete mode 100644 src/Squidex/Config/Domain/StoreModule.cs delete mode 100644 src/Squidex/Config/Domain/StoreMongoDbModule.cs create mode 100644 src/Squidex/Config/Domain/StoreServices.cs create mode 100644 src/Squidex/Config/Domain/SystemExtensions.cs delete mode 100644 src/Squidex/Config/Domain/WriteModule.cs create mode 100644 src/Squidex/Config/Domain/WriteServices.cs rename src/Squidex/Config/Identity/{AuthenticationUsage.cs => AuthenticationExtensions.cs} (87%) rename src/Squidex/Config/Identity/{IdentityUsage.cs => IdentityExtensions.cs} (97%) create mode 100644 src/Squidex/Config/Identity/IdentityServerServices.cs create mode 100644 src/Squidex/Config/Options.cs create mode 100644 src/Squidex/Config/ServiceExtensions.cs rename src/Squidex/Config/Swagger/{SwaggerUsage.cs => SwaggerExtensions.cs} (81%) rename src/Squidex/Config/Web/{WebUsages.cs => WebExtensions.cs} (98%) delete mode 100644 src/Squidex/Config/Web/WebModule.cs rename src/Squidex/Config/Web/{WebDependencies.cs => WebServices.cs} (71%) rename src/Squidex/Config/Web/{WebpackUsages.cs => WebpackExtensions.cs} (92%) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs index c6f475e2e..92050fb6d 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs @@ -18,7 +18,7 @@ using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Apps { - public partial class MongoAppRepository : MongoRepositoryBase, IAppRepository, IEventConsumer + public partial class MongoAppRepository : MongoRepositoryBase, IAppRepository, IAppEventConsumer { public MongoAppRepository(IMongoDatabase database) : base(database) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs index 87b055d15..e00e6e34d 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs @@ -19,7 +19,7 @@ using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Assets { - public partial class MongoAssetRepository : MongoRepositoryBase, IAssetRepository, IEventConsumer + public partial class MongoAssetRepository : MongoRepositoryBase, IAssetRepository, IAssetEventConsumer { public MongoAssetRepository(IMongoDatabase database) : base(database) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs index 9b0495efe..2ef42ad04 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs @@ -19,7 +19,7 @@ using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Assets { - public partial class MongoAssetStatsRepository : MongoRepositoryBase, IAssetStatsRepository, IEventConsumer + public partial class MongoAssetStatsRepository : MongoRepositoryBase, IAssetStatsRepository, IAssetEventConsumer { public MongoAssetStatsRepository(IMongoDatabase database) : base(database) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs index a43da664f..c9c9fb5f8 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs @@ -20,7 +20,7 @@ using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { - public partial class MongoSchemaRepository : MongoRepositoryBase, ISchemaRepository, IEventConsumer + public partial class MongoSchemaRepository : MongoRepositoryBase, ISchemaRepository, ISchemaEventConsumer { private readonly FieldRegistry registry; diff --git a/src/Squidex.Domain.Apps.Read/Apps/IAppEventConsumer.cs b/src/Squidex.Domain.Apps.Read/Apps/IAppEventConsumer.cs new file mode 100644 index 000000000..c34d229cf --- /dev/null +++ b/src/Squidex.Domain.Apps.Read/Apps/IAppEventConsumer.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// IAppEventConsumer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Infrastructure.CQRS.Events; + +namespace Squidex.Domain.Apps.Read.Apps +{ + public interface IAppEventConsumer : IEventConsumer + { + } +} diff --git a/src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs b/src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs new file mode 100644 index 000000000..28ebcf5d5 --- /dev/null +++ b/src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// IAssetEventConsumer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Infrastructure.CQRS.Events; + +namespace Squidex.Domain.Apps.Read.Assets +{ + public interface IAssetEventConsumer : IEventConsumer + { + } +} diff --git a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs new file mode 100644 index 000000000..f94bdee68 --- /dev/null +++ b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// ISchemaEventConsumer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Infrastructure.CQRS.Events; + +namespace Squidex.Domain.Apps.Read.Schemas +{ + public interface ISchemaEventConsumer : IEventConsumer + { + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs index ae167f3a5..d86bdf58a 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -24,6 +25,22 @@ namespace Squidex.Infrastructure.CQRS.Events { } + public CompoundEventConsumer(IEventConsumer[] inners) + { + Guard.NotNull(inners, nameof(inners)); + Guard.NotEmpty(inners, nameof(inners)); + + this.inners = inners; + + Name = inners.First().Name; + + var innerFilters = + this.inners.Where(x => !string.IsNullOrWhiteSpace(x.EventsFilter)) + .Select(x => $"({x.EventsFilter})"); + + EventsFilter = string.Join("|", innerFilters); + } + public CompoundEventConsumer(string name, IEventConsumer first, params IEventConsumer[] inners) { Guard.NotNull(first, nameof(first)); diff --git a/src/Squidex.Infrastructure/CollectionExtensions.cs b/src/Squidex.Infrastructure/CollectionExtensions.cs index da9268948..ea0f8362e 100644 --- a/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -30,6 +30,16 @@ namespace Squidex.Infrastructure } } + public static IEnumerable OrEmpty(this IEnumerable source) + { + return source ?? Enumerable.Empty(); + } + + public static IEnumerable Concat(this IEnumerable source, T value) + { + return source.Concat(Enumerable.Repeat(value, 1)); + } + public static int SequentialHashCode(this IEnumerable collection) { return collection.SequentialHashCode(EqualityComparer.Default); diff --git a/src/Squidex/Config/Domain/AssetServices.cs b/src/Squidex/Config/Domain/AssetServices.cs new file mode 100644 index 000000000..cca549879 --- /dev/null +++ b/src/Squidex/Config/Domain/AssetServices.cs @@ -0,0 +1,51 @@ +// ========================================================================== +// AssetServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Assets; +using Squidex.Infrastructure.Log; + +namespace Squidex.Config.Domain +{ + public static class AssetServices + { + public static void AddMyAssetServices(this IServiceCollection services, IConfiguration config) + { + config.ConfigureByOption("assetStore:type", new Options + { + ["Folder"] = () => + { + var path = config.GetRequiredValue("assetStore:folder:path"); + + services.AddSingleton(c => new FolderAssetStore(path, c.GetRequiredService())) + .As() + .As(); + }, + ["GoogleCloud"] = () => + { + var bucketName = config.GetRequiredValue("assetStore:googleCloud:bucket"); + + services.AddSingleton(c => new GoogleCloudAssetStore(bucketName)) + .As() + .As(); + }, + ["AzureBlob"] = () => + { + var connectionString = config.GetRequiredValue("assetStore:azureBlob:connectionString"); + var containerName = config.GetRequiredValue("assetStore:azureBlob:containerName"); + + services.AddSingleton(c => new AzureBlobAssetStore(connectionString, containerName)) + .As() + .As(); + } + }); + } + } +} diff --git a/src/Squidex/Config/Domain/AssetStoreModule.cs b/src/Squidex/Config/Domain/AssetStoreModule.cs deleted file mode 100644 index 683dbe70e..000000000 --- a/src/Squidex/Config/Domain/AssetStoreModule.cs +++ /dev/null @@ -1,91 +0,0 @@ -// ========================================================================== -// AssetStoreModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Autofac; -using Microsoft.Extensions.Configuration; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Assets; -using Squidex.Infrastructure.Log; - -namespace Squidex.Config.Domain -{ - public sealed class AssetStoreModule : Module - { - private IConfiguration Configuration { get; } - - public AssetStoreModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - var assetStoreType = Configuration.GetValue("assetStore:type"); - - if (string.IsNullOrWhiteSpace(assetStoreType)) - { - throw new ConfigurationException("Configure the AssetStore type with 'assetStore:type'."); - } - - if (string.Equals(assetStoreType, "Folder", StringComparison.OrdinalIgnoreCase)) - { - var path = Configuration.GetValue("assetStore:folder:path"); - - if (string.IsNullOrWhiteSpace(path)) - { - throw new ConfigurationException("Configure AssetStore Folder path with 'assetStore:folder:path'."); - } - - builder.Register(c => new FolderAssetStore(path, c.Resolve())) - .As() - .As() - .SingleInstance(); - } - else if (string.Equals(assetStoreType, "GoogleCloud", StringComparison.OrdinalIgnoreCase)) - { - var bucketName = Configuration.GetValue("assetStore:googleCloud:bucket"); - - if (string.IsNullOrWhiteSpace(bucketName)) - { - throw new ConfigurationException("Configure AssetStore GoogleCloud bucket with 'assetStore:googleCloud:bucket'."); - } - - builder.Register(c => new GoogleCloudAssetStore(bucketName)) - .As() - .As() - .SingleInstance(); - } - else if (string.Equals(assetStoreType, "AzureBlob", StringComparison.OrdinalIgnoreCase)) - { - var connectionString = Configuration.GetValue("assetStore:azureBlob:connectionString"); - - if (string.IsNullOrWhiteSpace(connectionString)) - { - throw new ConfigurationException("Configure AssetStore AzureBlob connection string with 'assetStore:azureBlob:connectionString'."); - } - - var containerName = Configuration.GetValue("assetStore:azureBlob:containerName"); - - if (string.IsNullOrWhiteSpace(containerName)) - { - throw new ConfigurationException("Configure AssetStore AzureBlob container with 'assetStore:azureBlob:containerName'."); - } - - builder.Register(c => new AzureBlobAssetStore(connectionString, containerName)) - .As() - .As() - .SingleInstance(); - } - else - { - throw new ConfigurationException($"Unsupported value '{assetStoreType}' for 'assetStore:type', supported: AzureBlob, Folder, GoogleCloud."); - } - } - } -} diff --git a/src/Squidex/Config/Domain/EventPublishersModule.cs b/src/Squidex/Config/Domain/EventPublishersServices.cs similarity index 70% rename from src/Squidex/Config/Domain/EventPublishersModule.cs rename to src/Squidex/Config/Domain/EventPublishersServices.cs index bafc5b3cf..90b46821b 100644 --- a/src/Squidex/Config/Domain/EventPublishersModule.cs +++ b/src/Squidex/Config/Domain/EventPublishersServices.cs @@ -1,5 +1,5 @@ // ========================================================================== -// EventPublishersModule.cs +// EventPublishersServices.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -7,26 +7,19 @@ // ========================================================================== using System; -using Autofac; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; namespace Squidex.Config.Domain { - public sealed class EventPublishersModule : Module + public static class EventPublishersServices { - private IConfiguration Configuration { get; } - - public EventPublishersModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) + public static void AddMyEventPublishersServices(this IServiceCollection services, IConfiguration configuration) { - var eventPublishers = Configuration.GetSection("eventPublishers"); + var eventPublishers = configuration.GetSection("eventPublishers"); foreach (var child in eventPublishers.GetChildren()) { @@ -37,15 +30,15 @@ namespace Squidex.Config.Domain throw new ConfigurationException($"Configure EventPublisher type with 'eventPublishers:{child.Key}:type'."); } - var eventsFilter = Configuration.GetValue("eventsFilter"); + var eventsFilter = configuration.GetValue("eventsFilter"); var enabled = child.GetValue("enabled"); if (string.Equals(eventPublisherType, "RabbitMq", StringComparison.OrdinalIgnoreCase)) { - var configuration = child.GetValue("configuration"); + var publisherConfig = child.GetValue("configuration"); - if (string.IsNullOrWhiteSpace(configuration)) + if (string.IsNullOrWhiteSpace(publisherConfig)) { throw new ConfigurationException($"Configure EventPublisher RabbitMq configuration with 'eventPublishers:{child.Key}:configuration'."); } @@ -61,10 +54,9 @@ namespace Squidex.Config.Domain if (enabled) { - builder.Register(c => new RabbitMqEventConsumer(c.Resolve(), name, configuration, exchange, eventsFilter)) + services.AddSingleton(c => new RabbitMqEventConsumer(c.GetRequiredService(), name, publisherConfig, exchange, eventsFilter)) .As() - .As() - .SingleInstance(); + .As(); } } else diff --git a/src/Squidex/Config/Domain/Usages.cs b/src/Squidex/Config/Domain/EventStoreExtensions.cs similarity index 73% rename from src/Squidex/Config/Domain/Usages.cs rename to src/Squidex/Config/Domain/EventStoreExtensions.cs index d9574bfa6..0a925c656 100644 --- a/src/Squidex/Config/Domain/Usages.cs +++ b/src/Squidex/Config/Domain/EventStoreExtensions.cs @@ -1,22 +1,20 @@ // ========================================================================== -// Usages.cs +// EventStoreUsages.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== -using System.Collections.Generic; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using Squidex.Infrastructure; using Squidex.Infrastructure.Actors; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events.Actors; namespace Squidex.Config.Domain { - public static class Usages + public static class EventStoreExtensions { public static IApplicationBuilder UseMyEventStore(this IApplicationBuilder app) { @@ -40,17 +38,5 @@ namespace Squidex.Config.Domain return app; } - - public static IApplicationBuilder TestExternalSystems(this IApplicationBuilder app) - { - var systems = app.ApplicationServices.GetRequiredService>(); - - foreach (var system in systems) - { - system.Connect(); - } - - return app; - } } } diff --git a/src/Squidex/Config/Domain/EventStoreModule.cs b/src/Squidex/Config/Domain/EventStoreModule.cs deleted file mode 100644 index ea282e94b..000000000 --- a/src/Squidex/Config/Domain/EventStoreModule.cs +++ /dev/null @@ -1,112 +0,0 @@ -// ========================================================================== -// EventStoreModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Autofac; -using Autofac.Core; -using EventStore.ClientAPI; -using Microsoft.Extensions.Configuration; -using MongoDB.Driver; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Events; -using Squidex.Infrastructure.CQRS.Events.Actors; - -namespace Squidex.Config.Domain -{ - public sealed class EventStoreModule : Module - { - private const string MongoClientRegistration = "EventStoreMongoClient"; - private const string MongoDatabaseRegistration = "EventStoreMongoDatabase"; - - private IConfiguration Configuration { get; } - - public EventStoreModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - var consumeEvents = Configuration.GetValue("eventStore:consume"); - - if (consumeEvents) - { - builder.RegisterType() - .AsSelf() - .InstancePerDependency(); - } - - var eventStoreType = Configuration.GetValue("eventStore:type"); - - if (string.IsNullOrWhiteSpace(eventStoreType)) - { - throw new ConfigurationException("Configure EventStore type with 'eventStore:type'."); - } - - if (string.Equals(eventStoreType, "MongoDb", StringComparison.OrdinalIgnoreCase)) - { - var configuration = Configuration.GetValue("eventStore:mongoDb:configuration"); - - if (string.IsNullOrWhiteSpace(configuration)) - { - throw new ConfigurationException("Configure EventStore MongoDb configuration with 'eventStore:mongoDb:configuration'."); - } - - var database = Configuration.GetValue("eventStore:mongoDb:database"); - - if (string.IsNullOrWhiteSpace(database)) - { - throw new ConfigurationException("Configure EventStore MongoDb Database name with 'eventStore:mongoDb:database'."); - } - - builder.Register(c => Singletons.GetOrAdd(configuration, s => new MongoClient(s))) - .Named(MongoClientRegistration) - .SingleInstance(); - - builder.Register(c => c.ResolveNamed(MongoClientRegistration).GetDatabase(database)) - .Named(MongoDatabaseRegistration) - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .SingleInstance(); - } - else if (string.Equals(eventStoreType, "GetEventStore", StringComparison.OrdinalIgnoreCase)) - { - var configuration = Configuration.GetValue("eventStore:getEventStore:configuration"); - - if (string.IsNullOrWhiteSpace(configuration)) - { - throw new ConfigurationException("Configure GetEventStore EventStore configuration with 'eventStore:getEventStore:configuration'."); - } - - var projectionHost = Configuration.GetValue("eventStore:getEventStore:projectionHost"); - - if (string.IsNullOrWhiteSpace(projectionHost)) - { - throw new ConfigurationException("Configure GetEventStore EventStore projection host with 'eventStore:getEventStore:projectionHost'."); - } - - var prefix = Configuration.GetValue("eventStore:getEventStore:prefix"); - - var connection = EventStoreConnection.Create(configuration); - - builder.Register(c => new GetEventStore(connection, prefix, projectionHost)) - .As() - .As() - .SingleInstance(); - } - else - { - throw new ConfigurationException($"Unsupported value '{eventStoreType}' for 'eventStore:type', supported: MongoDb, GetEventStore."); - } - } - } -} diff --git a/src/Squidex/Config/Domain/EventStoreServices.cs b/src/Squidex/Config/Domain/EventStoreServices.cs new file mode 100644 index 000000000..e3173e3fd --- /dev/null +++ b/src/Squidex/Config/Domain/EventStoreServices.cs @@ -0,0 +1,64 @@ +// ========================================================================== +// EventStoreServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using EventStore.ClientAPI; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Driver; +using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.CQRS.Events.Actors; + +namespace Squidex.Config.Domain +{ + public static class EventStoreServices + { + public static void AddMyEventStoreServices(this IServiceCollection services, IConfiguration config) + { + var consumeEvents = config.GetOptionalValue("eventStore:consume", false); + + if (!consumeEvents) + { + return; + } + + config.ConfigureByOption("eventStore:type", new Options + { + ["MongoDb"] = () => + { + var mongoConfiguration = config.GetRequiredValue("eventStore:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("eventStore:mongoDb:database"); + + services.AddSingleton(c => + { + var mongoClient = Singletons.GetOrAdd(mongoConfiguration, s => new MongoClient(s)); + var mongDatabase = mongoClient.GetDatabase(mongoDatabaseName); + + return new MongoEventStore(mongDatabase, c.GetRequiredService()); + }) + .As() + .As(); + }, + ["GetEventStore"] = () => + { + var eventStoreConfiguration = config.GetRequiredValue("eventStore:getEventStore:configuration"); + var eventStoreProjectionHost = config.GetRequiredValue("eventStore:getEventStore:projectionHost"); + var eventStorePrefix = config.GetValue("eventStore:getEventStore:prefix"); + + var connection = EventStoreConnection.Create(eventStoreConfiguration); + + services.AddSingleton(c => new GetEventStore(connection, eventStorePrefix, eventStoreProjectionHost)) + .As() + .As(); + } + }); + + services.AddSingleton(); + } + } +} diff --git a/src/Squidex/Config/Domain/InfrastructureModule.cs b/src/Squidex/Config/Domain/InfrastructureModule.cs deleted file mode 100644 index 67e0e731c..000000000 --- a/src/Squidex/Config/Domain/InfrastructureModule.cs +++ /dev/null @@ -1,155 +0,0 @@ -// ========================================================================== -// InfrastructureModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Autofac; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using NodaTime; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Actors; -using Squidex.Infrastructure.Assets; -using Squidex.Infrastructure.Assets.ImageSharp; -using Squidex.Infrastructure.Caching; -using Squidex.Infrastructure.CQRS.Commands; -using Squidex.Infrastructure.CQRS.Events; -using Squidex.Infrastructure.Log; -using Squidex.Infrastructure.UsageTracking; -using Squidex.Pipeline; - -namespace Squidex.Config.Domain -{ - public sealed class InfrastructureModule : Module - { - private IConfiguration Configuration { get; } - - public InfrastructureModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - if (Configuration.GetValue("logging:human")) - { - builder.Register(c => new Func(() => new JsonLogWriter(Formatting.Indented, true))) - .AsSelf() - .SingleInstance(); - } - else - { - builder.Register(c => new Func(() => new JsonLogWriter())) - .AsSelf() - .SingleInstance(); - } - - var loggingFile = Configuration.GetValue("logging:file"); - - if (!string.IsNullOrWhiteSpace(loggingFile)) - { - builder.RegisterInstance(new FileChannel(loggingFile)) - .As() - .As() - .SingleInstance(); - } - - builder.Register(c => new ApplicationInfoLogAppender(GetType(), Guid.NewGuid())) - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.Register(c => SystemClock.Instance) - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.Register(c => new InvalidatingMemoryCache(new MemoryCache(c.Resolve>()), c.Resolve())) - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .AsSelf() - .SingleInstance(); - } - } -} diff --git a/src/Squidex/Config/Domain/InfrastructureServices.cs b/src/Squidex/Config/Domain/InfrastructureServices.cs new file mode 100644 index 000000000..d35477ea5 --- /dev/null +++ b/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -0,0 +1,116 @@ +// ========================================================================== +// InfrastructureServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using NodaTime; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Actors; +using Squidex.Infrastructure.Assets; +using Squidex.Infrastructure.Assets.ImageSharp; +using Squidex.Infrastructure.Caching; +using Squidex.Infrastructure.CQRS.Commands; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.UsageTracking; +using Squidex.Pipeline; + +namespace Squidex.Config.Domain +{ + public static class InfrastructureServices + { + public static void AddMyInfrastructureServices(this IServiceCollection services, IConfiguration config) + { + if (config.GetValue("logging:human")) + { + services.AddSingleton(c => new Func(() => new JsonLogWriter(Formatting.Indented, true))); + } + else + { + services.AddSingleton(c => new Func(() => new JsonLogWriter())); + } + + var loggingFile = config.GetValue("logging:file"); + + if (!string.IsNullOrWhiteSpace(loggingFile)) + { + services.AddSingleton(new FileChannel(loggingFile)) + .As() + .As(); + } + + services.AddSingleton(c => new ApplicationInfoLogAppender(typeof(Startup).Assembly, Guid.NewGuid())) + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton(SystemClock.Instance) + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton(); + services.AddSingleton(); + } + } +} diff --git a/src/Squidex/Config/Domain/PubSubModule.cs b/src/Squidex/Config/Domain/PubSubModule.cs deleted file mode 100644 index 07b07ea43..000000000 --- a/src/Squidex/Config/Domain/PubSubModule.cs +++ /dev/null @@ -1,69 +0,0 @@ -// ========================================================================== -// PubSubModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Autofac; -using Autofac.Core; -using Microsoft.Extensions.Configuration; -using Squidex.Infrastructure; -using StackExchange.Redis; - -namespace Squidex.Config.Domain -{ - public sealed class PubSubModule : Module - { - private const string RedisRegistration = "PubSubRedis"; - - private IConfiguration Configuration { get; } - - public PubSubModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - var pubSubType = Configuration.GetValue("pubSub:type"); - - if (string.IsNullOrWhiteSpace(pubSubType)) - { - throw new ConfigurationException("Configure the PubSub type with 'pubSub:type'."); - } - - if (string.Equals(pubSubType, "Redis", StringComparison.OrdinalIgnoreCase)) - { - var configuration = Configuration.GetValue("pubsub:redis:configuration"); - - if (string.IsNullOrWhiteSpace(configuration)) - { - throw new ConfigurationException("Configure PubSub Redis configuration with 'pubSub:redis:configuration'."); - } - - builder.Register(c => Singletons.GetOrAddLazy(configuration, s => ConnectionMultiplexer.Connect(s))) - .Named>(RedisRegistration) - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed>(RedisRegistration)) - .As() - .As() - .SingleInstance(); - } - else if (string.Equals(pubSubType, "InMemory", StringComparison.OrdinalIgnoreCase)) - { - builder.RegisterType() - .As() - .SingleInstance(); - } - else - { - throw new ConfigurationException($"Unsupported value '{pubSubType}' for 'pubSub:type', supported: Redis, InMemory."); - } - } - } -} diff --git a/src/Squidex/Config/Domain/PubSubServices.cs b/src/Squidex/Config/Domain/PubSubServices.cs new file mode 100644 index 000000000..dc251043e --- /dev/null +++ b/src/Squidex/Config/Domain/PubSubServices.cs @@ -0,0 +1,41 @@ +// ========================================================================== +// PubSubServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Log; +using StackExchange.Redis; + +namespace Squidex.Config.Domain +{ + public static class PubSubServices + { + public static void AddMyPubSubServices(this IServiceCollection services, IConfiguration config) + { + config.ConfigureByOption("pubSub:type", new Options + { + ["InMemory"] = () => + { + services.AddSingleton() + .As(); + }, + ["Redis"] = () => + { + var configuration = config.GetRequiredValue("pubsub:redis:configuration"); + + var redis = Singletons.GetOrAddLazy(configuration, s => ConnectionMultiplexer.Connect(s)); + + services.AddSingleton(c => new RedisPubSub(redis, c.GetRequiredService())) + .As() + .As(); + } + }); + } + } +} diff --git a/src/Squidex/Config/Domain/ReadModule.cs b/src/Squidex/Config/Domain/ReadModule.cs deleted file mode 100644 index fca1204f7..000000000 --- a/src/Squidex/Config/Domain/ReadModule.cs +++ /dev/null @@ -1,139 +0,0 @@ -// ========================================================================== -// ReadModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Collections.Generic; -using System.Linq; -using Autofac; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; -using Squidex.Domain.Apps.Core.HandleRules; -using Squidex.Domain.Apps.Core.HandleRules.ActionHandlers; -using Squidex.Domain.Apps.Core.HandleRules.Triggers; -using Squidex.Domain.Apps.Read.Apps; -using Squidex.Domain.Apps.Read.Apps.Services; -using Squidex.Domain.Apps.Read.Apps.Services.Implementations; -using Squidex.Domain.Apps.Read.Contents; -using Squidex.Domain.Apps.Read.Contents.Edm; -using Squidex.Domain.Apps.Read.Contents.GraphQL; -using Squidex.Domain.Apps.Read.History; -using Squidex.Domain.Apps.Read.Rules; -using Squidex.Domain.Apps.Read.Schemas; -using Squidex.Domain.Apps.Read.Schemas.Services; -using Squidex.Domain.Apps.Read.Schemas.Services.Implementations; -using Squidex.Domain.Users; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Assets; -using Squidex.Infrastructure.CQRS.Events; -using Squidex.Pipeline; - -namespace Squidex.Config.Domain -{ - public sealed class ReadModule : Module - { - private IConfiguration Configuration { get; } - - public ReadModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - builder.Register(c => c.Resolve>().Value?.Plans ?? Enumerable.Empty()) - .As>() - .AsSelf() - .SingleInstance(); - - builder.Register(c => new GraphQLUrlGenerator( - c.Resolve>(), - c.Resolve(), - Configuration.GetValue("assetStore:exposeSourceUrl"))) - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .InstancePerDependency(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .InstancePerDependency(); - - builder.RegisterType() - .As() - .AsSelf() - .InstancePerDependency(); - - builder.RegisterType() - .As() - .AsSelf() - .InstancePerDependency(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .AsSelf() - .SingleInstance(); - } - } -} diff --git a/src/Squidex/Config/Domain/ReadServices.cs b/src/Squidex/Config/Domain/ReadServices.cs new file mode 100644 index 000000000..730a5b156 --- /dev/null +++ b/src/Squidex/Config/Domain/ReadServices.cs @@ -0,0 +1,114 @@ +// ========================================================================== +// ReadServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.HandleRules.ActionHandlers; +using Squidex.Domain.Apps.Core.HandleRules.Triggers; +using Squidex.Domain.Apps.Read.Apps; +using Squidex.Domain.Apps.Read.Apps.Services; +using Squidex.Domain.Apps.Read.Apps.Services.Implementations; +using Squidex.Domain.Apps.Read.Assets; +using Squidex.Domain.Apps.Read.Contents; +using Squidex.Domain.Apps.Read.Contents.Edm; +using Squidex.Domain.Apps.Read.Contents.GraphQL; +using Squidex.Domain.Apps.Read.History; +using Squidex.Domain.Apps.Read.Rules; +using Squidex.Domain.Apps.Read.Schemas; +using Squidex.Domain.Apps.Read.Schemas.Services; +using Squidex.Domain.Apps.Read.Schemas.Services.Implementations; +using Squidex.Domain.Users; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Assets; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Pipeline; + +namespace Squidex.Config.Domain +{ + public static class ReadServices + { + public static void AddMyReadServices(this IServiceCollection services, IConfiguration config) + { + var exposeSourceUrl = config.GetOptionalValue("assetStore:exposeSourceUrl", true); + + services.AddSingleton(c => new GraphQLUrlGenerator( + c.GetRequiredService>(), + c.GetRequiredService(), + exposeSourceUrl)) + .As(); + + services.AddSingleton(c => c.GetService>()?.Value?.Plans.OrEmpty()) + .As>(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton(c => new CompoundEventConsumer(c.GetServices().ToArray())) + .As(); + + services.AddSingleton(c => + new CompoundEventConsumer( + c.GetServices().OfType() + .Concat(c.GetRequiredService()).ToArray())) + .As(); + + services.AddSingleton(c => + new CompoundEventConsumer( + c.GetServices().OfType() + .Concat(c.GetRequiredService()) + .Concat(c.GetRequiredService()).ToArray())) + .As(); + + services.AddSingleton(); + services.AddSingleton(); + } + } +} diff --git a/src/Squidex/Config/Domain/Serializers.cs b/src/Squidex/Config/Domain/SerializationServices.cs similarity index 94% rename from src/Squidex/Config/Domain/Serializers.cs rename to src/Squidex/Config/Domain/SerializationServices.cs index 97c3d3139..aa9c9aa62 100644 --- a/src/Squidex/Config/Domain/Serializers.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -1,5 +1,5 @@ // ========================================================================== -// Serializers.cs +// SerializationServices.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -23,7 +23,7 @@ using Squidex.Infrastructure.MongoDb; namespace Squidex.Config.Domain { - public static class Serializers + public static class SerializationServices { private static readonly TypeNameRegistry TypeNameRegistry = new TypeNameRegistry(); private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings(); @@ -61,7 +61,7 @@ namespace Squidex.Config.Domain return settings; } - static Serializers() + static SerializationServices() { TypeNameRegistry.MapUnmapped(typeof(SquidexCoreModel).Assembly); TypeNameRegistry.MapUnmapped(typeof(SquidexEvents).Assembly); @@ -72,7 +72,7 @@ namespace Squidex.Config.Domain BsonJsonConvention.Register(JsonSerializer.Create(SerializerSettings)); } - public static IServiceCollection AddMyEventFormatter(this IServiceCollection services) + public static IServiceCollection AddMySerializers(this IServiceCollection services) { services.AddSingleton(t => TypeNameRegistry); services.AddSingleton(t => FieldRegistry); diff --git a/src/Squidex/Config/Domain/StoreModule.cs b/src/Squidex/Config/Domain/StoreModule.cs deleted file mode 100644 index 85a17d57d..000000000 --- a/src/Squidex/Config/Domain/StoreModule.cs +++ /dev/null @@ -1,44 +0,0 @@ -// ========================================================================== -// StoreModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Autofac; -using Microsoft.Extensions.Configuration; -using Squidex.Infrastructure; - -namespace Squidex.Config.Domain -{ - public class StoreModule : Module - { - private IConfiguration Configuration { get; } - - public StoreModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - var storeType = Configuration.GetValue("store:type"); - - if (string.IsNullOrWhiteSpace(storeType)) - { - throw new ConfigurationException("Configure the Store type with 'store:type'."); - } - - if (string.Equals(storeType, "MongoDB", StringComparison.OrdinalIgnoreCase)) - { - builder.RegisterModule(new StoreMongoDbModule(Configuration)); - } - else - { - throw new ConfigurationException($"Unsupported value '{storeType}' for 'stores:type', supported: MongoDb."); - } - } - } -} diff --git a/src/Squidex/Config/Domain/StoreMongoDbModule.cs b/src/Squidex/Config/Domain/StoreMongoDbModule.cs deleted file mode 100644 index faa0c355f..000000000 --- a/src/Squidex/Config/Domain/StoreMongoDbModule.cs +++ /dev/null @@ -1,208 +0,0 @@ -// ========================================================================== -// StoreMongoDbModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Autofac; -using Autofac.Core; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Configuration; -using MongoDB.Driver; -using Squidex.Domain.Apps.Read.Apps.Repositories; -using Squidex.Domain.Apps.Read.Apps.Services.Implementations; -using Squidex.Domain.Apps.Read.Assets.Repositories; -using Squidex.Domain.Apps.Read.Contents.GraphQL; -using Squidex.Domain.Apps.Read.Contents.Repositories; -using Squidex.Domain.Apps.Read.History.Repositories; -using Squidex.Domain.Apps.Read.MongoDb.Apps; -using Squidex.Domain.Apps.Read.MongoDb.Assets; -using Squidex.Domain.Apps.Read.MongoDb.Contents; -using Squidex.Domain.Apps.Read.MongoDb.History; -using Squidex.Domain.Apps.Read.MongoDb.Rules; -using Squidex.Domain.Apps.Read.MongoDb.Schemas; -using Squidex.Domain.Apps.Read.Rules.Repositories; -using Squidex.Domain.Apps.Read.Schemas.Repositories; -using Squidex.Domain.Apps.Read.Schemas.Services.Implementations; -using Squidex.Domain.Users; -using Squidex.Domain.Users.MongoDb; -using Squidex.Domain.Users.MongoDb.Infrastructure; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Events; -using Squidex.Infrastructure.UsageTracking; -using Squidex.Shared.Users; - -namespace Squidex.Config.Domain -{ - public class StoreMongoDbModule : Module - { - private const string MongoClientRegistration = "StoreMongoClient"; - private const string MongoDatabaseRegistration = "StoreMongoDatabaseName"; - private const string MongoContentDatabaseRegistration = "StoreMongoDatabaseNameContent"; - - private IConfiguration Configuration { get; } - - public StoreMongoDbModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - var configuration = Configuration.GetValue("store:mongoDb:configuration"); - - if (string.IsNullOrWhiteSpace(configuration)) - { - throw new ConfigurationException("Configure the Store MongoDb configuration with 'store:mongoDb:configuration'."); - } - - var database = Configuration.GetValue("store:mongoDb:database"); - - if (string.IsNullOrWhiteSpace(database)) - { - throw new ConfigurationException("Configure the Store MongoDb database with 'store:mongoDb:database'."); - } - - var contentDatabase = Configuration.GetValue("store:mongoDb:contentDatabase"); - - if (string.IsNullOrWhiteSpace(contentDatabase)) - { - contentDatabase = database; - } - - builder.Register(c => Singletons.GetOrAdd(configuration, s => new MongoClient(s))) - .Named(MongoClientRegistration) - .SingleInstance(); - - builder.Register(c => c.ResolveNamed(MongoClientRegistration).GetDatabase(database)) - .Named(MongoDatabaseRegistration) - .SingleInstance(); - - builder.Register(c => c.ResolveNamed(MongoClientRegistration).GetDatabase(contentDatabase)) - .Named(MongoContentDatabaseRegistration) - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As>() - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As>() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoContentDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) - .As() - .As() - .As() - .AsSelf() - .SingleInstance(); - - builder.Register(c => - new CompoundEventConsumer( - c.Resolve(), - c.Resolve())) - .As() - .AsSelf() - .SingleInstance(); - - builder.Register(c => - new CompoundEventConsumer( - c.Resolve(), - c.Resolve(), - c.Resolve())) - .As() - .AsSelf() - .SingleInstance(); - - builder.Register(c => - new CompoundEventConsumer( - c.Resolve(), - c.Resolve())) - .As() - .AsSelf() - .SingleInstance(); - } - } -} diff --git a/src/Squidex/Config/Domain/StoreServices.cs b/src/Squidex/Config/Domain/StoreServices.cs new file mode 100644 index 000000000..ada868799 --- /dev/null +++ b/src/Squidex/Config/Domain/StoreServices.cs @@ -0,0 +1,122 @@ +// ========================================================================== +// StoreServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Driver; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Read.Apps; +using Squidex.Domain.Apps.Read.Apps.Repositories; +using Squidex.Domain.Apps.Read.Assets; +using Squidex.Domain.Apps.Read.Assets.Repositories; +using Squidex.Domain.Apps.Read.Contents.Repositories; +using Squidex.Domain.Apps.Read.History; +using Squidex.Domain.Apps.Read.History.Repositories; +using Squidex.Domain.Apps.Read.MongoDb.Apps; +using Squidex.Domain.Apps.Read.MongoDb.Assets; +using Squidex.Domain.Apps.Read.MongoDb.Contents; +using Squidex.Domain.Apps.Read.MongoDb.History; +using Squidex.Domain.Apps.Read.MongoDb.Rules; +using Squidex.Domain.Apps.Read.MongoDb.Schemas; +using Squidex.Domain.Apps.Read.Rules.Repositories; +using Squidex.Domain.Apps.Read.Schemas; +using Squidex.Domain.Apps.Read.Schemas.Repositories; +using Squidex.Domain.Apps.Read.Schemas.Services; +using Squidex.Domain.Users; +using Squidex.Domain.Users.MongoDb; +using Squidex.Domain.Users.MongoDb.Infrastructure; +using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.UsageTracking; +using Squidex.Shared.Users; + +namespace Squidex.Config.Domain +{ + public static class StoreServices + { + public static void AddMyStoreServices(this IServiceCollection services, IConfiguration config) + { + config.ConfigureByOption("store:type", new Options + { + ["MongoDB"] = () => + { + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + var mongoContentDatabaseName = config.GetOptionalValue("store:mongoDb:contentDatabase", mongoDatabaseName); + + var mongoClient = Singletons.GetOrAdd(mongoConfiguration, s => new MongoClient(s)); + var mongoDatabase = mongoClient.GetDatabase(mongoDatabaseName); + var mongoContentDatabase = mongoClient.GetDatabase(mongoContentDatabaseName); + + services.AddSingleton(c => new MongoUserStore(mongoDatabase)) + .As>() + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoRoleStore(mongoDatabase)) + .As>() + .As() + .As(); + + services.AddSingleton(c => new MongoPersistedGrantStore(mongoDatabase)) + .As() + .As(); + + services.AddSingleton(c => new MongoUsageStore(mongoDatabase)) + .As() + .As(); + + services.AddSingleton(c => new MongoEventConsumerInfoRepository(mongoDatabase)) + .As() + .As(); + + services.AddSingleton(c => new MongoContentRepository(mongoContentDatabase, c.GetService())) + .As() + .As(); + + services.AddSingleton(c => new MongoRuleEventRepository(mongoDatabase)) + .As() + .As(); + + services.AddSingleton(c => new MongoHistoryEventRepository(mongoDatabase, c.GetServices())) + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoAppRepository(mongoDatabase)) + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoSchemaRepository(mongoDatabase, c.GetRequiredService())) + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoAssetStatsRepository(mongoDatabase)) + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoAssetRepository(mongoDatabase)) + .As() + .As() + .As(); + + services.AddSingleton(c => new MongoAssetRepository(mongoDatabase)) + .As() + .As() + .As(); + } + }); + } + } +} diff --git a/src/Squidex/Config/Domain/SystemExtensions.cs b/src/Squidex/Config/Domain/SystemExtensions.cs new file mode 100644 index 000000000..6e6e5ce01 --- /dev/null +++ b/src/Squidex/Config/Domain/SystemExtensions.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// SystemExtensions.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure; + +namespace Squidex.Config.Domain +{ + public static class SystemExtensions + { + public static IApplicationBuilder TestExternalSystems(this IApplicationBuilder app) + { + var systems = app.ApplicationServices.GetRequiredService>(); + + foreach (var system in systems) + { + system.Connect(); + } + + return app; + } + } +} diff --git a/src/Squidex/Config/Domain/WriteModule.cs b/src/Squidex/Config/Domain/WriteModule.cs deleted file mode 100644 index e6ce99535..000000000 --- a/src/Squidex/Config/Domain/WriteModule.cs +++ /dev/null @@ -1,113 +0,0 @@ -// ========================================================================== -// WriteModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Autofac; -using Microsoft.Extensions.Configuration; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Scripting; -using Squidex.Domain.Apps.Write.Apps; -using Squidex.Domain.Apps.Write.Assets; -using Squidex.Domain.Apps.Write.Contents; -using Squidex.Domain.Apps.Write.Rules; -using Squidex.Domain.Apps.Write.Schemas; -using Squidex.Domain.Users; -using Squidex.Infrastructure.CQRS.Commands; -using Squidex.Pipeline.CommandMiddlewares; - -namespace Squidex.Config.Domain -{ - public class WriteModule : Module - { - private IConfiguration Configuration { get; } - - public WriteModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.Register>(c => (id => new AppDomainObject(id, -1))) - .AsSelf() - .SingleInstance(); - - builder.Register>(c => (id => new AssetDomainObject(id, -1))) - .AsSelf() - .SingleInstance(); - - builder.Register>(c => (id => new ContentDomainObject(id, -1))) - .AsSelf() - .SingleInstance(); - - builder.Register>(c => (id => new RuleDomainObject(id, -1))) - .AsSelf() - .SingleInstance(); - - builder.Register>(c => - { - var fieldRegistry = c.Resolve(); - - return id => new SchemaDomainObject(id, -1, fieldRegistry); - }) - .AsSelf() - .SingleInstance(); - } - } -} diff --git a/src/Squidex/Config/Domain/WriteServices.cs b/src/Squidex/Config/Domain/WriteServices.cs new file mode 100644 index 000000000..6282d19ab --- /dev/null +++ b/src/Squidex/Config/Domain/WriteServices.cs @@ -0,0 +1,79 @@ +// ========================================================================== +// WriteServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Write.Apps; +using Squidex.Domain.Apps.Write.Assets; +using Squidex.Domain.Apps.Write.Contents; +using Squidex.Domain.Apps.Write.Rules; +using Squidex.Domain.Apps.Write.Schemas; +using Squidex.Domain.Users; +using Squidex.Infrastructure.CQRS.Commands; +using Squidex.Pipeline.CommandMiddlewares; + +namespace Squidex.Config.Domain +{ + public static class WriteServices + { + public static void AddMyWriteServices(this IServiceCollection services) + { + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton() + .As(); + + services.AddSingleton>(c => (id => new AppDomainObject(id, -1))); + services.AddSingleton>(c => (id => new RuleDomainObject(id, -1))); + services.AddSingleton>(c => (id => new AssetDomainObject(id, -1))); + services.AddSingleton>(c => (id => new ContentDomainObject(id, -1))); + + services.AddSingleton>(c => + { + var fieldRegistry = c.GetRequiredService(); + + return id => new SchemaDomainObject(id, -1, fieldRegistry); + }); + } + } +} diff --git a/src/Squidex/Config/Identity/AuthenticationUsage.cs b/src/Squidex/Config/Identity/AuthenticationExtensions.cs similarity index 87% rename from src/Squidex/Config/Identity/AuthenticationUsage.cs rename to src/Squidex/Config/Identity/AuthenticationExtensions.cs index b9dd4ad8f..69fe4f294 100644 --- a/src/Squidex/Config/Identity/AuthenticationUsage.cs +++ b/src/Squidex/Config/Identity/AuthenticationExtensions.cs @@ -1,5 +1,5 @@ // ========================================================================== -// AuthenticationUsage.cs +// AuthenticationExtensions.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Builder; namespace Squidex.Config.Identity { - public static class AuthenticationUsage + public static class AuthenticationExtensions { public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app) { diff --git a/src/Squidex/Config/Identity/AuthenticationServices.cs b/src/Squidex/Config/Identity/AuthenticationServices.cs index aeb2743b9..efd01bebc 100644 --- a/src/Squidex/Config/Identity/AuthenticationServices.cs +++ b/src/Squidex/Config/Identity/AuthenticationServices.cs @@ -16,14 +16,14 @@ namespace Squidex.Config.Identity { public static class AuthenticationServices { - public static IServiceCollection AddMyAuthentication(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddMyAuthentication(this IServiceCollection services, IConfiguration config) { - var identityOptions = configuration.GetSection("identity").Get(); + var identityOptions = config.GetSection("identity").Get(); services.AddAuthentication() .AddMyGoogleAuthentication(identityOptions) .AddMyMicrosoftAuthentication(identityOptions) - .AddMyApiProtection(identityOptions, configuration); + .AddMyApiProtection(identityOptions, config); return services; } diff --git a/src/Squidex/Config/Identity/IdentityUsage.cs b/src/Squidex/Config/Identity/IdentityExtensions.cs similarity index 97% rename from src/Squidex/Config/Identity/IdentityUsage.cs rename to src/Squidex/Config/Identity/IdentityExtensions.cs index 22db07e35..6bcee01ee 100644 --- a/src/Squidex/Config/Identity/IdentityUsage.cs +++ b/src/Squidex/Config/Identity/IdentityExtensions.cs @@ -1,5 +1,5 @@ // ========================================================================== -// IdentityUsage.cs +// IdentityExtensions.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -20,7 +20,7 @@ using Squidex.Shared.Users; namespace Squidex.Config.Identity { - public static class IdentityUsage + public static class IdentityExtensions { public static IApplicationBuilder UseMyIdentityServer(this IApplicationBuilder app) { diff --git a/src/Squidex/Config/Identity/IdentityServerServices.cs b/src/Squidex/Config/Identity/IdentityServerServices.cs new file mode 100644 index 000000000..2416df114 --- /dev/null +++ b/src/Squidex/Config/Identity/IdentityServerServices.cs @@ -0,0 +1,103 @@ +// ========================================================================== +// IdentityServerServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using IdentityModel; +using IdentityServer4.Models; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Users; +using Squidex.Shared.Identity; +using Squidex.Shared.Users; + +namespace Squidex.Config.Identity +{ + public static class IdentityServerServices + { + public static IServiceCollection AddMyIdentityServer(this IServiceCollection services) + { + X509Certificate2 certificate; + + var assembly = typeof(IdentityServices).GetTypeInfo().Assembly; + + using (var certStream = assembly.GetManifestResourceStream("Squidex.Config.Identity.Cert.IdentityCert.pfx")) + { + var certData = new byte[certStream.Length]; + + certStream.Read(certData, 0, certData.Length); + certificate = new X509Certificate2(certData, "password", + X509KeyStorageFlags.MachineKeySet | + X509KeyStorageFlags.PersistKeySet | + X509KeyStorageFlags.Exportable); + } + + services.AddSingleton( + GetApiResources()); + services.AddSingleton( + GetIdentityResources()); + services.AddSingleton, + UserClaimsPrincipalFactoryWithEmail>(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddIdentityServer(options => + { + options.UserInteraction.ErrorUrl = "/error/"; + }) + .AddAspNetIdentity() + .AddInMemoryApiResources(GetApiResources()) + .AddInMemoryIdentityResources(GetIdentityResources()) + .AddSigningCredential(certificate); + + return services; + } + + public static IServiceCollection AddMyIdentity(this IServiceCollection services) + { + services.AddIdentity() + .AddDefaultTokenProviders(); + + return services; + } + + private static IEnumerable GetApiResources() + { + yield return new ApiResource(Constants.ApiScope) + { + UserClaims = new List + { + JwtClaimTypes.Email, + JwtClaimTypes.Role + } + }; + } + + private static IEnumerable GetIdentityResources() + { + yield return new IdentityResources.OpenId(); + yield return new IdentityResources.Profile(); + yield return new IdentityResources.Email(); + yield return new IdentityResource(Constants.RoleScope, + new[] + { + JwtClaimTypes.Role + }); + yield return new IdentityResource(Constants.ProfileScope, + new[] + { + SquidexClaimTypes.SquidexDisplayName, + SquidexClaimTypes.SquidexPictureUrl + }); + } + } +} diff --git a/src/Squidex/Config/Identity/IdentityServices.cs b/src/Squidex/Config/Identity/IdentityServices.cs index cdd8421a2..52f941e15 100644 --- a/src/Squidex/Config/Identity/IdentityServices.cs +++ b/src/Squidex/Config/Identity/IdentityServices.cs @@ -6,22 +6,11 @@ // All rights reserved. // ========================================================================== -using System; -using System.Collections.Generic; using System.IO; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using IdentityModel; -using IdentityServer4.Models; -using IdentityServer4.Stores; using Microsoft.AspNetCore.DataProtection; -using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Squidex.Domain.Users; using Squidex.Infrastructure; -using Squidex.Shared.Identity; -using Squidex.Shared.Users; using StackExchange.Redis; namespace Squidex.Config.Identity @@ -32,121 +21,26 @@ namespace Squidex.Config.Identity { var dataProtection = services.AddDataProtection().SetApplicationName("Squidex"); - var keyStoreType = configuration.GetValue("identity:keysStore:type"); - - if (string.IsNullOrWhiteSpace(keyStoreType)) - { - throw new ConfigurationException("Configure KeyStore type with 'identity:keysStore:type'."); - } - - if (string.Equals(keyStoreType, "Redis", StringComparison.OrdinalIgnoreCase)) + configuration.ConfigureByOption("identity:keyStore:type", new Options { - var redisConfiguration = configuration.GetValue("identity:keysStore:redis:configuration"); - - if (string.IsNullOrWhiteSpace(redisConfiguration)) - { - throw new ConfigurationException("Configure KeyStore Redis configuration with 'identity:keysStore:redis:configuration'."); - } - - var connectionMultiplexer = Singletons.GetOrAdd(redisConfiguration, s => ConnectionMultiplexer.Connect(s)); - - dataProtection.PersistKeysToRedis(connectionMultiplexer); - } - else if (string.Equals(keyStoreType, "Folder", StringComparison.OrdinalIgnoreCase)) - { - var folderPath = configuration.GetValue("identity:keysStore:folder:path"); - - if (string.IsNullOrWhiteSpace(folderPath)) + ["Redis"] = () => { - throw new ConfigurationException("Configure KeyStore Folder path with 'identity:keysStore:folder:path'."); - } - - dataProtection.PersistKeysToFileSystem(new DirectoryInfo(folderPath)); - } - else if (!string.Equals(keyStoreType, "InMemory", StringComparison.OrdinalIgnoreCase)) - { - throw new ConfigurationException($"Unsupported value '{keyStoreType}' for 'identity:keysStore:type', supported: Redis, Folder, InMemory."); - } - - return services; - } - - public static IServiceCollection AddMyIdentityServer(this IServiceCollection services) - { - X509Certificate2 certificate; - - var assembly = typeof(IdentityServices).GetTypeInfo().Assembly; + var redisConfiguration = configuration.GetRequiredValue("identity:keysStore:redis:configuration"); - using (var certStream = assembly.GetManifestResourceStream("Squidex.Config.Identity.Cert.IdentityCert.pfx")) - { - var certData = new byte[certStream.Length]; - - certStream.Read(certData, 0, certData.Length); - certificate = new X509Certificate2(certData, "password", - X509KeyStorageFlags.MachineKeySet | - X509KeyStorageFlags.PersistKeySet | - X509KeyStorageFlags.Exportable); - } - - services.AddSingleton( - GetApiResources()); - services.AddSingleton( - GetIdentityResources()); - services.AddSingleton, - UserClaimsPrincipalFactoryWithEmail>(); - services.AddSingleton(); - services.AddSingleton(); + var connectionMultiplexer = Singletons.GetOrAdd(redisConfiguration, s => ConnectionMultiplexer.Connect(s)); - services.AddIdentityServer(options => + dataProtection.PersistKeysToRedis(connectionMultiplexer); + }, + ["Folder"] = () => { - options.UserInteraction.ErrorUrl = "/error/"; - }) - .AddAspNetIdentity() - .AddInMemoryApiResources(GetApiResources()) - .AddInMemoryIdentityResources(GetIdentityResources()) - .AddSigningCredential(certificate); + var folderPath = configuration.GetRequiredValue("identity:keysStore:folder:path"); - return services; - } - - public static IServiceCollection AddMyIdentity(this IServiceCollection services) - { - services.AddIdentity() - .AddDefaultTokenProviders(); + dataProtection.PersistKeysToFileSystem(new DirectoryInfo(folderPath)); + }, + ["InMemory"] = () => { } + }); return services; } - - private static IEnumerable GetApiResources() - { - yield return new ApiResource(Constants.ApiScope) - { - UserClaims = new List - { - JwtClaimTypes.Email, - JwtClaimTypes.Role - } - }; - } - - private static IEnumerable GetIdentityResources() - { - yield return new IdentityResources.OpenId(); - yield return new IdentityResources.Profile(); - yield return new IdentityResources.Email(); - yield return new IdentityResource(Constants.RoleScope, - new[] - { - JwtClaimTypes.Role - }); - yield return new IdentityResource(Constants.ProfileScope, - new[] - { - SquidexClaimTypes.SquidexDisplayName, - SquidexClaimTypes.SquidexPictureUrl - }); - } } } diff --git a/src/Squidex/Config/Options.cs b/src/Squidex/Config/Options.cs new file mode 100644 index 000000000..e1e5fb45b --- /dev/null +++ b/src/Squidex/Config/Options.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Options.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; + +namespace Squidex.Config +{ + public sealed class Options : Dictionary + { + public Options() + : base(StringComparer.OrdinalIgnoreCase) + { + } + } +} diff --git a/src/Squidex/Config/ServiceExtensions.cs b/src/Squidex/Config/ServiceExtensions.cs new file mode 100644 index 000000000..5c83223d5 --- /dev/null +++ b/src/Squidex/Config/ServiceExtensions.cs @@ -0,0 +1,94 @@ +// ========================================================================== +// ServiceExtensions.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure; + +namespace Squidex.Config +{ + public static class ServiceExtensions + { + public sealed class InterfaceRegistrator + { + private readonly IServiceCollection services; + + public InterfaceRegistrator(IServiceCollection services) + { + this.services = services; + } + + public InterfaceRegistrator As() + { + this.services.AddSingleton(typeof(TInterface), c => c.GetRequiredService()); + + return this; + } + } + + public static InterfaceRegistrator AddSingleton(this IServiceCollection services, Func factory) + { + services.AddSingleton(typeof(T), factory); + + return new InterfaceRegistrator(services); + } + + public static InterfaceRegistrator AddSingleton(this IServiceCollection services, T instance) + { + services.AddSingleton(instance); + + return new InterfaceRegistrator(services); + } + + public static InterfaceRegistrator AddSingleton(this IServiceCollection services) + { + services.AddSingleton(typeof(T)); + + return new InterfaceRegistrator(services); + } + + public static T GetOptionalValue(this IConfiguration config, string path, T defaultValue = default(T)) + { + var value = config.GetValue(path, defaultValue); + + return value; + } + + public static string GetRequiredValue(this IConfiguration config, string path) + { + var value = config.GetValue(path); + + if (string.IsNullOrWhiteSpace(value)) + { + var name = string.Join(' ', path.Split(':').Select(x => x.ToPascalCase())); + + throw new ConfigurationException($"Configure the {name} with '{path}'."); + } + + return value; + } + + public static string ConfigureByOption(this IConfiguration config, string path, Options options) + { + var value = config.GetRequiredValue(path); + + if (options.TryGetValue(value, out var action)) + { + action(); + } + else + { + throw new ConfigurationException($"Unsupported value '{value}' for '{path}', supported: {string.Join(' ', options.Keys)}."); + } + + return value; + } + } +} diff --git a/src/Squidex/Config/Swagger/SwaggerUsage.cs b/src/Squidex/Config/Swagger/SwaggerExtensions.cs similarity index 81% rename from src/Squidex/Config/Swagger/SwaggerUsage.cs rename to src/Squidex/Config/Swagger/SwaggerExtensions.cs index b0c1ecc07..c15976c81 100644 --- a/src/Squidex/Config/Swagger/SwaggerUsage.cs +++ b/src/Squidex/Config/Swagger/SwaggerExtensions.cs @@ -1,5 +1,5 @@ // ========================================================================== -// SwaggerUsage.cs +// SwaggerExtensions.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -13,13 +13,13 @@ using NSwag.AspNetCore; namespace Squidex.Config.Swagger { - public static class SwaggerUsage + public static class SwaggerExtensions { public static void UseMySwagger(this IApplicationBuilder app) { var settings = app.ApplicationServices.GetService(); - app.UseSwagger(typeof(SwaggerUsage).GetTypeInfo().Assembly, settings); + app.UseSwagger(typeof(SwaggerExtensions).GetTypeInfo().Assembly, settings); } } } diff --git a/src/Squidex/Config/Web/WebUsages.cs b/src/Squidex/Config/Web/WebExtensions.cs similarity index 98% rename from src/Squidex/Config/Web/WebUsages.cs rename to src/Squidex/Config/Web/WebExtensions.cs index 75357fe80..314360c5a 100644 --- a/src/Squidex/Config/Web/WebUsages.cs +++ b/src/Squidex/Config/Web/WebExtensions.cs @@ -15,7 +15,7 @@ using Squidex.Pipeline; namespace Squidex.Config.Web { - public static class WebUsages + public static class WebExtensions { public static void UseMyCors(this IApplicationBuilder app) { diff --git a/src/Squidex/Config/Web/WebModule.cs b/src/Squidex/Config/Web/WebModule.cs deleted file mode 100644 index 0b83f7e63..000000000 --- a/src/Squidex/Config/Web/WebModule.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ========================================================================== -// WebModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Autofac; -using Microsoft.Extensions.Configuration; -using Squidex.Pipeline; - -namespace Squidex.Config.Web -{ - public class WebModule : Module - { - private IConfiguration Configuration { get; } - - public WebModule(IConfiguration configuration) - { - Configuration = configuration; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType() - .AsSelf() - .SingleInstance(); - - builder.RegisterType() - .AsSelf() - .SingleInstance(); - } - } -} diff --git a/src/Squidex/Config/Web/WebDependencies.cs b/src/Squidex/Config/Web/WebServices.cs similarity index 71% rename from src/Squidex/Config/Web/WebDependencies.cs rename to src/Squidex/Config/Web/WebServices.cs index 019def3c5..bb1d994fd 100644 --- a/src/Squidex/Config/Web/WebDependencies.cs +++ b/src/Squidex/Config/Web/WebServices.cs @@ -8,14 +8,20 @@ using Microsoft.Extensions.DependencyInjection; using Squidex.Config.Domain; +using Squidex.Pipeline; namespace Squidex.Config.Web { - public static class WebDependencies + public static class WebServices { public static void AddMyMvc(this IServiceCollection services) { + services.AddSingleton(); + services.AddSingleton(); + services.AddMvc().AddMySerializers(); + services.AddCors(); + services.AddRouting(); } } } diff --git a/src/Squidex/Config/Web/WebpackUsages.cs b/src/Squidex/Config/Web/WebpackExtensions.cs similarity index 92% rename from src/Squidex/Config/Web/WebpackUsages.cs rename to src/Squidex/Config/Web/WebpackExtensions.cs index 01ff6278b..b33a7979d 100644 --- a/src/Squidex/Config/Web/WebpackUsages.cs +++ b/src/Squidex/Config/Web/WebpackExtensions.cs @@ -1,5 +1,5 @@ // ========================================================================== -// WebpackUsages.cs +// WebpackExtensions.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -11,7 +11,7 @@ using Squidex.Pipeline; namespace Squidex.Config.Web { - public static class WebpackUsages + public static class WebpackExtensions { public static IApplicationBuilder UseWebpackProxy(this IApplicationBuilder app) { diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index eff3313f2..37227c20d 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -48,8 +48,6 @@ - - diff --git a/src/Squidex/Startup.cs b/src/Squidex/Startup.cs index deb1601d0..7deeb29a8 100644 --- a/src/Squidex/Startup.cs +++ b/src/Squidex/Startup.cs @@ -6,11 +6,8 @@ // All rights reserved. // ========================================================================== -using System; using System.IO; using System.Linq; -using Autofac; -using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -50,21 +47,27 @@ namespace Squidex Configuration = config; } - public IServiceProvider ConfigureServices(IServiceCollection services) + public void ConfigureServices(IServiceCollection services) { - services.AddMySwaggerSettings(); - services.AddMyEventFormatter(); - services.AddMyDataProtectection(Configuration); + services.AddLogging(); + services.AddMemoryCache(); + services.AddOptions(); + + services.AddMyAssetServices(Configuration); services.AddMyAuthentication(Configuration); + services.AddMyDataProtectection(Configuration); + services.AddMyEventPublishersServices(Configuration); + services.AddMyEventStoreServices(Configuration); services.AddMyIdentity(); services.AddMyIdentityServer(); + services.AddMyInfrastructureServices(Configuration); services.AddMyMvc(); - - services.AddCors(); - services.AddLogging(); - services.AddMemoryCache(); - services.AddOptions(); - services.AddRouting(); + services.AddMyPubSubServices(Configuration); + services.AddMyReadServices(Configuration); + services.AddMySerializers(); + services.AddMyStoreServices(Configuration); + services.AddMySwaggerSettings(); + services.AddMyWriteServices(); services.Configure( Configuration.GetSection("urls")); @@ -74,27 +77,6 @@ namespace Squidex Configuration.GetSection("ui")); services.Configure( Configuration.GetSection("usage")); - - var builder = new ContainerBuilder(); - builder.Populate(services); - builder.RegisterModule(new AssetStoreModule(Configuration)); - builder.RegisterModule(new EventPublishersModule(Configuration)); - builder.RegisterModule(new EventStoreModule(Configuration)); - builder.RegisterModule(new InfrastructureModule(Configuration)); - builder.RegisterModule(new PubSubModule(Configuration)); - builder.RegisterModule(new ReadModule(Configuration)); - builder.RegisterModule(new StoreModule(Configuration)); - builder.RegisterModule(new WebModule(Configuration)); - builder.RegisterModule(new WriteModule(Configuration)); - - var container = builder.Build(); - - container.Resolve().ApplicationStopping.Register(() => - { - container.Dispose(); - }); - - return new AutofacServiceProvider(container); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) From 12330a45cb8c80b1d31b044898c07cb01767af36 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 12 Nov 2017 00:49:01 +0100 Subject: [PATCH 2/4] Bugfixes --- .../Config/Domain/InfrastructureServices.cs | 10 +++++++--- src/Squidex/Config/Domain/StoreServices.cs | 2 +- src/Squidex/Config/Identity/IdentityServices.cs | 2 +- src/Squidex/Config/ServiceExtensions.cs | 15 +++++++++------ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Squidex/Config/Domain/InfrastructureServices.cs b/src/Squidex/Config/Domain/InfrastructureServices.cs index d35477ea5..f6c8884d5 100644 --- a/src/Squidex/Config/Domain/InfrastructureServices.cs +++ b/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using NodaTime; using Squidex.Infrastructure; @@ -100,9 +101,6 @@ namespace Squidex.Config.Domain services.AddSingleton() .As(); - services.AddSingleton() - .As(); - services.AddSingleton() .As(); @@ -111,6 +109,12 @@ namespace Squidex.Config.Domain services.AddSingleton(); services.AddSingleton(); + + services.AddSingleton(c => new InvalidatingMemoryCache( + new MemoryCache( + c.GetRequiredService>()), + c.GetRequiredService())) + .As(); } } } diff --git a/src/Squidex/Config/Domain/StoreServices.cs b/src/Squidex/Config/Domain/StoreServices.cs index ada868799..46101f10c 100644 --- a/src/Squidex/Config/Domain/StoreServices.cs +++ b/src/Squidex/Config/Domain/StoreServices.cs @@ -111,7 +111,7 @@ namespace Squidex.Config.Domain .As() .As(); - services.AddSingleton(c => new MongoAssetRepository(mongoDatabase)) + services.AddSingleton(c => new MongoRuleRepository(mongoDatabase)) .As() .As() .As(); diff --git a/src/Squidex/Config/Identity/IdentityServices.cs b/src/Squidex/Config/Identity/IdentityServices.cs index 52f941e15..330af04b0 100644 --- a/src/Squidex/Config/Identity/IdentityServices.cs +++ b/src/Squidex/Config/Identity/IdentityServices.cs @@ -21,7 +21,7 @@ namespace Squidex.Config.Identity { var dataProtection = services.AddDataProtection().SetApplicationName("Squidex"); - configuration.ConfigureByOption("identity:keyStore:type", new Options + configuration.ConfigureByOption("identity:keysStore:type", new Options { ["Redis"] = () => { diff --git a/src/Squidex/Config/ServiceExtensions.cs b/src/Squidex/Config/ServiceExtensions.cs index 5c83223d5..7b4255849 100644 --- a/src/Squidex/Config/ServiceExtensions.cs +++ b/src/Squidex/Config/ServiceExtensions.cs @@ -27,29 +27,32 @@ namespace Squidex.Config public InterfaceRegistrator As() { - this.services.AddSingleton(typeof(TInterface), c => c.GetRequiredService()); + this.services.AddSingleton(typeof(TInterface), c => + { + return c.GetRequiredService(); + }); return this; } } - public static InterfaceRegistrator AddSingleton(this IServiceCollection services, Func factory) + public static InterfaceRegistrator AddSingleton(this IServiceCollection services, Func factory) where T : class { services.AddSingleton(typeof(T), factory); return new InterfaceRegistrator(services); } - public static InterfaceRegistrator AddSingleton(this IServiceCollection services, T instance) + public static InterfaceRegistrator AddSingleton(this IServiceCollection services, T instance) where T : class { - services.AddSingleton(instance); + services.AddSingleton(typeof(T), instance); return new InterfaceRegistrator(services); } - public static InterfaceRegistrator AddSingleton(this IServiceCollection services) + public static InterfaceRegistrator AddSingleton(this IServiceCollection services) where T : class { - services.AddSingleton(typeof(T)); + services.AddSingleton(); return new InterfaceRegistrator(services); } From d8351a6704b3e06cb1354e74ec49397c2fc5af10 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 12 Nov 2017 01:17:21 +0100 Subject: [PATCH 3/4] Code cleanup. --- .../Apps/MongoAppRepository.cs | 1 - .../Assets/MongoAssetRepository.cs | 1 - .../Assets/MongoAssetStatsRepository.cs | 1 - .../Schemas/MongoSchemaRepository.cs | 1 - .../CQRS/Events/CompoundEventConsumer.cs | 1 - .../SemanticLogLoggerFactoryExtensions.cs | 7 +- .../Log/Adapter/SemanticLogLoggerProvider.cs | 2 + src/Squidex/AppConfiguration.cs | 24 ++++++ src/Squidex/AppServices.cs | 53 ++++++++++++ .../Config/Domain/EventPublishersServices.cs | 6 +- .../Config/Identity/AuthenticationServices.cs | 4 +- .../Config/Identity/IdentityServices.cs | 8 +- src/Squidex/Program.cs | 19 +++-- src/Squidex/{Startup.cs => WebApp.cs} | 81 +++++-------------- 14 files changed, 122 insertions(+), 87 deletions(-) create mode 100644 src/Squidex/AppConfiguration.cs create mode 100644 src/Squidex/AppServices.cs rename src/Squidex/{Startup.cs => WebApp.cs} (58%) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs index 92050fb6d..f55b7e494 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks; using MongoDB.Driver; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps.Repositories; -using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Apps diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs index e00e6e34d..2caca0755 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs @@ -14,7 +14,6 @@ using MongoDB.Bson; using MongoDB.Driver; using Squidex.Domain.Apps.Read.Assets; using Squidex.Domain.Apps.Read.Assets.Repositories; -using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Assets diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs index 2ef42ad04..290c754e7 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs @@ -14,7 +14,6 @@ using MongoDB.Driver; using Squidex.Domain.Apps.Read.Assets; using Squidex.Domain.Apps.Read.Assets.Repositories; using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Assets diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs index c9c9fb5f8..f17434021 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs @@ -15,7 +15,6 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas.Repositories; using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Read.MongoDb.Schemas diff --git a/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs index d86bdf58a..463348dde 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerFactoryExtensions.cs b/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerFactoryExtensions.cs index 662da7078..fbb32bc50 100644 --- a/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerFactoryExtensions.cs +++ b/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerFactoryExtensions.cs @@ -6,17 +6,18 @@ // All rights reserved. // ========================================================================== +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Squidex.Infrastructure.Log.Adapter { public static class SemanticLogLoggerFactoryExtensions { - public static ILoggerFactory AddSemanticLog(this ILoggerFactory factory, ISemanticLog semanticLog) + public static ILoggingBuilder AddSemanticLog(this ILoggingBuilder builder) { - factory.AddProvider(new SemanticLogLoggerProvider(semanticLog)); + builder.Services.AddSingleton(); - return factory; + return builder; } } } diff --git a/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerProvider.cs b/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerProvider.cs index 3bd84a2b1..7e2ad2956 100644 --- a/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerProvider.cs +++ b/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerProvider.cs @@ -16,6 +16,8 @@ namespace Squidex.Infrastructure.Log.Adapter public SemanticLogLoggerProvider(ISemanticLog semanticLog) { + Guard.NotNull(semanticLog, nameof(semanticLog)); + this.semanticLog = semanticLog; } diff --git a/src/Squidex/AppConfiguration.cs b/src/Squidex/AppConfiguration.cs new file mode 100644 index 000000000..63579016c --- /dev/null +++ b/src/Squidex/AppConfiguration.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// AppConfiguration.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +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/AppServices.cs b/src/Squidex/AppServices.cs new file mode 100644 index 000000000..05d969b3f --- /dev/null +++ b/src/Squidex/AppServices.cs @@ -0,0 +1,53 @@ +// ========================================================================== +// Services.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Config; +using Squidex.Config.Domain; +using Squidex.Config.Identity; +using Squidex.Config.Swagger; +using Squidex.Config.Web; + +namespace Squidex +{ + public static class AppServices + { + public static void AddAppServices(this IServiceCollection services, IConfiguration config) + { + services.AddLogging(); + services.AddMemoryCache(); + services.AddOptions(); + + services.AddMyAssetServices(config); + services.AddMyAuthentication(config); + services.AddMyDataProtectection(config); + services.AddMyEventPublishersServices(config); + services.AddMyEventStoreServices(config); + services.AddMyIdentity(); + services.AddMyIdentityServer(); + services.AddMyInfrastructureServices(config); + services.AddMyMvc(); + services.AddMyPubSubServices(config); + services.AddMyReadServices(config); + services.AddMySerializers(); + services.AddMyStoreServices(config); + services.AddMySwaggerSettings(); + services.AddMyWriteServices(); + + services.Configure( + config.GetSection("urls")); + services.Configure( + config.GetSection("identity")); + services.Configure( + config.GetSection("ui")); + services.Configure( + config.GetSection("usage")); + } + } +} diff --git a/src/Squidex/Config/Domain/EventPublishersServices.cs b/src/Squidex/Config/Domain/EventPublishersServices.cs index 90b46821b..0d3edbacb 100644 --- a/src/Squidex/Config/Domain/EventPublishersServices.cs +++ b/src/Squidex/Config/Domain/EventPublishersServices.cs @@ -17,9 +17,9 @@ namespace Squidex.Config.Domain { public static class EventPublishersServices { - public static void AddMyEventPublishersServices(this IServiceCollection services, IConfiguration configuration) + public static void AddMyEventPublishersServices(this IServiceCollection services, IConfiguration config) { - var eventPublishers = configuration.GetSection("eventPublishers"); + var eventPublishers = config.GetSection("eventPublishers"); foreach (var child in eventPublishers.GetChildren()) { @@ -30,7 +30,7 @@ namespace Squidex.Config.Domain throw new ConfigurationException($"Configure EventPublisher type with 'eventPublishers:{child.Key}:type'."); } - var eventsFilter = configuration.GetValue("eventsFilter"); + var eventsFilter = config.GetValue("eventsFilter"); var enabled = child.GetValue("enabled"); diff --git a/src/Squidex/Config/Identity/AuthenticationServices.cs b/src/Squidex/Config/Identity/AuthenticationServices.cs index efd01bebc..156ef6072 100644 --- a/src/Squidex/Config/Identity/AuthenticationServices.cs +++ b/src/Squidex/Config/Identity/AuthenticationServices.cs @@ -28,11 +28,11 @@ namespace Squidex.Config.Identity return services; } - public static AuthenticationBuilder AddMyApiProtection(this AuthenticationBuilder authBuilder, MyIdentityOptions identityOptions, IConfiguration configuration) + public static AuthenticationBuilder AddMyApiProtection(this AuthenticationBuilder authBuilder, MyIdentityOptions identityOptions, IConfiguration config) { var apiScope = Constants.ApiScope; - var urlsOptions = configuration.GetSection("urls").Get(); + var urlsOptions = config.GetSection("urls").Get(); if (!string.IsNullOrWhiteSpace(urlsOptions.BaseUrl)) { diff --git a/src/Squidex/Config/Identity/IdentityServices.cs b/src/Squidex/Config/Identity/IdentityServices.cs index 330af04b0..206637c1c 100644 --- a/src/Squidex/Config/Identity/IdentityServices.cs +++ b/src/Squidex/Config/Identity/IdentityServices.cs @@ -17,15 +17,15 @@ namespace Squidex.Config.Identity { public static class IdentityServices { - public static IServiceCollection AddMyDataProtectection(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddMyDataProtectection(this IServiceCollection services, IConfiguration config) { var dataProtection = services.AddDataProtection().SetApplicationName("Squidex"); - configuration.ConfigureByOption("identity:keysStore:type", new Options + config.ConfigureByOption("identity:keysStore:type", new Options { ["Redis"] = () => { - var redisConfiguration = configuration.GetRequiredValue("identity:keysStore:redis:configuration"); + var redisConfiguration = config.GetRequiredValue("identity:keysStore:redis:configuration"); var connectionMultiplexer = Singletons.GetOrAdd(redisConfiguration, s => ConnectionMultiplexer.Connect(s)); @@ -33,7 +33,7 @@ namespace Squidex.Config.Identity }, ["Folder"] = () => { - var folderPath = configuration.GetRequiredValue("identity:keysStore:folder:path"); + var folderPath = config.GetRequiredValue("identity:keysStore:folder:path"); dataProtection.PersistKeysToFileSystem(new DirectoryInfo(folderPath)); }, diff --git a/src/Squidex/Program.cs b/src/Squidex/Program.cs index 35d74a6e0..f109dc475 100644 --- a/src/Squidex/Program.cs +++ b/src/Squidex/Program.cs @@ -8,7 +8,7 @@ using System.IO; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Squidex.Infrastructure.Log.Adapter; namespace Squidex { @@ -20,14 +20,17 @@ namespace Squidex .UseKestrel(k => { k.AddServerHeader = false; }) .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() - .UseStartup() - .ConfigureAppConfiguration((hostContext, options) => + .ConfigureLogging(builder => { - options.Sources.Clear(); - options.AddJsonFile("appsettings.json", true, true); - options.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true); - options.AddEnvironmentVariables(); - options.AddCommandLine(args); + builder.AddSemanticLog(); + }) + .ConfigureAppConfiguration((hostContext, builder) => + { + builder.AddAppConfiguration(hostContext.HostingEnvironment.EnvironmentName, args); + }) + .ConfigureServices((context, services) => + { + services.AddAppServices(context.Configuration); }) .Build() .Run(); diff --git a/src/Squidex/Startup.cs b/src/Squidex/WebApp.cs similarity index 58% rename from src/Squidex/Startup.cs rename to src/Squidex/WebApp.cs index 7deeb29a8..6b6a5f764 100644 --- a/src/Squidex/Startup.cs +++ b/src/Squidex/WebApp.cs @@ -1,5 +1,5 @@ // ========================================================================== -// Startup.cs +// WebApp.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -13,20 +13,18 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Squidex.Config; using Squidex.Config.Domain; using Squidex.Config.Identity; using Squidex.Config.Swagger; using Squidex.Config.Web; using Squidex.Infrastructure.Log; -using Squidex.Infrastructure.Log.Adapter; #pragma warning disable RECS0002 // Convert anonymous method to method group namespace Squidex { - public class Startup + public static class WebApp { private static readonly string[] IdentityServerPaths = { @@ -36,52 +34,9 @@ namespace Squidex "/error" }; - private IConfiguration Configuration { get; } - - private IHostingEnvironment Environment { get; } - - public Startup(IHostingEnvironment env, IConfiguration config) - { - Environment = env; - - Configuration = config; - } - - public void ConfigureServices(IServiceCollection services) - { - services.AddLogging(); - services.AddMemoryCache(); - services.AddOptions(); - - services.AddMyAssetServices(Configuration); - services.AddMyAuthentication(Configuration); - services.AddMyDataProtectection(Configuration); - services.AddMyEventPublishersServices(Configuration); - services.AddMyEventStoreServices(Configuration); - services.AddMyIdentity(); - services.AddMyIdentityServer(); - services.AddMyInfrastructureServices(Configuration); - services.AddMyMvc(); - services.AddMyPubSubServices(Configuration); - services.AddMyReadServices(Configuration); - services.AddMySerializers(); - services.AddMyStoreServices(Configuration); - services.AddMySwaggerSettings(); - services.AddMyWriteServices(); - - services.Configure( - Configuration.GetSection("urls")); - services.Configure( - Configuration.GetSection("identity")); - services.Configure( - Configuration.GetSection("ui")); - services.Configure( - Configuration.GetSection("usage")); - } - - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + public static void ConfigureApp(this IApplicationBuilder app) { - loggerFactory.AddSemanticLog(app.ApplicationServices.GetRequiredService()); + var env = app.ApplicationServices.GetRequiredService(); app.TestExternalSystems(); @@ -89,30 +44,32 @@ namespace Squidex app.UseMyForwardingRules(); app.UseMyTracking(); - MapAndUseIdentity(app); - MapAndUseApi(app); - MapAndUseFrontend(app); - - app.UseMyEventStore(); + app.MapAndUseIdentityServer(env); + app.MapAndUseApi(env); + app.MapAndUseFrontend(env); var log = app.ApplicationServices.GetRequiredService(); + var config = app.ApplicationServices.GetRequiredService(); + log.LogInformation(w => w .WriteProperty("message", "Application started") .WriteObject("environment", c => { - foreach (var kvp in Configuration.AsEnumerable().Where(kvp => kvp.Value != null)) + foreach (var kvp in config.AsEnumerable().Where(kvp => kvp.Value != null)) { c.WriteProperty(kvp.Key, kvp.Value); } })); + + app.UseMyEventStore(); } - private void MapAndUseIdentity(IApplicationBuilder app) + private static void MapAndUseIdentityServer(this IApplicationBuilder app, IHostingEnvironment env) { app.Map(Constants.IdentityPrefix, identityApp => { - if (Environment.IsDevelopment()) + if (env.IsDevelopment()) { identityApp.UseDeveloperExceptionPage(); } @@ -127,18 +84,18 @@ namespace Squidex identityApp.UseMyAdmin(); identityApp.UseStaticFiles(); - identityApp.MapWhen(x => IsIdentityRequest(x), mvcApp => + identityApp.MapWhen(IsIdentityRequest, mvcApp => { mvcApp.UseMvc(); }); }); } - private void MapAndUseApi(IApplicationBuilder app) + private static void MapAndUseApi(this IApplicationBuilder app, IHostingEnvironment env) { app.Map(Constants.ApiPrefix, appApi => { - if (Environment.IsDevelopment()) + if (env.IsDevelopment()) { appApi.UseDeveloperExceptionPage(); } @@ -152,9 +109,9 @@ namespace Squidex }); } - private void MapAndUseFrontend(IApplicationBuilder app) + private static void MapAndUseFrontend(this IApplicationBuilder app, IHostingEnvironment env) { - if (Environment.IsDevelopment()) + if (env.IsDevelopment()) { app.UseWebpackProxy(); From 5d830a5eb67a9bf946ad5ce761a1f242530618cd Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 12 Nov 2017 13:40:18 +0100 Subject: [PATCH 4/4] Bugfix and cleanup --- src/Squidex/AppConfiguration.cs | 3 + .../Config/Domain/EventStoreExtensions.cs | 10 +--- .../Config/Domain/InfrastructureServices.cs | 2 +- .../Config/Domain/LoggingExtensions.cs | 36 ++++++++++++ src/Squidex/Config/Domain/ReadServices.cs | 3 +- src/Squidex/Config/Domain/SystemExtensions.cs | 8 +-- src/Squidex/Config/ServiceExtensions.cs | 9 ++- src/Squidex/Program.cs | 5 +- src/Squidex/{WebApp.cs => WebStartup.cs} | 57 ++++++++++--------- 9 files changed, 83 insertions(+), 50 deletions(-) create mode 100644 src/Squidex/Config/Domain/LoggingExtensions.cs rename src/Squidex/{WebApp.cs => WebStartup.cs} (70%) diff --git a/src/Squidex/AppConfiguration.cs b/src/Squidex/AppConfiguration.cs index 63579016c..16a9fa09e 100644 --- a/src/Squidex/AppConfiguration.cs +++ b/src/Squidex/AppConfiguration.cs @@ -15,9 +15,12 @@ namespace Squidex 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/Config/Domain/EventStoreExtensions.cs b/src/Squidex/Config/Domain/EventStoreExtensions.cs index 0a925c656..88d0ac501 100644 --- a/src/Squidex/Config/Domain/EventStoreExtensions.cs +++ b/src/Squidex/Config/Domain/EventStoreExtensions.cs @@ -1,12 +1,12 @@ // ========================================================================== -// EventStoreUsages.cs +// EventStoreExtensions.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Builder; +using System; using Microsoft.Extensions.DependencyInjection; using Squidex.Infrastructure.Actors; using Squidex.Infrastructure.CQRS.Events; @@ -16,10 +16,8 @@ namespace Squidex.Config.Domain { public static class EventStoreExtensions { - public static IApplicationBuilder UseMyEventStore(this IApplicationBuilder app) + public static void UseMyEventStore(this IServiceProvider services) { - var services = app.ApplicationServices; - services.GetService().CleanAsync().Wait(); var consumers = services.GetServices(); @@ -35,8 +33,6 @@ namespace Squidex.Config.Domain services.GetService().Connect(consumer.Name, actor); } } - - return app; } } } diff --git a/src/Squidex/Config/Domain/InfrastructureServices.cs b/src/Squidex/Config/Domain/InfrastructureServices.cs index f6c8884d5..a2c33d9ec 100644 --- a/src/Squidex/Config/Domain/InfrastructureServices.cs +++ b/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -50,7 +50,7 @@ namespace Squidex.Config.Domain .As(); } - services.AddSingleton(c => new ApplicationInfoLogAppender(typeof(Startup).Assembly, Guid.NewGuid())) + services.AddSingleton(c => new ApplicationInfoLogAppender(typeof(Program).Assembly, Guid.NewGuid())) .As(); services.AddSingleton() diff --git a/src/Squidex/Config/Domain/LoggingExtensions.cs b/src/Squidex/Config/Domain/LoggingExtensions.cs new file mode 100644 index 000000000..6571256e5 --- /dev/null +++ b/src/Squidex/Config/Domain/LoggingExtensions.cs @@ -0,0 +1,36 @@ +// ========================================================================== +// LoggingExtensions.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure.Log; + +namespace Squidex.Config.Domain +{ + public static class LoggingExtensions + { + public static void LogConfiguration(this IServiceProvider services) + { + var log = services.GetRequiredService(); + + var config = services.GetRequiredService(); + + log.LogInformation(w => w + .WriteProperty("message", "Application started") + .WriteObject("environment", c => + { + foreach (var kvp in config.AsEnumerable().Where(kvp => kvp.Value != null)) + { + c.WriteProperty(kvp.Key, kvp.Value); + } + })); + } + } +} diff --git a/src/Squidex/Config/Domain/ReadServices.cs b/src/Squidex/Config/Domain/ReadServices.cs index 730a5b156..a0d908423 100644 --- a/src/Squidex/Config/Domain/ReadServices.cs +++ b/src/Squidex/Config/Domain/ReadServices.cs @@ -46,8 +46,7 @@ namespace Squidex.Config.Domain exposeSourceUrl)) .As(); - services.AddSingleton(c => c.GetService>()?.Value?.Plans.OrEmpty()) - .As>(); + services.AddSingleton(c => c.GetService>()?.Value?.Plans.OrEmpty()); services.AddSingleton() .As(); diff --git a/src/Squidex/Config/Domain/SystemExtensions.cs b/src/Squidex/Config/Domain/SystemExtensions.cs index 6e6e5ce01..ed8a4bccd 100644 --- a/src/Squidex/Config/Domain/SystemExtensions.cs +++ b/src/Squidex/Config/Domain/SystemExtensions.cs @@ -6,8 +6,8 @@ // All rights reserved. // ========================================================================== +using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Squidex.Infrastructure; @@ -15,16 +15,14 @@ namespace Squidex.Config.Domain { public static class SystemExtensions { - public static IApplicationBuilder TestExternalSystems(this IApplicationBuilder app) + public static void TestExternalSystems(this IServiceProvider services) { - var systems = app.ApplicationServices.GetRequiredService>(); + var systems = services.GetRequiredService>(); foreach (var system in systems) { system.Connect(); } - - return app; } } } diff --git a/src/Squidex/Config/ServiceExtensions.cs b/src/Squidex/Config/ServiceExtensions.cs index 7b4255849..d669bb64a 100644 --- a/src/Squidex/Config/ServiceExtensions.cs +++ b/src/Squidex/Config/ServiceExtensions.cs @@ -27,10 +27,13 @@ namespace Squidex.Config public InterfaceRegistrator As() { - this.services.AddSingleton(typeof(TInterface), c => + if (typeof(TInterface) != typeof(T)) { - return c.GetRequiredService(); - }); + this.services.AddSingleton(typeof(TInterface), c => + { + return c.GetRequiredService(); + }); + } return this; } diff --git a/src/Squidex/Program.cs b/src/Squidex/Program.cs index f109dc475..2cde15aa0 100644 --- a/src/Squidex/Program.cs +++ b/src/Squidex/Program.cs @@ -20,6 +20,7 @@ namespace Squidex .UseKestrel(k => { k.AddServerHeader = false; }) .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() + .UseStartup() .ConfigureLogging(builder => { builder.AddSemanticLog(); @@ -28,10 +29,6 @@ namespace Squidex { builder.AddAppConfiguration(hostContext.HostingEnvironment.EnvironmentName, args); }) - .ConfigureServices((context, services) => - { - services.AddAppServices(context.Configuration); - }) .Build() .Run(); } diff --git a/src/Squidex/WebApp.cs b/src/Squidex/WebStartup.cs similarity index 70% rename from src/Squidex/WebApp.cs rename to src/Squidex/WebStartup.cs index 6b6a5f764..47195309c 100644 --- a/src/Squidex/WebApp.cs +++ b/src/Squidex/WebStartup.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System; using System.IO; using System.Linq; using Microsoft.AspNetCore.Builder; @@ -24,8 +25,10 @@ using Squidex.Infrastructure.Log; namespace Squidex { - public static class WebApp + public class WebStartup : IStartup { + private readonly IConfiguration configuration; + private readonly IHostingEnvironment environment; private static readonly string[] IdentityServerPaths = { "/client-callback-popup", @@ -34,42 +37,40 @@ namespace Squidex "/error" }; - public static void ConfigureApp(this IApplicationBuilder app) + public WebStartup(IConfiguration configuration, IHostingEnvironment environment) { - var env = app.ApplicationServices.GetRequiredService(); + this.configuration = configuration; + this.environment = environment; + } + + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddAppServices(configuration); - app.TestExternalSystems(); + return services.BuildServiceProvider(); + } + + public void Configure(IApplicationBuilder app) + { + app.ApplicationServices.LogConfiguration(); + app.ApplicationServices.TestExternalSystems(); app.UseMyCors(); app.UseMyForwardingRules(); app.UseMyTracking(); - app.MapAndUseIdentityServer(env); - app.MapAndUseApi(env); - app.MapAndUseFrontend(env); - - var log = app.ApplicationServices.GetRequiredService(); - - var config = app.ApplicationServices.GetRequiredService(); - - log.LogInformation(w => w - .WriteProperty("message", "Application started") - .WriteObject("environment", c => - { - foreach (var kvp in config.AsEnumerable().Where(kvp => kvp.Value != null)) - { - c.WriteProperty(kvp.Key, kvp.Value); - } - })); + MapAndUseIdentityServer(app); + MapAndUseApi(app); + MapAndUseFrontend(app); - app.UseMyEventStore(); + app.ApplicationServices.UseMyEventStore(); } - private static void MapAndUseIdentityServer(this IApplicationBuilder app, IHostingEnvironment env) + private void MapAndUseIdentityServer(IApplicationBuilder app) { app.Map(Constants.IdentityPrefix, identityApp => { - if (env.IsDevelopment()) + if (environment.IsDevelopment()) { identityApp.UseDeveloperExceptionPage(); } @@ -91,11 +92,11 @@ namespace Squidex }); } - private static void MapAndUseApi(this IApplicationBuilder app, IHostingEnvironment env) + private void MapAndUseApi(IApplicationBuilder app) { app.Map(Constants.ApiPrefix, appApi => { - if (env.IsDevelopment()) + if (environment.IsDevelopment()) { appApi.UseDeveloperExceptionPage(); } @@ -109,9 +110,9 @@ namespace Squidex }); } - private static void MapAndUseFrontend(this IApplicationBuilder app, IHostingEnvironment env) + private void MapAndUseFrontend(IApplicationBuilder app) { - if (env.IsDevelopment()) + if (environment.IsDevelopment()) { app.UseWebpackProxy();