/// <summary> /// Adds tenant level configuration to serve static files from modules /// </summary> private static void AddStaticFiles(BlocksCoreBuilder builder) { builder.Configure((app, routes, serviceProvider) => { var env = serviceProvider.GetRequiredService <IHostingEnvironment>(); var appContext = serviceProvider.GetRequiredService <IApplicationContext>(); IFileProvider fileProvider; if (env.IsDevelopment()) { var fileProviders = new List <IFileProvider>(); fileProviders.Add(new ModuleProjectStaticFileProvider(appContext)); fileProviders.Add(new ModuleEmbeddedStaticFileProvider(appContext)); fileProvider = new CompositeFileProvider(fileProviders); } else { fileProvider = new ModuleEmbeddedStaticFileProvider(appContext); } var options = serviceProvider.GetRequiredService <IOptions <StaticFileOptions> >().Value; options.RequestPath = ""; options.FileProvider = fileProvider; // Cache static files for a year as they are coming from embedded resources and should not vary options.OnPrepareResponse = ctx => { ctx.Context.Response.Headers[HeaderNames.CacheControl] = "max-age=" + (int)TimeSpan.FromDays(365).TotalSeconds; }; app.UseStaticFiles(options); }); }
/// <summary> /// Adds BlocksCore services to the host service collection. /// </summary> public static BlocksCoreBuilder AddBlocksCore(this IServiceCollection services) { // If an instance of BlocksCoreBuilder exists reuse it, // so we can call AddBlocksCore several times. var builder = services .LastOrDefault(d => d.ServiceType == typeof(BlocksCoreBuilder))? .ImplementationInstance as BlocksCoreBuilder; if (builder == null) { builder = new BlocksCoreBuilder(services); services.AddSingleton(builder); AddDefaultServices(services); AddShellServices(services); AddExtensionServices(builder); AddStaticFiles(builder); AddAntiForgery(builder); AddAuthentication(builder); AddDataProtection(builder); // Register the list of services to be resolved later on services.AddSingleton(services); } return(builder); }
/// <summary> /// Adds tenant level caching services. /// </summary> public static BlocksCoreBuilder AddCaching(this BlocksCoreBuilder builder) { builder.ConfigureServices(services => { services.AddTransient <ITagCache, DefaultTagCache>(); services.AddSingleton <ISignal, Signal>(); services.AddScoped <ICacheContextManager, CacheContextManager>(); services.AddScoped <ICacheScopeManager, CacheScopeManager>(); services.AddScoped <ICacheContextProvider, FeaturesCacheContextProvider>(); services.AddScoped <ICacheContextProvider, QueryCacheContextProvider>(); services.AddScoped <ICacheContextProvider, RolesCacheContextProvider>(); services.AddScoped <ICacheContextProvider, RouteCacheContextProvider>(); services.AddScoped <ICacheContextProvider, UserCacheContextProvider>(); services.AddScoped <ICacheContextProvider, KnownValueCacheContextProvider>(); // IMemoryCache is registered at the tenant level so that there is one instance for each tenant. services.AddSingleton <IMemoryCache, MemoryCache>(); // MemoryDistributedCache needs to be registered as a singleton as it owns a MemoryCache instance. services.AddSingleton <IDistributedCache, MemoryDistributedCache>(); }); return(builder); }
/// <summary> /// Registers a default tenant with a set of features that are used to setup and configure the actual tenants. /// For instance you can use this to add a custom Setup module. /// </summary> public static BlocksCoreBuilder AddSetupFeatures(this BlocksCoreBuilder builder, params string[] featureIds) { foreach (var featureId in featureIds) { builder.ApplicationServices.AddTransient(sp => new ShellFeature(featureId)); } return(builder); }
/// <summary> /// Host services to load site settings from the file system /// </summary> public static BlocksCoreBuilder AddSitesFolder(this BlocksCoreBuilder builder) { var services = builder.ApplicationServices; services.AddSingleton <IShellSettingsConfigurationProvider, ShellSettingsConfigurationProvider>(); services.AddSingleton <IShellSettingsManager, ShellSettingsManager>(); services.AddTransient <IConfigureOptions <ShellOptions>, ShellOptionsSetup>(); return(builder); }
/// <summary> /// Adds tenant level deferred tasks services. /// </summary> public static BlocksCoreBuilder AddDeferredTasks(this BlocksCoreBuilder builder) { builder.ConfigureServices(services => { services.TryAddScoped <IDeferredTaskEngine, DeferredTaskEngine>(); services.TryAddScoped <IDeferredTaskState, HttpContextTaskState>(); }); return(builder); }
/// <summary> /// Adds host level services to provide CLI commands. /// </summary> public static BlocksCoreBuilder AddCommands(this BlocksCoreBuilder builder) { var services = builder.ApplicationServices; services.AddScoped <ICommandManager, DefaultCommandManager>(); services.AddScoped <ICommandHandler, HelpCommand>(); services.AddScoped <ICommandParametersParser, CommandParametersParser>(); services.AddScoped <ICommandParser, CommandParser>(); return(builder); }
/// <summary> /// Registers tenants defined in configuration. /// </summary> public static BlocksCoreBuilder WithTenants(this BlocksCoreBuilder builder) { var services = builder.ApplicationServices; services.AddSingleton <IShellSettingsConfigurationProvider, FileShellSettingsConfigurationProvider>(); services.AddScoped <IShellDescriptorManager, FileShellDescriptorManager>(); services.AddSingleton <IShellSettingsManager, ShellSettingsManager>(); services.AddTransient <IConfigureOptions <ShellOptions>, ShellOptionsSetup>(); services.AddScoped <ShellSettingsWithTenants>(); return(builder); }
private static void AddExtensionServices(BlocksCoreBuilder builder) { builder.ApplicationServices.AddSingleton <IModuleNamesProvider, AssemblyAttributeModuleNamesProvider>(); builder.ApplicationServices.AddSingleton <IApplicationContext, ModularApplicationContext>(); builder.ApplicationServices.AddExtensionManagerHost(); builder.ConfigureServices(services => { services.AddExtensionManager(); }); }
/// <summary> /// Registers at the tenant level a set of features which are always enabled. /// </summary> public static BlocksCoreBuilder AddTenantFeatures(this BlocksCoreBuilder builder, params string[] featureIds) { builder.ConfigureServices(services => { foreach (var featureId in featureIds) { services.AddTransient(sp => new ShellFeature(featureId, alwaysEnabled: true)); } }); return(builder); }
/// <summary> /// Adds services at the host level to load site settings from the file system /// and tenant level services to store states and descriptors in the database. /// </summary> public static BlocksCoreBuilder AddDataStorage(this BlocksCoreBuilder builder) { builder.AddSitesFolder() .ConfigureServices(services => { services.AddScoped <IShellDescriptorManager, ShellDescriptorManager>(); services.AddScoped <IShellStateManager, ShellStateManager>(); services.AddScoped <IShellFeaturesManager, ShellFeaturesManager>(); services.AddScoped <IShellDescriptorFeaturesManager, ShellDescriptorFeaturesManager>(); }); return(builder); }
/// <summary> /// Adds tenant level data protection services. /// </summary> private static void AddDataProtection(BlocksCoreBuilder builder) { builder.ConfigureServices((services, serviceProvider) => { var settings = serviceProvider.GetRequiredService <ShellSettings>(); var options = serviceProvider.GetRequiredService <IOptions <ShellOptions> >(); var directory = Directory.CreateDirectory(Path.Combine( options.Value.ShellsApplicationDataPath, options.Value.ShellsContainerName, settings.Name, "DataProtection-Keys")); // Re-register the data protection services to be tenant-aware so that modules that internally // rely on IDataProtector/IDataProtectionProvider automatically get an isolated instance that // manages its own key ring and doesn't allow decrypting payloads encrypted by another tenant. // By default, the key ring is stored in the tenant directory of the configured App_Data path. var collection = new ServiceCollection() .AddDataProtection() .PersistKeysToFileSystem(directory) .SetApplicationName(settings.Name) .Services; // Retrieve the implementation type of the newly startup filter registered as a singleton var startupFilterType = collection.FirstOrDefault(s => s.ServiceType == typeof(IStartupFilter))?.ImplementationType; if (startupFilterType != null) { // Remove any previously registered data protection startup filters. var descriptors = services.Where(s => s.ServiceType == typeof(IStartupFilter) && (s.ImplementationInstance?.GetType() == startupFilterType || s.ImplementationType == startupFilterType)).ToArray(); foreach (var descriptor in descriptors) { services.Remove(descriptor); } } // Remove any previously registered options setups. services.RemoveAll <IConfigureOptions <KeyManagementOptions> >(); services.RemoveAll <IConfigureOptions <DataProtectionOptions> >(); services.Add(collection); }); }
/// <summary> /// Adds host and tenant level antiforgery services. /// </summary> private static void AddAntiForgery(BlocksCoreBuilder builder) { builder.ApplicationServices.AddAntiforgery(); builder.ConfigureServices((services, serviceProvider) => { var settings = serviceProvider.GetRequiredService <ShellSettings>(); var tenantName = settings.Name; var tenantPrefix = "/" + settings.RequestUrlPrefix; services.AddAntiforgery(options => { options.Cookie.Name = "orchantiforgery_" + tenantName; options.Cookie.Path = tenantPrefix; }); }); }
/// <summary> /// Adds host and tenant level authentication services and configuration. /// </summary> private static void AddAuthentication(BlocksCoreBuilder builder) { builder.ApplicationServices.AddAuthentication(); builder.ConfigureServices(services => { services.AddAuthentication(); // IAuthenticationSchemeProvider is already registered at the host level. // We need to register it again so it is taken into account at the tenant level // because it holds a reference to an underlying dictionary, responsible of storing // the registered schemes which need to be distinct for each tenant. services.AddSingleton <IAuthenticationSchemeProvider, AuthenticationSchemeProvider>(); }) .Configure(app => { app.UseAuthentication(); }); }
public static BlocksCoreBuilder AddDataAccess(this BlocksCoreBuilder builder) { builder.ConfigureServices(services => { services.TryAddDataProvider(name: "Sql Server", value: "SqlConnection", hasConnectionString: true, hasTablePrefix: true, isDefault: false); services.TryAddDataProvider(name: "Sqlite", value: "Sqlite", hasConnectionString: false, hasTablePrefix: false, isDefault: true); services.TryAddDataProvider(name: "MySql", value: "MySql", hasConnectionString: true, hasTablePrefix: true, isDefault: false); services.TryAddDataProvider(name: "Postgres", value: "Postgres", hasConnectionString: true, hasTablePrefix: true, isDefault: false); services.AddDbContext <DbContext, BlocksCoreDbContext>(); services.AddScoped <IDataContext, DataContext>(); }); return(builder); }
/// <summary> /// Registers and configures a background hosted service to manage tenant background tasks. /// </summary> public static BlocksCoreBuilder AddBackgroundService(this BlocksCoreBuilder builder) { builder.ApplicationServices.AddSingleton <IHostedService, ModularBackgroundService>(); return(builder); }
/// <summary> /// Adds tenant level MVC services and configuration. /// </summary> public static BlocksCoreBuilder AddMvc(this BlocksCoreBuilder builder) { return(builder.RegisterStartup <Startup>()); }