mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
216 changed files with 2812 additions and 2835 deletions
@ -0,0 +1,57 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Security.Claims; |
|||
using Jint; |
|||
using Jint.Native; |
|||
using Jint.Runtime.Interop; |
|||
using NodaTime; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Scripting.ContentWrapper; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Scripting |
|||
{ |
|||
public sealed class DefaultConverter : IObjectConverter |
|||
{ |
|||
public static readonly DefaultConverter Instance = new DefaultConverter(); |
|||
|
|||
private DefaultConverter() |
|||
{ |
|||
} |
|||
|
|||
public bool TryConvert(Engine engine, object value, out JsValue result) |
|||
{ |
|||
result = null; |
|||
|
|||
if (value is Enum) |
|||
{ |
|||
result = value.ToString(); |
|||
return true; |
|||
} |
|||
|
|||
switch (value) |
|||
{ |
|||
case IUser user: |
|||
result = JintUser.Create(engine, user); |
|||
return true; |
|||
case ClaimsPrincipal principal: |
|||
result = JintUser.Create(engine, principal); |
|||
return true; |
|||
case Instant instant: |
|||
result = JsValue.FromObject(engine, instant.ToDateTimeUtc()); |
|||
return true; |
|||
case NamedContentData content: |
|||
result = new ContentDataObject(engine, content); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -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<ContentOptions>( |
|||
config.GetSection("contents")); |
|||
services.Configure<AssetOptions>( |
|||
config.GetSection("assets")); |
|||
services.Configure<ReadonlyOptions>( |
|||
config.GetSection("mode")); |
|||
services.Configure<TwitterOptions>( |
|||
config.GetSection("twitter")); |
|||
services.Configure<RobotsTxtOptions>( |
|||
config.GetSection("robots")); |
|||
services.Configure<GCHealthCheckOptions>( |
|||
config.GetSection("healthz:gc")); |
|||
services.Configure<ETagOptions>( |
|||
config.GetSection("etags")); |
|||
|
|||
services.Configure<MyContentsControllerOptions>( |
|||
config.GetSection("contentsController")); |
|||
services.Configure<MyUrlsOptions>( |
|||
config.GetSection("urls")); |
|||
services.Configure<MyIdentityOptions>( |
|||
config.GetSection("identity")); |
|||
services.Configure<MyUIOptions>( |
|||
config.GetSection("ui")); |
|||
services.Configure<MyUsageOptions>( |
|||
config.GetSection("usage")); |
|||
} |
|||
} |
|||
} |
|||
@ -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<IInitializable> targets; |
|||
private readonly IApplicationLifetime lifetime; |
|||
private readonly ISemanticLog log; |
|||
|
|||
public InitializeHostedService(IEnumerable<IInitializable> 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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<SiloWrapper>() |
|||
.As<IHostedService>() |
|||
.AsSelf(); |
|||
services.Configure<ClusterOptions>(options => |
|||
{ |
|||
options.Configure(); |
|||
}); |
|||
|
|||
services.Configure<ProcessExitHandlingOptions>(options => |
|||
{ |
|||
options.FastKillOnProcessExit = false; |
|||
}); |
|||
|
|||
services.AddServicesForSelfHostedDashboard(null, options => |
|||
{ |
|||
options.HideTrace = true; |
|||
}); |
|||
|
|||
services.AddSingletonAs(c => c.GetRequiredService<IClusterClient>()) |
|||
.As<IClusterClient>(); |
|||
services.AddHostedService<SiloHost>(); |
|||
|
|||
var hostBuilder = new SiloHostBuilder() |
|||
.UseDashboardEx() |
|||
.EnableDirectClient() |
|||
.AddIncomingGrainCallFilter<LocalCacheFilter>() |
|||
.AddStartupTask<Bootstrap<IContentSchedulerGrain>>() |
|||
.AddStartupTask<Bootstrap<IEventConsumerManagerGrain>>() |
|||
.AddStartupTask<Bootstrap<IRuleDequeuerGrain>>() |
|||
.AddStartupTask<Bootstrap<IUsageTrackerGrain>>() |
|||
.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<ClusterMembershipOptions>(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<SiloWrapper>().Client) |
|||
.As<IGrainFactory>(); |
|||
return provider; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -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<ISiloHost> 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<IClusterClient>(); } |
|||
} |
|||
|
|||
public SiloWrapper(IConfiguration config, ISemanticLog log, IApplicationLifetime lifetime) |
|||
{ |
|||
this.lifetime = lifetime; |
|||
this.log = log; |
|||
|
|||
lazySilo = new Lazy<ISiloHost>(() => |
|||
{ |
|||
var hostBuilder = new SiloHostBuilder() |
|||
.UseDashboard(options => options.HostSelf = false) |
|||
.EnableDirectClient() |
|||
.AddIncomingGrainCallFilter<LocalCacheFilter>() |
|||
.AddStartupTask<InitializerStartup>() |
|||
.AddStartupTask<Bootstrap<IContentSchedulerGrain>>() |
|||
.AddStartupTask<Bootstrap<IEventConsumerManagerGrain>>() |
|||
.AddStartupTask<Bootstrap<IRuleDequeuerGrain>>() |
|||
.AddStartupTask<Bootstrap<IUsageTrackerGrain>>() |
|||
.Configure<ClusterOptions>(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<ProcessExitHandlingOptions>(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<ClusterMembershipOptions>(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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<IInitializable> targets; |
|||
|
|||
public InitializerHost(IEnumerable<IInitializable> 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)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
<sqx-title message="{app} | API | GraphQL" parameter1="app" [value1]="appsState.appName"></sqx-title> |
|||
|
|||
<sqx-panel desiredWidth="*" isFullSize="true"> |
|||
<sqx-panel desiredWidth="*" minWidth="50rem" isFullSize="true"> |
|||
<div inner #graphiQLContainer></div> |
|||
</sqx-panel> |
|||
@ -0,0 +1,52 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; |
|||
|
|||
import { AppLanguageDto, RootFieldDto } from '@app/shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-field-languages', |
|||
template: ` |
|||
<ng-container *ngIf="field.isLocalizable && languages.length > 1"> |
|||
<button *ngIf="!field.properties.isComplexUI" type="button" class="btn btn-text-secondary btn-sm mr-1" (click)="showAllControlsChange.emit(!showAllControls)"> |
|||
{{showAllControls ? 'Single Language' : 'All Languages'}} |
|||
</button> |
|||
|
|||
<ng-container *ngIf="field.properties.isComplexUI || !showAllControls"> |
|||
<sqx-language-selector size="sm" #buttonLanguages |
|||
[selectedLanguage]="language" |
|||
(selectedLanguageChange)="languageChange.emit($event)" |
|||
[languages]="languages"> |
|||
</sqx-language-selector> |
|||
|
|||
<sqx-onboarding-tooltip helpId="languages" [for]="buttonLanguages" position="topRight" after="120000"> |
|||
Please remember to check all languages when you see validation errors. |
|||
</sqx-onboarding-tooltip> |
|||
</ng-container> |
|||
</ng-container>`, |
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class FieldLanguagesComponent { |
|||
@Input() |
|||
public field: RootFieldDto; |
|||
|
|||
@Input() |
|||
public showAllControls: boolean; |
|||
|
|||
@Input() |
|||
public language: AppLanguageDto; |
|||
|
|||
@Input() |
|||
public languages: AppLanguageDto[]; |
|||
|
|||
@Output() |
|||
public languageChange = new EventEmitter<AppLanguageDto>(); |
|||
|
|||
@Output() |
|||
public showAllControlsChange = new EventEmitter<AppLanguageDto>(); |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
|
|||
import { FieldDto } from '@app/shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-content-item-editor', |
|||
template: ` |
|||
<div [formGroup]="form"> |
|||
<div [ngSwitch]="field.properties.fieldType"> |
|||
<div *ngSwitchCase="'Number'"> |
|||
<div [ngSwitch]="field.properties['editor']"> |
|||
<div *ngSwitchCase="'Input'"> |
|||
<input class="form-control" type="number" [formControlName]="field.name" [placeholder]="field.displayPlaceholder" /> |
|||
</div> |
|||
<div *ngSwitchCase="'Dropdown'"> |
|||
<select class="form-control" [formControlName]="field.name"> |
|||
<option [ngValue]="null"></option> |
|||
<option *ngFor="let value of field.properties['allowedValues']" [ngValue]="value">{{value}}</option> |
|||
</select> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div *ngSwitchCase="'String'"> |
|||
<div [ngSwitch]="field.properties['editor']"> |
|||
<div *ngSwitchCase="'Input'"> |
|||
<input class="form-control" type="text" [formControlName]="field.name" [placeholder]="field.displayPlaceholder" /> |
|||
</div> |
|||
<div *ngSwitchCase="'Slug'"> |
|||
<input class="form-control" type="text" [formControlName]="field.name" [placeholder]="field.displayPlaceholder" sqxTransformInput="Slugify" /> |
|||
</div> |
|||
<div *ngSwitchCase="'Dropdown'"> |
|||
<select class="form-control" [formControlName]="field.name"> |
|||
<option [ngValue]="null"></option> |
|||
<option *ngFor="let value of field.properties['allowedValues']" [ngValue]="value">{{value}}</option> |
|||
</select> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div *ngSwitchCase="'Boolean'"> |
|||
<div [ngSwitch]="field.properties['editor']"> |
|||
<div *ngSwitchCase="'Toggle'"> |
|||
<sqx-toggle [formControlName]="field.name" [threeStates]="!field.properties.isRequired"></sqx-toggle> |
|||
</div> |
|||
<div *ngSwitchCase="'Checkbox'"> |
|||
<div class="form-check form-check-inline"> |
|||
<input class="form-check-input" type="checkbox" [formControlName]="field.name" sqxIndeterminateValue /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div>`,
|
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class ContentItemEditorComponent { |
|||
@Input() |
|||
public field: FieldDto; |
|||
|
|||
@Input() |
|||
public form: FormGroup; |
|||
} |
|||
@ -1,16 +1,16 @@ |
|||
<ng-container *ngIf="selectedName"> |
|||
<ng-container *ngIf="snapshot.selectedName"> |
|||
<span>Preview: </span> |
|||
|
|||
<div class="btn-group ml-1" #buttonGroup> |
|||
<button type="button" class="btn btn-secondary" (click)="follow(selectedName)"> |
|||
<i class="icon-external-link"></i> {{selectedName}} |
|||
<button type="button" class="btn btn-secondary" (click)="follow(snapshot.selectedName)"> |
|||
<i class="icon-external-link"></i> {{snapshot.selectedName}} |
|||
</button> |
|||
|
|||
<div class="btn-group" *ngIf="alternativeNames.length > 0"> |
|||
<div class="btn-group" *ngIf="snapshot.alternativeNames.length > 0"> |
|||
<button type="button" class="btn btn-secondary dropdown-toggle" (click)="dropdown.toggle()"></button> |
|||
|
|||
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="buttonGroup" @fade> |
|||
<a *ngFor="let name of alternativeNames" class="dropdown-item" (click)="follow(name)">{{name}}</a> |
|||
<a *ngFor="let name of snapshot.alternativeNames" class="dropdown-item" (click)="follow(name)">{{name}}</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue