Files
CMS/src/CMSMicroservice.WebApi/Program.cs

214 lines
7.6 KiB
C#

using CMSMicroservice.Infrastructure.Persistence;
using CMSMicroservice.Infrastructure.Data.Seeding;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Serilog.Core;
using Serilog;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.OpenApi.Models;
using CMSMicroservice.WebApi.Common.Behaviours;
using Hangfire;
using Hangfire.SqlServer;
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
builder.WebHost.ConfigureKestrel(options =>
{
// Setup a HTTP/2 endpoint without TLS.
options.ListenLocalhost(5000, o => o.Protocols =
HttpProtocols.Http2);
});
}
var levelSwitch = new LoggingLevelSwitch();
// Read Seq configuration from appsettings.json
var seqServerUrl = builder.Configuration["Seq:ServerUrl"] ?? "http://seq-svc:5341";
var seqApiKey = builder.Configuration["Seq:ApiKey"];
var logger = new LoggerConfiguration()
.WriteTo.Console()
//.WriteTo.MSSqlServer(builder.Configuration.GetConnectionString("LogConnection"),
// sinkOptions: new MSSqlServerSinkOptions
// {
// TableName = "LogCMSEvents",
// SchemaName = "Log",
// AutoCreateSqlTable = true
// })
.WriteTo.Seq(seqServerUrl,
apiKey: string.IsNullOrEmpty(seqApiKey) ? null : seqApiKey,
controlLevelSwitch: levelSwitch)
.CreateLogger();
builder.Logging.AddSerilog(logger);
#if DEBUG
Serilog.Debugging.SelfLog.Enable(msg => Console.WriteLine(msg));
#endif
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
// Add services to the container.
builder.Services.AddGrpc(options =>
{
options.Interceptors.Add<LoggingBehaviour>();
options.Interceptors.Add<PerformanceBehaviour>();
//options.Interceptors.Add<ExceptionHandlingBehaviour>();
options.EnableDetailedErrors = true;
options.MaxReceiveMessageSize = 1000 * 1024 * 1024; // 1 GB
options.MaxSendMessageSize = 1000 * 1024 * 1024; // 1 GB
}).AddJsonTranscoding();
builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices(builder.Configuration);
builder.Services.AddPresentationServices(builder.Configuration);
builder.Services.AddProtobufServices();
#region Configure Hangfire
builder.Services.AddHangfire(config => config
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration["ConnectionStrings:DefaultConnection"]));
builder.Services.AddHangfireServer();
#endregion
#region Configure Health Checks
builder.Services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>("database");
#endregion
// Add Controllers for REST APIs
builder.Services.AddControllers();
#region Configure Cors
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll",
builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Grpc-Status",
"Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding", "validation-errors-text"));
});
#endregion
builder.Services.AddGrpcSwagger();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "gRPC transcoding", Version = "v1" });
c.CustomSchemaIds(type=>type.ToString());
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
// Initialise and seed database
using (var scope = app.Services.CreateScope())
{
var initialiser = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitialiser>();
await initialiser.InitialiseAsync();
await initialiser.SeedAsync();
// Run Migration: ParentId → NetworkParentId (فقط یکبار اجرا می‌شود)
var migrationLogger = scope.ServiceProvider.GetRequiredService<ILogger<NetworkParentIdMigrationSeeder>>();
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var migrationSeeder = new NetworkParentIdMigrationSeeder(dbContext, migrationLogger);
await migrationSeeder.SeedAsync();
}
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
// Map Health Check endpoints
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = _ => false
});
app.MapControllers();
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true }); // Configure the HTTP request pipeline.
app.ConfigureGrpcEndpoints(Assembly.GetExecutingAssembly(), endpoints =>
{
// endpoints.MapGrpcService<ExampleService>();
});
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
// Configure Hangfire Dashboard
app.UseHangfireDashboard("/hangfire", new Hangfire.DashboardOptions
{
// TODO: برای production از Authorization filter استفاده کنید
Authorization = Array.Empty<Hangfire.Dashboard.IDashboardAuthorizationFilter>()
});
// Configure Recurring Jobs
using (var scope = app.Services.CreateScope())
{
var recurringJobManager = scope.ServiceProvider.GetRequiredService<IRecurringJobManager>();
// Weekly Commission Calculation: Every Sunday at 00:05 (UTC)
recurringJobManager.AddOrUpdate<CMSMicroservice.Infrastructure.BackgroundJobs.WeeklyCommissionJob>(
recurringJobId: "weekly-commission-calculation",
methodCall: job => job.ExecuteAsync(null, CancellationToken.None),
cronExpression: "5 0 * * 0", // Sunday at 00:05
options: new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Utc
});
app.Logger.LogInformation("✅ Hangfire recurring job 'weekly-commission-calculation' registered (Cron: 5 0 * * 0 - Sunday 00:05 UTC)");
// Daya Loan Check: Every 15 minutes
CMSMicroservice.WebApi.Workers.DayaLoanCheckWorker.Schedule(recurringJobManager);
app.Logger.LogInformation("✅ Hangfire recurring job 'daya-loan-check' registered (Cron: */15 * * * * - Every 15 minutes)");
}
app.Run();