diff --git a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs index 1e5c1084c..c5da46700 100644 --- a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using NodaTime; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure.EventSourcing; diff --git a/src/Squidex/AppServices.cs b/src/Squidex/AppServices.cs deleted file mode 100644 index c880e9e1f..000000000 --- a/src/Squidex/AppServices.cs +++ /dev/null @@ -1,79 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Squidex.Areas.Api.Config.Swagger; -using Squidex.Areas.Api.Controllers.Contents; -using Squidex.Areas.IdentityServer.Config; -using Squidex.Config; -using Squidex.Config.Authentication; -using Squidex.Config.Domain; -using Squidex.Config.Web; -using Squidex.Domain.Apps.Entities.Assets; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Extensions.Actions.Twitter; -using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Diagnostics; -using Squidex.Pipeline; -using Squidex.Pipeline.Robots; - -namespace Squidex -{ - public static class AppServices - { - public static void AddAppServices(this IServiceCollection services, IConfiguration config) - { - services.AddHttpClient(); - services.AddLogging(); - services.AddMemoryCache(); - services.AddOptions(); - - services.AddMyAssetServices(config); - services.AddMyAuthentication(config); - services.AddMyEntitiesServices(config); - services.AddMyEventPublishersServices(config); - services.AddMyEventStoreServices(config); - services.AddMyIdentityServer(); - services.AddMyInfrastructureServices(); - services.AddMyLoggingServices(config); - services.AddMyMigrationServices(); - services.AddMyMvc(); - services.AddMyRuleServices(); - services.AddMySerializers(); - services.AddMyStoreServices(config); - services.AddMySwaggerSettings(); - services.AddMySubscriptionServices(config); - - services.Configure( - config.GetSection("contents")); - services.Configure( - config.GetSection("assets")); - services.Configure( - config.GetSection("mode")); - services.Configure( - config.GetSection("twitter")); - services.Configure( - config.GetSection("robots")); - services.Configure( - config.GetSection("healthz:gc")); - services.Configure( - config.GetSection("etags")); - - services.Configure( - config.GetSection("contentsController")); - 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/EventStoreServices.cs b/src/Squidex/Config/Domain/EventStoreServices.cs index e203383e1..defcbc2c7 100644 --- a/src/Squidex/Config/Domain/EventStoreServices.cs +++ b/src/Squidex/Config/Domain/EventStoreServices.cs @@ -37,7 +37,6 @@ namespace Squidex.Config.Domain return new MongoEventStore(mongDatabase, c.GetRequiredService()); }) - .As() .As(); }, ["GetEventStore"] = () => diff --git a/src/Squidex/Config/Domain/LoggingServices.cs b/src/Squidex/Config/Domain/LoggingServices.cs index 36af59330..5e18bd491 100644 --- a/src/Squidex/Config/Domain/LoggingServices.cs +++ b/src/Squidex/Config/Domain/LoggingServices.cs @@ -12,15 +12,10 @@ using Squidex.Domain.Apps.Entities.Apps; using Squidex.Infrastructure.Log; using Squidex.Pipeline; -#pragma warning disable RECS0092 // Convert field to readonly - namespace Squidex.Config.Domain { public static class LoggingServices { - private static ILogChannel console = new ConsoleLogChannel(); - private static ILogChannel file; - public static void AddMyLoggingServices(this IServiceCollection services, IConfiguration config) { if (config.GetValue("logging:human")) @@ -38,18 +33,13 @@ namespace Squidex.Config.Domain if (!string.IsNullOrWhiteSpace(loggingFile)) { - services.AddSingletonAs(file ?? (file = new FileChannel(loggingFile))) + services.AddSingletonAs(new FileChannel(loggingFile)) .As(); } var useColors = config.GetValue("logging:colors"); - if (console == null) - { - console = new ConsoleLogChannel(useColors); - } - - services.AddSingletonAs(console) + services.AddSingletonAs(new ConsoleLogChannel(useColors)) .As(); services.AddSingletonAs(c => new ApplicationInfoLogAppender(typeof(Program).Assembly, Guid.NewGuid())) diff --git a/src/Squidex/Config/Domain/SystemExtensions.cs b/src/Squidex/Config/Domain/SystemExtensions.cs deleted file mode 100644 index c11bd99fd..000000000 --- a/src/Squidex/Config/Domain/SystemExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Log; -using Squidex.Infrastructure.Migrations; - -namespace Squidex.Config.Domain -{ - public static class SystemExtensions - { - public sealed class InitializeHostedService : IHostedService - { - private readonly IEnumerable targets; - private readonly IApplicationLifetime lifetime; - private readonly ISemanticLog log; - - public InitializeHostedService(IEnumerable targets, IApplicationLifetime lifetime, ISemanticLog log) - { - this.targets = targets; - this.lifetime = lifetime; - this.log = log; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - try - { - foreach (var target in targets) - { - await target.InitializeAsync(cancellationToken); - - log.LogInformation(w => w.WriteProperty("initializedSystem", target.GetType().Name)); - } - } - catch - { - lifetime.StopApplication(); - throw; - } - } - - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - } - - public sealed class MigratorHostedService : IHostedService - { - private readonly IApplicationLifetime lifetime; - private readonly Migrator migrator; - - public MigratorHostedService(IApplicationLifetime lifetime, Migrator migrator) - { - this.lifetime = lifetime; - this.migrator = migrator; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - try - { - await migrator.MigrateAsync(); - } - catch - { - lifetime.StopApplication(); - throw; - } - } - - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - } - } -} diff --git a/src/Squidex/Config/Orleans/Extensions.cs b/src/Squidex/Config/Orleans/Extensions.cs index 7f9a14a8e..4ecbd022f 100644 --- a/src/Squidex/Config/Orleans/Extensions.cs +++ b/src/Squidex/Config/Orleans/Extensions.cs @@ -5,9 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Orleans; using Orleans.ApplicationParts; using Orleans.Configuration; +using Orleans.Hosting; +using OrleansDashboard; +using OrleansDashboard.Client; +using OrleansDashboard.Metrics; using Squidex.Domain.Apps.Entities; using Squidex.Infrastructure; @@ -26,5 +31,27 @@ namespace Squidex.Config.Orleans options.ClusterId = Constants.OrleansClusterId; options.ServiceId = Constants.OrleansClusterId; } + + public static ISiloHostBuilder UseDashboardEx(this ISiloHostBuilder builder, Action configurator = null) + { + builder.AddStartupTask(); + + builder.ConfigureApplicationParts(appParts => + appParts + .AddFrameworkPart(typeof(Dashboard).Assembly) + .AddFrameworkPart(typeof(DashboardClient).Assembly)); + + builder.ConfigureServices(services => + { + services.AddDashboard(options => + { + options.HostSelf = false; + }); + }); + + builder.AddIncomingGrainCallFilter(); + + return builder; + } } } diff --git a/src/Squidex/Config/Orleans/OrleansServices.cs b/src/Squidex/Config/Orleans/OrleansServices.cs index cb32bced3..352611909 100644 --- a/src/Squidex/Config/Orleans/OrleansServices.cs +++ b/src/Squidex/Config/Orleans/OrleansServices.cs @@ -1,34 +1,113 @@ // ========================================================================== // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) +// Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Net; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Orleans; +using Orleans.Configuration; +using Orleans.Hosting; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.UsageTracking; +using Squidex.Infrastructure.EventSourcing.Grains; +using Squidex.Infrastructure.Orleans; namespace Squidex.Config.Orleans { public static class OrleansServices { - public static void AddOrleansSilo(this IServiceCollection services) + public static IServiceProvider AddAndBuildOrleans(this IServiceCollection services, IConfiguration config) { - services.AddSingletonAs() - .As() - .AsSelf(); + services.Configure(options => + { + options.Configure(); + }); + + services.Configure(options => + { + options.FastKillOnProcessExit = false; + }); services.AddServicesForSelfHostedDashboard(null, options => { options.HideTrace = true; }); - services.AddSingletonAs(c => c.GetRequiredService()) - .As(); + services.AddHostedService(); + + var hostBuilder = new SiloHostBuilder() + .UseDashboardEx() + .EnableDirectClient() + .AddIncomingGrainCallFilter() + .AddStartupTask>() + .AddStartupTask>() + .AddStartupTask>() + .AddStartupTask>() + .ConfigureApplicationParts(builder => + { + builder.AddMyParts(); + }); + + config.ConfigureByOption("orleans:clustering", new Options + { + ["MongoDB"] = () => + { + hostBuilder.ConfigureEndpoints(Dns.GetHostName(), 11111, 40000, listenOnAnyHostAddress: true); + + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + + hostBuilder.UseMongoDBClustering(options => + { + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; + }); + }, + ["Development"] = () => + { + hostBuilder.UseLocalhostClustering(gatewayPort: 40000, serviceId: Constants.OrleansClusterId, clusterId: Constants.OrleansClusterId); + hostBuilder.Configure(options => options.ExpectedClusterSize = 1); + } + }); + + config.ConfigureByOption("store:type", new Options + { + ["MongoDB"] = () => + { + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + + hostBuilder.UseMongoDBReminders(options => + { + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; + }); + } + }); + + IServiceProvider provider = null; + + hostBuilder.UseServiceProviderFactory((siloServices) => + { + foreach (var descriptor in services) + { + siloServices.Add(descriptor); + } + + provider = siloServices.BuildServiceProvider(); + + return provider; + }).Build(); - services.AddSingletonAs(c => c.GetRequiredService().Client) - .As(); + return provider; } } } diff --git a/src/Squidex/Config/Orleans/SiloHost.cs b/src/Squidex/Config/Orleans/SiloHost.cs new file mode 100644 index 000000000..1817dfa12 --- /dev/null +++ b/src/Squidex/Config/Orleans/SiloHost.cs @@ -0,0 +1,51 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Orleans.Hosting; +using Squidex.Config.Startup; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Log; + +namespace Squidex.Config.Orleans +{ + public sealed class SiloHost : SafeHostedService + { + private readonly ISiloHost silo; + + public SiloHost(ISiloHost silo, ISemanticLog log, IApplicationLifetime lifetime) + : base(lifetime, log) + { + this.silo = silo; + } + + protected override async Task StartAsync(ISemanticLog log, CancellationToken ct) + { + var watch = ValueStopwatch.StartNew(); + try + { + await silo.StartAsync(ct); + } + finally + { + var elapsedMs = watch.Stop(); + + log.LogInformation(w => w + .WriteProperty("message", "Silo started") + .WriteProperty("elapsedMs", elapsedMs)); + } + } + + protected override async Task StopAsync(ISemanticLog log, CancellationToken ct) + { + await silo.StopAsync(); + } + } +} diff --git a/src/Squidex/Config/Orleans/SiloServices.cs b/src/Squidex/Config/Orleans/SiloServices.cs deleted file mode 100644 index 865302883..000000000 --- a/src/Squidex/Config/Orleans/SiloServices.cs +++ /dev/null @@ -1,42 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Orleans.Hosting; - -namespace Squidex.Config.Orleans -{ - public static class SiloServices - { - public static void AddAppSiloServices(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"); - - services.AddMongoDBMembershipTable(options => - { - options.ConnectionString = mongoConfiguration; - options.CollectionPrefix = "Orleans_"; - options.DatabaseName = mongoDatabaseName; - }); - - services.AddMongoDBReminders(options => - { - options.ConnectionString = mongoConfiguration; - options.CollectionPrefix = "Orleans_"; - options.DatabaseName = mongoDatabaseName; - }); - } - }); - } - } -} \ No newline at end of file diff --git a/src/Squidex/Config/Orleans/SiloWrapper.cs b/src/Squidex/Config/Orleans/SiloWrapper.cs deleted file mode 100644 index 55f8b6d11..000000000 --- a/src/Squidex/Config/Orleans/SiloWrapper.cs +++ /dev/null @@ -1,190 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Orleans; -using Orleans.Configuration; -using Orleans.Hosting; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Rules; -using Squidex.Domain.Apps.Entities.Rules.UsageTracking; -using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing.Grains; -using Squidex.Infrastructure.Log; -using Squidex.Infrastructure.Log.Adapter; -using Squidex.Infrastructure.Orleans; - -namespace Squidex.Config.Orleans -{ - public sealed class SiloWrapper : IHostedService - { - private readonly Lazy lazySilo; - private readonly ISemanticLog log; - private readonly IApplicationLifetime lifetime; - private bool isStopping; - - internal sealed class Source : IConfigurationSource - { - private readonly IConfigurationProvider configurationProvider; - - public Source(IConfigurationProvider configurationProvider) - { - this.configurationProvider = configurationProvider; - } - - public IConfigurationProvider Build(IConfigurationBuilder builder) - { - return configurationProvider; - } - } - - public IClusterClient Client - { - get { return lazySilo.Value.Services.GetRequiredService(); } - } - - public SiloWrapper(IConfiguration config, ISemanticLog log, IApplicationLifetime lifetime) - { - this.lifetime = lifetime; - this.log = log; - - lazySilo = new Lazy(() => - { - var hostBuilder = new SiloHostBuilder() - .UseDashboard(options => options.HostSelf = false) - .EnableDirectClient() - .AddIncomingGrainCallFilter() - .AddStartupTask() - .AddStartupTask>() - .AddStartupTask>() - .AddStartupTask>() - .AddStartupTask>() - .Configure(options => - { - options.Configure(); - }) - .ConfigureApplicationParts(builder => - { - builder.AddMyParts(); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("logging")); - builder.AddSemanticLog(); - builder.AddFilter(); - }) - .ConfigureServices((context, services) => - { - services.AddAppSiloServices(context.Configuration); - services.AddAppServices(context.Configuration); - - services.Configure(options => options.FastKillOnProcessExit = false); - }) - .ConfigureAppConfiguration((hostContext, builder) => - { - if (config is IConfigurationRoot root) - { - foreach (var provider in root.Providers) - { - builder.Add(new Source(provider)); - } - } - }); - - config.ConfigureByOption("orleans:clustering", new Options - { - ["MongoDB"] = () => - { - hostBuilder.ConfigureEndpoints(Dns.GetHostName(), 11111, 40000, listenOnAnyHostAddress: true); - - var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); - var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); - - hostBuilder.UseMongoDBClustering(options => - { - options.ConnectionString = mongoConfiguration; - options.CollectionPrefix = "Orleans_"; - options.DatabaseName = mongoDatabaseName; - }); - }, - ["Development"] = () => - { - hostBuilder.UseLocalhostClustering(gatewayPort: 40000, serviceId: Constants.OrleansClusterId, clusterId: Constants.OrleansClusterId); - hostBuilder.Configure(options => options.ExpectedClusterSize = 1); - } - }); - - config.ConfigureByOption("store:type", new Options - { - ["MongoDB"] = () => - { - var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); - var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); - - hostBuilder.UseMongoDBReminders(options => - { - options.ConnectionString = mongoConfiguration; - options.CollectionPrefix = "Orleans_"; - options.DatabaseName = mongoDatabaseName; - }); - } - }); - - var silo = hostBuilder.Build(); - - silo.Stopped.ContinueWith(x => - { - if (!isStopping) - { - lifetime.StopApplication(); - } - }); - - return silo; - }); - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - var watch = ValueStopwatch.StartNew(); - try - { - await lazySilo.Value.StartAsync(cancellationToken); - } - catch - { - lifetime.StopApplication(); - throw; - } - finally - { - var elapsedMs = watch.Stop(); - - log.LogInformation(w => w - .WriteProperty("message", "Silo started") - .WriteProperty("elapsedMs", elapsedMs)); - } - } - - public async Task StopAsync(CancellationToken cancellationToken) - { - if (lazySilo.IsValueCreated) - { - isStopping = true; - - await lazySilo.Value.StopAsync(cancellationToken); - } - } - } -} diff --git a/src/Squidex/Config/Startup/InitializerHost.cs b/src/Squidex/Config/Startup/InitializerHost.cs new file mode 100644 index 000000000..9d8e2790a --- /dev/null +++ b/src/Squidex/Config/Startup/InitializerHost.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Log; + +namespace Squidex.Config.Startup +{ + public sealed class InitializerHost : SafeHostedService + { + private readonly IEnumerable targets; + + public InitializerHost(IEnumerable targets, IApplicationLifetime lifetime, ISemanticLog log) + : base(lifetime, log) + { + this.targets = targets; + } + + protected override async Task StartAsync(ISemanticLog log, CancellationToken ct) + { + foreach (var target in targets.Distinct()) + { + await target.InitializeAsync(ct); + + log.LogInformation(w => w.WriteProperty("initializedSystem", target.GetType().Name)); + } + } + } +} diff --git a/src/Squidex/Config/Startup/MigratorHost.cs b/src/Squidex/Config/Startup/MigratorHost.cs new file mode 100644 index 000000000..f65867da4 --- /dev/null +++ b/src/Squidex/Config/Startup/MigratorHost.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.Migrations; + +namespace Squidex.Config.Startup +{ + public sealed class MigratorHost : SafeHostedService + { + private readonly Migrator migrator; + + public MigratorHost(Migrator migrator, IApplicationLifetime lifetime, ISemanticLog log) + : base(lifetime, log) + { + this.migrator = migrator; + } + + protected override Task StartAsync(ISemanticLog log, CancellationToken ct) + { + return migrator.MigrateAsync(); + } + } +} diff --git a/src/Squidex/Config/Startup/SafeHostedService.cs b/src/Squidex/Config/Startup/SafeHostedService.cs new file mode 100644 index 000000000..90f39c691 --- /dev/null +++ b/src/Squidex/Config/Startup/SafeHostedService.cs @@ -0,0 +1,59 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.Tasks; + +namespace Squidex.Config.Startup +{ + public abstract class SafeHostedService : IHostedService + { + private readonly IApplicationLifetime lifetime; + private readonly ISemanticLog log; + private bool isStarted; + + protected SafeHostedService(IApplicationLifetime lifetime, ISemanticLog log) + { + this.lifetime = lifetime; + + this.log = log; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + try + { + await StartAsync(log, cancellationToken); + + isStarted = true; + } + catch + { + lifetime.StopApplication(); + throw; + } + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (isStarted) + { + await StopAsync(log, cancellationToken); + } + } + + protected abstract Task StartAsync(ISemanticLog log, CancellationToken ct); + + protected virtual Task StopAsync(ISemanticLog log, CancellationToken ct) + { + return TaskHelper.Done; + } + } +} diff --git a/src/Squidex/WebStartup.cs b/src/Squidex/WebStartup.cs index ff233f5f7..10b2546e3 100644 --- a/src/Squidex/WebStartup.cs +++ b/src/Squidex/WebStartup.cs @@ -5,17 +5,31 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Squidex.Areas.Api; +using Squidex.Areas.Api.Config.Swagger; +using Squidex.Areas.Api.Controllers.Contents; using Squidex.Areas.Frontend; using Squidex.Areas.IdentityServer; +using Squidex.Areas.IdentityServer.Config; using Squidex.Areas.OrleansDashboard; using Squidex.Areas.Portal; +using Squidex.Config; +using Squidex.Config.Authentication; using Squidex.Config.Domain; using Squidex.Config.Orleans; +using Squidex.Config.Startup; using Squidex.Config.Web; +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Extensions.Actions.Twitter; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Diagnostics; +using Squidex.Pipeline; +using Squidex.Pipeline.Robots; namespace Squidex { @@ -28,13 +42,63 @@ namespace Squidex this.configuration = configuration; } - public void ConfigureServices(IServiceCollection services) + public IServiceProvider ConfigureServices(IServiceCollection services) { - services.AddOrleansSilo(); - services.AddAppServices(configuration); + var config = configuration; - services.AddHostedService(); - services.AddHostedService(); + services.AddHttpClient(); + services.AddLogging(); + services.AddMemoryCache(); + services.AddOptions(); + + services.AddMyAssetServices(config); + services.AddMyAuthentication(config); + services.AddMyEntitiesServices(config); + services.AddMyEventPublishersServices(config); + services.AddMyEventStoreServices(config); + services.AddMyIdentityServer(); + services.AddMyInfrastructureServices(); + services.AddMyLoggingServices(config); + services.AddMyMigrationServices(); + services.AddMyMvc(); + services.AddMyRuleServices(); + services.AddMySerializers(); + services.AddMyStoreServices(config); + services.AddMySwaggerSettings(); + services.AddMySubscriptionServices(config); + + services.Configure( + config.GetSection("contents")); + services.Configure( + config.GetSection("assets")); + services.Configure( + config.GetSection("mode")); + services.Configure( + config.GetSection("twitter")); + services.Configure( + config.GetSection("robots")); + services.Configure( + config.GetSection("healthz:gc")); + services.Configure( + config.GetSection("etags")); + + services.Configure( + config.GetSection("contentsController")); + services.Configure( + config.GetSection("urls")); + services.Configure( + config.GetSection("identity")); + services.Configure( + config.GetSection("ui")); + services.Configure( + config.GetSection("usage")); + + services.AddHostedService(); + services.AddHostedService(); + + var provider = services.AddAndBuildOrleans(configuration); + + return provider; } public void Configure(IApplicationBuilder app) diff --git a/src/Squidex/app/features/content/shared/content-item.component.html b/src/Squidex/app/features/content/shared/content-item.component.html index ff79e23d8..5fd8b28c0 100644 --- a/src/Squidex/app/features/content/shared/content-item.component.html +++ b/src/Squidex/app/features/content/shared/content-item.component.html @@ -16,7 +16,7 @@ - {{values[i]}} + {{values[i]}} diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts index 3d09b375e..6affed5fb 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts @@ -7,7 +7,7 @@ // tslint:disable:no-shadowed-variable -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { onErrorResumeNext } from 'rxjs/operators'; @@ -38,7 +38,7 @@ import { fadeAnimation ] }) -export class SchemaPageComponent extends ResourceOwner implements OnDestroy, OnInit { +export class SchemaPageComponent extends ResourceOwner implements OnInit { public fieldTypes = fieldTypes; public schemaExport: any; diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts index cd5173320..77cd4f192 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -26,7 +26,7 @@ import { styleUrls: ['string-validation.component.scss'], templateUrl: 'string-validation.component.html' }) -export class StringValidationComponent extends ResourceOwner implements OnDestroy, OnInit { +export class StringValidationComponent extends ResourceOwner implements OnInit { @Input() public editForm: FormGroup; diff --git a/src/Squidex/app/framework/angular/modals/tooltip.component.ts b/src/Squidex/app/framework/angular/modals/tooltip.component.ts index d4c3e9ec0..8d25f819e 100644 --- a/src/Squidex/app/framework/angular/modals/tooltip.component.ts +++ b/src/Squidex/app/framework/angular/modals/tooltip.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, Renderer2 } from '@angular/core'; import { fadeAnimation, @@ -22,7 +22,7 @@ import { ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class TooltipComponent extends StatefulComponent implements OnDestroy, OnInit { +export class TooltipComponent extends StatefulComponent implements OnInit { @Input() public target: any; diff --git a/src/Squidex/app/framework/angular/routers/parent-link.directive.ts b/src/Squidex/app/framework/angular/routers/parent-link.directive.ts index 71611a6b6..fd29a6404 100644 --- a/src/Squidex/app/framework/angular/routers/parent-link.directive.ts +++ b/src/Squidex/app/framework/angular/routers/parent-link.directive.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { ResourceOwner } from '@app/framework/internal'; @@ -13,7 +13,7 @@ import { ResourceOwner } from '@app/framework/internal'; @Directive({ selector: '[sqxParentLink]' }) -export class ParentLinkDirective extends ResourceOwner implements OnDestroy, OnInit { +export class ParentLinkDirective extends ResourceOwner implements OnInit { private url: string; @Input() diff --git a/src/Squidex/app/framework/angular/user-report.component.ts b/src/Squidex/app/framework/angular/user-report.component.ts index a67a7bfd3..7abc5dd45 100644 --- a/src/Squidex/app/framework/angular/user-report.component.ts +++ b/src/Squidex/app/framework/angular/user-report.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { timer } from 'rxjs'; import { @@ -18,7 +18,7 @@ import { selector: 'sqx-user-report', template: '' }) -export class UserReportComponent extends ResourceOwner implements OnDestroy, OnInit { +export class UserReportComponent extends ResourceOwner implements OnInit { constructor(changeDetector: ChangeDetectorRef, private readonly config: UserReportConfig, private readonly resourceLoader: ResourceLoaderService diff --git a/src/Squidex/app/shared/components/comments.component.ts b/src/Squidex/app/shared/components/comments.component.ts index 5081da228..5cbea6db8 100644 --- a/src/Squidex/app/shared/components/comments.component.ts +++ b/src/Squidex/app/shared/components/comments.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { timer } from 'rxjs'; import { onErrorResumeNext, switchMap } from 'rxjs/operators'; @@ -26,7 +26,7 @@ import { styleUrls: ['./comments.component.scss'], templateUrl: './comments.component.html' }) -export class CommentsComponent extends ResourceOwner implements OnDestroy, OnInit { +export class CommentsComponent extends ResourceOwner implements OnInit { public state: CommentsState; public userId: string; diff --git a/src/Squidex/app/shared/components/permission.directive.ts b/src/Squidex/app/shared/components/permission.directive.ts index 7f91d02c1..540b16c2f 100644 --- a/src/Squidex/app/shared/components/permission.directive.ts +++ b/src/Squidex/app/shared/components/permission.directive.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectorRef, Directive, Input, OnChanges, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; +import { ChangeDetectorRef, Directive, Input, OnChanges, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; import { AppDto, @@ -20,7 +20,7 @@ import { @Directive({ selector: '[sqxPermission]' }) -export class PermissionDirective extends ResourceOwner implements OnChanges, OnInit, OnDestroy { +export class PermissionDirective extends ResourceOwner implements OnChanges, OnInit { private viewCreated = false; @Input('sqxPermissionApp') diff --git a/src/Squidex/app/shell/pages/internal/internal-area.component.ts b/src/Squidex/app/shell/pages/internal/internal-area.component.ts index 243160d02..8282cdf9a 100644 --- a/src/Squidex/app/shell/pages/internal/internal-area.component.ts +++ b/src/Squidex/app/shell/pages/internal/internal-area.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { @@ -24,7 +24,7 @@ import { fadeAnimation ] }) -export class InternalAreaComponent extends ResourceOwner implements OnDestroy, OnInit { +export class InternalAreaComponent extends ResourceOwner implements OnInit { constructor( public readonly loadingService: LoadingService, private readonly dialogs: DialogService, diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/AssertHelper.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/AssertHelper.cs index 0cf6b6af2..b2a07c0d6 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/AssertHelper.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/AssertHelper.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; -using FluentAssertions.Equivalency; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Orleans;