2020-05-03 17:09:45 +02:00
using FluentValidation ;
2020-11-04 01:25:38 +01:00
using Microsoft.AspNetCore.Authentication.Cookies ;
2020-05-09 20:05:08 +02:00
using Microsoft.AspNetCore.Hosting ;
2020-05-03 17:09:45 +02:00
using Microsoft.AspNetCore.Identity ;
2020-10-27 15:47:23 +01:00
using Microsoft.AspNetCore.Mvc ;
2020-05-11 15:34:47 +02:00
using Microsoft.Extensions.Configuration ;
2020-04-15 01:42:06 +02:00
using Microsoft.Extensions.DependencyInjection ;
2020-10-27 15:47:23 +01:00
using Microsoft.Extensions.DependencyInjection.Extensions ;
2020-05-11 15:34:47 +02:00
using Microsoft.Extensions.Options ;
using Raven.Client.Documents ;
2020-05-15 14:23:47 +02:00
using Raven.Client.Documents.Indexes ;
2021-09-29 11:41:19 +02:00
using Raven.Client.Http ;
2020-04-05 01:19:42 +02:00
using System ;
2021-08-27 11:46:50 +02:00
using System.Collections.Generic ;
2020-11-01 23:23:36 +01:00
using System.IO ;
2020-05-29 14:33:20 +02:00
using System.Linq ;
2020-05-15 14:23:47 +02:00
using System.Reflection ;
2020-04-05 01:19:42 +02:00
using zero.Core ;
2020-05-09 20:05:08 +02:00
using zero.Core.Api ;
2020-05-27 16:03:38 +02:00
using zero.Core.Assemblies ;
2020-11-19 00:14:52 +01:00
using zero.Core.Collections ;
2020-12-15 16:09:58 +01:00
using zero.Core.Cultures ;
2020-11-15 17:07:27 +01:00
using zero.Core.Database ;
2020-04-05 01:19:42 +02:00
using zero.Core.Entities ;
2020-05-11 15:34:47 +02:00
using zero.Core.Extensions ;
2020-11-05 00:27:02 +01:00
using zero.Core.Handlers ;
2020-04-15 01:42:06 +02:00
using zero.Core.Identity ;
2020-05-14 13:32:33 +02:00
using zero.Core.Options ;
2020-05-08 18:43:39 +02:00
using zero.Core.Plugins ;
2020-11-03 16:07:35 +01:00
using zero.Core.Security ;
2020-05-03 17:09:45 +02:00
using zero.Core.Validation ;
2020-10-27 15:47:23 +01:00
using zero.Web.Controllers ;
2020-05-14 13:32:33 +02:00
using zero.Web.Defaults ;
2020-08-13 12:03:24 +02:00
using zero.Web.Filters ;
2020-11-04 16:16:36 +01:00
using zero.Web.Security ;
2020-11-01 23:23:36 +01:00
using Zero.Web.DevServer ;
2020-04-05 00:39:12 +02:00
namespace zero.Web
{
2020-05-12 01:36:27 +02:00
// TODO maybe use a middleware like Hangfire does: https://github.com/HangfireIO/Hangfire/blob/master/src/Hangfire.AspNetCore/HangfireEndpointRouteBuilderExtensions.cs
2020-04-05 00:39:12 +02:00
public class ZeroBuilder
{
public virtual IServiceCollection Services { get ; }
2020-05-27 16:03:38 +02:00
public virtual IMvcBuilder Mvc { get ; }
IConfiguration Configuration ;
IZeroStartupOptions StartupOptions ;
2020-05-11 15:34:47 +02:00
2020-05-27 18:29:36 +02:00
public ZeroBuilder ( IServiceCollection services , IConfiguration configuration , Action < IZeroStartupOptions > setupAction )
2020-04-05 00:39:12 +02:00
{
2020-05-27 18:29:36 +02:00
Services = services ;
2020-10-27 15:47:23 +01:00
Mvc = services . AddMvc ();
2020-05-11 15:34:47 +02:00
Configuration = configuration ;
2020-04-15 01:42:06 +02:00
2020-05-27 16:03:38 +02:00
// create startup options
2020-05-27 18:29:36 +02:00
StartupOptions = new ZeroStartupOptions ( Mvc );
2020-05-27 16:03:38 +02:00
StartupOptions . AssemblyDiscoveryRules . Add ( new ZeroAssemblyDiscoveryRule ());
setupAction ?. Invoke ( StartupOptions );
2020-05-11 15:34:47 +02:00
2020-05-27 16:03:38 +02:00
2020-05-28 00:36:52 +02:00
// adds and discovers additional and built-in assemblies
new AssemblyDiscovery ( Mvc ). Execute ( StartupOptions . AssemblyDiscoveryRules );
2020-05-27 16:03:38 +02:00
// add default plugin
AddPlugin < ZeroBackofficePlugin >();
2020-05-27 18:29:36 +02:00
2020-05-27 16:03:38 +02:00
// create and bind zero options
2020-05-14 13:32:33 +02:00
Services . AddOptions < ZeroOptions >()
. Bind ( Configuration . GetSection ( "Zero" ))
. Configure ( opts =>
{
2020-05-27 16:03:38 +02:00
//opts.AssemblyDiscoveryRules.Add(new ZeroAssemblyDiscoveryRule());
2020-05-14 13:32:33 +02:00
opts . ZeroVersion = "0.0.1.0" ; // TODO
});
2020-05-13 14:06:11 +02:00
2020-05-27 16:03:38 +02:00
// add transient options to DI
Services . AddTransient < IZeroOptions >( factory => factory . GetService < IOptionsMonitor < ZeroOptions >>(). CurrentValue );
2020-05-11 15:34:47 +02:00
2020-05-27 16:03:38 +02:00
// configure MVC
2020-10-27 15:47:23 +01:00
Services . TryAddEnumerable ( ServiceDescriptor . Transient < IConfigureOptions < MvcOptions >, ZeroBuilderMvcOptions >());
2020-08-27 14:22:13 +02:00
Mvc . AddNewtonsoftJson ( x =>
{
// TODO this shall only be configurated for backoffice controllers
2020-10-27 15:47:23 +01:00
BackofficeJsonSerlializerSettings . Setup ( x . SerializerSettings );
2020-08-27 14:22:13 +02:00
});
2020-09-18 01:06:21 +02:00
2020-05-11 15:34:47 +02:00
if ( Environment . GetEnvironmentVariable ( "DOTNET_WATCH" ) == "1" )
{
2020-05-27 16:03:38 +02:00
Mvc . AddRazorRuntimeCompilation ();
2020-10-27 15:47:23 +01:00
}
2020-05-22 15:06:17 +02:00
2020-11-04 16:16:36 +01:00
// configure Raven + identity
2020-05-27 16:03:38 +02:00
ConfigureDatabase ();
ConfigureIdentity ();
// configure FluentValidation
2020-07-09 15:33:58 +02:00
ValidatorOptions . Global . PropertyNameResolver = ValidatorCamelCasePropertyResolver . ResolvePropertyName ;
2020-05-27 16:03:38 +02:00
// add default services
2020-11-03 16:07:35 +01:00
Services . AddHttpContextAccessor ();
2020-11-16 01:07:07 +01:00
Services . AddScoped < IApplicationResolver , ApplicationResolver >();
2020-12-15 16:09:58 +01:00
Services . AddScoped < ICultureResolver , CultureResolver >();
2020-11-04 20:45:53 +01:00
Services . AddScoped < IZeroContext , ZeroContext >();
2020-05-27 16:03:38 +02:00
2020-11-06 19:18:56 +01:00
Services . AddScoped < IBackofficeStore , BackofficeStore >();
2020-11-18 12:00:51 +01:00
Services . AddScoped < BackofficeFilterAttribute >();
2020-08-13 12:03:24 +02:00
Services . AddScoped < ModelStateValidationFilterAttribute >();
2021-08-26 14:05:17 +02:00
Services . AddScoped < ICollectionInterceptorHandler , CollectionInterceptorHandler >();
2020-11-18 15:55:18 +01:00
2020-05-27 16:03:38 +02:00
Services . AddHttpContextAccessor ();
Services . AddTransient < IZeroVue , ZeroVue >();
2021-08-11 15:47:42 +02:00
Services . AddScoped < IPaths >( factory => new Paths ( factory . GetService < IWebHostEnvironment >(), true ));
2020-11-01 23:23:36 +01:00
2020-11-05 00:46:18 +01:00
Services . AddTransient < IHandlerHolder , HandlerHolder >();
2020-11-01 23:23:36 +01:00
// add dev server
Services . AddOptions < ZeroDevOptions >()
. Bind ( Configuration . GetSection ( "Zero:DevServer" ))
. Configure < IWebHostEnvironment >(( opts , env ) =>
{
opts . WorkingDirectory = Path . Combine ( env . ContentRootPath , ".." , "Zero.Web.UI" , "App" );
});
Services . AddHostedService < ZeroDevService >();
2020-05-11 15:34:47 +02:00
}
/// <summary>
/// Configures Raven database instance
/// </summary>
void ConfigureDatabase ()
{
2021-03-18 15:54:29 +01:00
Services . AddSingleton < IZeroDocumentConventionsBuilder , ZeroDocumentConventionsBuilder >();
2021-10-28 12:52:18 +02:00
Services . AddSingleton < IZeroDocumentStore , ZeroDocumentStore >( context =>
2020-05-11 15:34:47 +02:00
{
IZeroOptions options = context . GetService < IZeroOptions >();
2021-03-18 15:54:29 +01:00
IZeroDocumentConventionsBuilder conventionsBuilder = context . GetService < IZeroDocumentConventionsBuilder >();
2020-11-15 17:07:27 +01:00
2021-10-28 12:52:18 +02:00
IDocumentStore store = new ZeroDocumentStore ( options )
2020-05-11 15:34:47 +02:00
{
2021-09-29 11:41:19 +02:00
Urls = new string [ 1 ] { options . Raven . Url },
2021-09-29 11:52:11 +02:00
Conventions = // TODO activate and test this
{
AggressiveCache =
{
Duration = TimeSpan . FromHours ( 1 ),
Mode = AggressiveCacheMode . TrackChanges
}
}
2020-05-11 15:34:47 +02:00
};
2021-03-18 15:54:29 +01:00
conventionsBuilder . Run ( store . Conventions );
2021-02-19 17:02:30 +01:00
2021-03-18 15:54:29 +01:00
IDocumentStore raven = store . Initialize ();
2020-05-15 14:23:47 +02:00
2020-05-21 11:18:05 +02:00
// create all indexes
2020-12-02 15:45:44 +01:00
if ( options . SetupCompleted )
2020-05-29 14:33:20 +02:00
{
2021-10-29 15:13:05 +02:00
//var indexes = options.Raven.Indexes.BuildAll(options, store);
//IndexCreation.CreateIndexes(indexes, store, database: options.Raven.Database);
2020-05-29 14:33:20 +02:00
}
2020-05-15 14:23:47 +02:00
2021-10-28 12:52:18 +02:00
return ( ZeroDocumentStore ) raven ;
2020-05-11 15:34:47 +02:00
});
2020-12-09 13:42:23 +01:00
2021-10-28 12:52:18 +02:00
Services . AddScoped < IZeroStore , ZeroStore >();
2020-05-11 15:34:47 +02:00
}
/// <summary>
2020-11-04 16:16:36 +01:00
/// Configures identity
2020-05-11 15:34:47 +02:00
/// </summary>
void ConfigureIdentity ()
{
2020-11-04 16:16:36 +01:00
Services . TryAddEnumerable ( ServiceDescriptor . Singleton < IPostConfigureOptions < CookieAuthenticationOptions >, PostConfigureCookieAuthenticationOptions >());
2020-11-05 00:27:02 +01:00
Services . AddZeroIdentity < BackofficeUser , BackofficeUserRole >();
Services . Replace < IUserClaimsPrincipalFactory < BackofficeUser >, ZeroBackofficeClaimsPrincipalFactory < BackofficeUser , BackofficeUserRole >>();
2020-11-17 12:24:56 +01:00
Services . Replace < IUserStore < BackofficeUser >, RavenCoreUserStore < BackofficeUser , BackofficeUserRole >>( ServiceLifetime . Scoped );
Services . Replace < IRoleStore < BackofficeUserRole >, RavenCoreRoleStore < BackofficeUserRole >>( ServiceLifetime . Scoped );
2020-11-04 16:16:36 +01:00
2020-11-04 16:22:40 +01:00
Services . AddAuthentication ( Constants . Auth . BackofficeScheme )
2020-11-05 00:27:02 +01:00
. AddZeroBackofficeCookie < BackofficeUser , BackofficeUserRole >();
2020-11-04 16:16:36 +01:00
Services . AddAuthorization ();
2020-04-05 01:19:42 +02:00
}
2020-05-11 15:34:47 +02:00
/// <summary>
/// Use specified options
/// </summary>
2020-04-05 01:19:42 +02:00
public ZeroBuilder WithOptions ( Action < ZeroOptions > configureOptions )
{
2020-05-14 13:32:33 +02:00
Services . Configure ( configureOptions );
2020-04-05 01:19:42 +02:00
return this ;
2020-04-05 00:39:12 +02:00
}
2020-05-18 15:55:43 +02:00
/// <summary>
/// Adds a zero plugin
/// </summary>
2020-05-27 18:29:36 +02:00
public ZeroBuilder AddPlugin < T >() where T : class , IZeroPlugin , new ()
2020-05-18 15:55:43 +02:00
{
2020-05-28 00:36:52 +02:00
AssemblyDiscovery . Current . AddAssembly ( typeof ( T ). Assembly );
2020-11-03 00:49:41 +01:00
Services . AddSingleton < IZeroPlugin , T >();
2020-05-18 15:55:43 +02:00
AddPluginServices < T >();
2020-05-27 18:29:36 +02:00
return this ;
2020-05-18 15:55:43 +02:00
}
/// <summary>
/// Adds a zero plugin
/// </summary>
2020-05-27 18:29:36 +02:00
public ZeroBuilder AddPlugin < T >( Func < IServiceProvider , T > implementationFactory ) where T : class , IZeroPlugin , new ()
2020-05-18 15:55:43 +02:00
{
2020-05-28 00:36:52 +02:00
AssemblyDiscovery . Current . AddAssembly ( typeof ( T ). Assembly );
2020-11-03 00:49:41 +01:00
Services . AddSingleton < IZeroPlugin , T >( implementationFactory );
2020-05-18 15:55:43 +02:00
AddPluginServices < T >();
2020-05-27 18:29:36 +02:00
return this ;
2020-05-18 15:55:43 +02:00
}
/// <summary>
/// Creates a temporary instance of the plugin to add additional services
/// </summary>
/// <typeparam name="T"></typeparam>
void AddPluginServices < T >() where T : class , IZeroPlugin , new ()
{
try
{
T plugin = new T ();
2020-09-10 16:12:06 +02:00
plugin . ConfigureServices ( Services , Configuration );
2020-05-18 15:55:43 +02:00
2020-10-19 15:58:32 +02:00
Services . Configure < ZeroOptions >( opts => plugin . Configure ( opts ));
2020-05-18 15:55:43 +02:00
}
catch
{
throw new Exception ( $"Plugin \" { nameof ( T )} \ " needs an additional parameterless constructor as ConfigureServices() is called before the DI container is built" );
}
}
2020-10-27 15:47:23 +01:00
class ZeroBuilderMvcOptions : IConfigureOptions < MvcOptions >
{
IZeroOptions Options { get ; set ; }
public ZeroBuilderMvcOptions ( IZeroOptions options )
{
Options = options ;
}
public void Configure ( MvcOptions options )
{
options . Conventions . Add ( new ZeroBackofficeControllerModelConvention ( Options . BackofficePath ));
}
}
2020-04-05 00:39:12 +02:00
}
}