sozsoft-platform/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs
2026-06-03 00:24:33 +03:00

534 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.PostgreSql;
using Sozsoft.Languages;
using Sozsoft.MailQueue;
using Sozsoft.Notifications.Application;
using Sozsoft.Platform.EntityFrameworkCore;
using Sozsoft.SqlQueryManager;
using Sozsoft.Platform.Extensions;
using Sozsoft.Platform.FileManagement;
using Sozsoft.Platform.Identity;
using Sozsoft.Platform.Localization;
using Sozsoft.Settings;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using OpenIddict.Server;
using OpenIddict.Server.AspNetCore;
using OpenIddict.Validation.AspNetCore;
using static OpenIddict.Server.OpenIddictServerEvents;
using Volo.Abp;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.BackgroundWorkers.Hangfire;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.FileSystem;
using Volo.Abp.Caching;
using Volo.Abp.Hangfire;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Sozsoft.Platform.DynamicServices;
using static Sozsoft.Platform.PlatformConsts;
using static Sozsoft.Settings.SettingsConsts;
using Hangfire.SqlServer;
using DevExpress.AspNetCore;
using DevExpress.AspNetCore.Reporting;
using DevExpress.XtraReports.Web.Extensions;
using Sozsoft.Platform.ReportServices;
namespace Sozsoft.Platform;
[DependsOn(
typeof(PlatformHttpApiModule),
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(PlatformApplicationModule),
typeof(PlatformEntityFrameworkCoreModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpAccountWebOpenIddictModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule),
typeof(AbpBackgroundWorkersHangfireModule),
typeof(SqlQueryManagerApplicationModule)
)]
public class PlatformHttpApiHostModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddServer(server =>
{
server.SetAccessTokenLifetime(TimeSpan.FromMinutes(60));
server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(90));
server.AddEventHandler<HandleRevocationRequestContext>(builder =>
builder.UseScopedHandler<PlatformSessionRevocationHandler>());
});
builder.AddValidation(options =>
{
options.AddAudiences(PlatformConsts.AppName);
options.UseLocalServer();
options.UseAspNetCore();
options.EnableTokenEntryValidation();
});
});
PreConfigure<IdentityBuilder>(builder =>
{
builder.AddClaimsPrincipalFactory<PlatformUserClaimsPrincipalFactory>();
builder.AddSignInManager<PlatformSignInManager>();
});
context.Services.Replace(
ServiceDescriptor.Transient<AbpUserClaimsPrincipalFactory, PlatformUserClaimsPrincipalFactory>());
context.Services.AddTransient<IPlatformSignInManager, PlatformSignInManager>();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
//Tenant bazında lokalizasyon ayarları için
//TenantLocalization Middleware kaydı
context.Services.AddTransient<TenantLocalizationMiddleware>();
context.Services.AddTransient<TenantLocalizationInitializer>();
ConfigureAuthentication(context);
ConfigureBundles();
ConfigureUrls(configuration);
ConfigureConventionalControllers();
//Localization'ı veritabanından kullandıgımız icin bir json resource kullanmıyoruz
//Dolayısıyla şimdilik ihtiyacımız yok ama ileride başka bir sebepten kullanabiliriz diye silmedik
//ConfigureVirtualFileSystem(context);
ConfigureCors(context, configuration);
ConfigureSwaggerServices(context, configuration);
ConfigureIdentity(configuration);
ConfigureCache();
ConfigureHangfire(context, configuration);
ConfigureBlobStoring(configuration);
ConfigureAuditing();
ConfigureDynamicServices(context);
ConfigureDevExpressReporting(context, configuration);
context.Services.AddSignalR();
Configure<AbpExceptionHttpStatusCodeOptions>(options =>
{
options.Map(AppErrorCodes.NoAuth, System.Net.HttpStatusCode.Unauthorized);
});
Configure<OpenIddictServerAspNetCoreOptions>(options =>
{
options.DisableTransportSecurityRequirement = true;
});
Configure<AbpApplicationConfigurationOptions>(options =>
{
options.Contributors.AddIfNotContains(new PlatformApplicationConfigurationContributor());
});
}
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
{
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles.Configure(
BasicThemeBundles.Styles.Global,
bundle =>
{
bundle.AddFiles("/global-styles.css");
}
);
});
}
private void ConfigureUrls(IConfiguration configuration)
{
Configure<AppUrlOptions>(options =>
{
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());
options.Applications[PlatformConsts.React].RootUrl = configuration["App:ClientUrl"];
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.EmailConfirmation] = "confirm";
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.PasswordReset] = "reset-password";
options.Applications[PlatformConsts.React].Urls[PlatformConsts.Urls.UserDetail] = "admin/users/detail";
//options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
//options.Applications["MVC"].Urls[PlatformConsts.Urls.EmailConfirmation] = "Account/Confirm";
//options.Applications["MVC"].Urls[PlatformConsts.Urls.TwoFactor] = "Account/TwoFactor";
//options.Applications["MVC"].Urls[PlatformConsts.Urls.Login] = "Account/Login";
//options.Applications["MVC"].Urls[PlatformConsts.Urls.UserDetail] = "Identity/Users/Detail";
});
}
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(PlatformApplicationModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == "Sozsoft.Platform.ListForms.Administration";
opts.RootPath = "admin";
});
options.ConventionalControllers.Create(typeof(PlatformApplicationModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace != "Sozsoft.Platform.ListForms.Administration";
opts.RootPath = "app";
});
options.ConventionalControllers.Create(typeof(LanguagesApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(SettingsApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(ErpMailQueueModule).Assembly);
options.ConventionalControllers.Create(typeof(NotificationApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(SqlQueryManagerApplicationModule).Assembly);
options.ChangeControllerModelApiExplorerGroupName = false;
options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(PlatformUpdateProfileDto));
options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(UploadFileDto));
});
}
private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
if (hostingEnvironment.IsDevelopment())
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<PlatformDomainSharedModule>(
Path.Combine(hostingEnvironment.ContentRootPath,
$"..{Path.DirectorySeparatorChar}Sozsoft.Platform.Domain.Shared"));
options.FileSets.ReplaceEmbeddedByPhysical<PlatformDomainModule>(
Path.Combine(hostingEnvironment.ContentRootPath,
$"..{Path.DirectorySeparatorChar}Sozsoft.Platform.Domain"));
options.FileSets.ReplaceEmbeddedByPhysical<PlatformApplicationContractsModule>(
Path.Combine(hostingEnvironment.ContentRootPath,
$"..{Path.DirectorySeparatorChar}Sozsoft.Platform.Application.Contracts"));
options.FileSets.ReplaceEmbeddedByPhysical<PlatformApplicationModule>(
Path.Combine(hostingEnvironment.ContentRootPath,
$"..{Path.DirectorySeparatorChar}Sozsoft.Platform.Application"));
options.FileSets.ReplaceEmbeddedByPhysical<ErpMailQueueModule>(
Path.Combine(hostingEnvironment.ContentRootPath,
$"..{Path.DirectorySeparatorChar}Sozsoft.MailQueue"));
});
}
}
private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(configuration["App:CorsOrigins"]?
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray() ?? Array.Empty<string>())
.WithAbpExposedHeaders()
.WithExposedHeaders(["X-Correlation-Id"])
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
private void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAbpSwaggerGenWithOAuth(
configuration["AuthServer:Authority"],
new Dictionary<string, string>
{
{"Platform", "Sozsoft Platform Api"}
},
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Sozsoft Platform Api", Version = "v1" });
options.DocInclusionPredicate((docName, description) =>
{
// DevExpress reporting controller'larını Swagger'dan hariç tut
if (description.ActionDescriptor.RouteValues.TryGetValue("controller", out var controllerName))
{
if (controllerName == "CustomReportDesigner" ||
controllerName == "CustomWebDocumentViewer" ||
controllerName == "QueryBuilder")
{
return false;
}
}
return true;
});
options.CustomSchemaIds(type => type.FullName);
});
}
private void ConfigureIdentity(IConfiguration configuration)
{
//Veritabanından yonetildigi icin bunlar kapatildi
//Configure<IdentityOptions>(options =>
//{
// options.SignIn.RequireConfirmedEmail = true;
// options.SignIn.RequireConfirmedAccount = true;
// options.SignIn.RequireConfirmedPhoneNumber = false;
// options.User.RequireUniqueEmail = true;
// options.Lockout.AllowedForNewUsers = true;
// options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
// options.Lockout.MaxFailedAccessAttempts = 5;
// options.Password.RequiredLength = 6;
// options.Password.RequiredUniqueChars = 0;
// options.Password.RequireNonAlphanumeric = false;
// options.Password.RequireLowercase = false;
// options.Password.RequireUppercase = false;
// options.Password.RequireDigit = false;
//});
Configure<PlatformIdentityOptions>(configuration.GetSection(PlatformConsts.AbpIdentity.GroupName));
}
private void ConfigureCache()
{
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = PlatformConsts.AppName;
});
Configure<RedisCacheOptions>(options =>
{
});
}
private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString(DefaultDatabaseProvider);
if (connectionString.IsNullOrWhiteSpace() || !SetupAppRunner.DatabaseIsReady(configuration))
{
return;
}
Configure<AbpHangfireOptions>(options =>
{
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = ["default", "platform"]
};
});
#pragma warning disable CS0162 // Unreachable code detected
if (DefaultDatabaseProvider == DatabaseProvider.PostgreSql)
{
context.Services.AddHangfire(options =>
{
options.UsePostgreSqlStorage(
storageOptions => storageOptions.UseNpgsqlConnection(connectionString),
new PostgreSqlStorageOptions
{
PrepareSchemaIfNecessary = true
});
});
}
else if (DefaultDatabaseProvider == DatabaseProvider.SqlServer)
{
context.Services.AddHangfire(options =>
{
options.UseSqlServerStorage(
connectionString,
new SqlServerStorageOptions
{
PrepareSchemaIfNecessary = true,
SchemaName = "HangFire",
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
});
});
}
#pragma warning restore CS0162
}
private void ConfigureBlobStoring(IConfiguration configuration)
{
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseFileSystem(fileSystem =>
{
fileSystem.BasePath = configuration["App:CdnPath"];
});
});
});
}
private void ConfigureAuditing()
{
Configure<AbpAspNetCoreAuditingOptions>(options =>
{
options.IgnoredUrls.Add("/api/app/list-form-customization");
});
}
private void ConfigureDynamicServices(ServiceConfigurationContext context)
{
// Dynamic AppService Background Service
context.Services.AddHostedService<DynamicAssemblyRegistrationService>();
// Roslyn Compiler
context.Services.AddSingleton<DynamicServiceCompiler>();
// Action descriptor change provider for runtime controller registration
context.Services.AddSingleton<ActionDescriptorChangeProvider>();
context.Services.AddSingleton<IActionDescriptorChangeProvider>(sp => sp.GetRequiredService<ActionDescriptorChangeProvider>());
// Custom controller activator for dynamic dependency injection
context.Services.AddSingleton<Microsoft.AspNetCore.Mvc.Controllers.IControllerActivator, DynamicControllerActivator>();
}
private void ConfigureDevExpressReporting(ServiceConfigurationContext context, IConfiguration configuration)
{
var logger = context.Services.BuildServiceProvider().GetService<ILogger<PlatformHttpApiHostModule>>();
// Configure fonts before initializing DevExpress controls
DevExpressFontConfiguration.ConfigureDefaultFonts(logger);
DevExpressFontConfiguration.InstallLinuxFonts(logger);
context.Services.AddDevExpressControls();
context.Services.ConfigureReportingServices(configurator =>
{
configurator.ConfigureReportDesigner(designerConfigurator =>
{
designerConfigurator.RegisterDataSourceWizardConfigFileConnectionStringsProvider();
});
configurator.ConfigureWebDocumentViewer(viewerConfigurator =>
{
viewerConfigurator.UseCachedReportSourceBuilder();
});
});
// Register report storage extension
context.Services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
// Setup delegate for dynamic service registration
DynamicServiceCompiler.NotifyAssemblyRegistration = (tenantId, assembly, assemblyName) =>
{
DynamicAssemblyRegistrationService.RequestAssemblyRegistration(tenantId, assembly, assemblyName);
};
// Setup delegate for dynamic service unregistration
DynamicServiceCompiler.NotifyAssemblyUnregistration = (tenantId, serviceName) =>
{
DynamicAssemblyRegistrationService.RequestAssemblyUnregistration(tenantId, serviceName);
};
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAbpRequestLocalization();
// Auth hataları MVC tarafından /Error sayfasına yönlendirilmesin diye bu kaldırıldı
// if (!env.IsDevelopment())
// {
// app.UseErrorPage();
// }
app.UseCorrelationId();
app.MapAbpStaticAssets();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAbpOpenIddictValidation();
app.UseMiddleware<CaptchaMiddleware>();
if (PlatformConsts.IsMultiTenant)
{
app.UseMultiTenancy();
app.UseMiddleware<TenantLocalizationMiddleware>();
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sozsoft Platform Api");
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
c.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
c.OAuthScopes("Platform");
});
app.UseAuditing();
app.UseAbpSerilogEnrichers();
if (env.IsDevelopment())
{
app.UseHangfireDashboard();
}
else
{
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AsyncAuthorization = [new AbpHangfireAuthorizationFilter()]
});
}
app.UseConfiguredEndpoints();
}
public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
{
using var scope = context.ServiceProvider.CreateScope();
var initializer = scope.ServiceProvider.GetRequiredService<BackgroundWorkerInitializer>();
await initializer.RunAsync();
}
}