global using System; global using System.Linq; global using System.IO; global using System.Net.Mail; global using System.Net; global using System.Threading; global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Runtime.Serialization; global using System.ComponentModel.DataAnnotations.Schema; global using System.Security.Claims; global using System.Text.Json; global using System.Text.Json.Serialization; global using IOL.GreatOffice.Api.Data.Database; global using IOL.GreatOffice.Api.Data.Exceptions; global using IOL.GreatOffice.Api.Data.Dtos; global using IOL.GreatOffice.Api.Data.Enums; global using IOL.GreatOffice.Api.Data.Models; global using IOL.GreatOffice.Api.Data.Results; global using IOL.Helpers; global using Microsoft.OpenApi.Models; global using Microsoft.AspNetCore.Authentication.Cookies; global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.DataProtection; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Http; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; global using Serilog; global using IOL.GreatOffice.Api.Data; global using IOL.GreatOffice.Api.Data.Static; global using IOL.GreatOffice.Api.Services; global using IOL.GreatOffice.Api.Utilities; using System.Reflection; using System.Security.Cryptography.X509Certificates; using IOL.GreatOffice.Api.Endpoints.V1; using IOL.GreatOffice.Api.Jobs; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc.Versioning; using Quartz; namespace IOL.GreatOffice.Api; public static class Program { public static WebApplicationBuilder CreateAppBuilder(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddLogging(); builder.Services.AddHttpClient(); builder.Services.AddMemoryCache(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddTransient(); var vaultService = builder.Services.BuildServiceProvider().GetRequiredService(); var configuration = vaultService.GetCurrentAppConfiguration(); var logger = new LoggerConfiguration() .Enrich.FromLogContext() .ReadFrom.Configuration(builder.Configuration) .WriteTo.Console(); if (!builder.Environment.IsDevelopment() && configuration.SEQ_API_KEY.HasValue() && configuration.SEQ_API_URL.HasValue()) { logger.WriteTo.Seq(configuration.SEQ_API_URL, apiKey: configuration.SEQ_API_KEY); } Log.Logger = logger.CreateLogger(); Log.Information("Starting web host, " + JsonSerializer.Serialize(configuration.GetPublicVersion(), new JsonSerializerOptions() { WriteIndented = true })); builder.Host.UseSerilog(Log.Logger); builder.WebHost.ConfigureKestrel(kestrel => { kestrel.AddServerHeader = false; }); if (builder.Environment.IsDevelopment()) { builder.Services.AddCors(); } if (builder.Environment.IsProduction()) { builder.Services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; }); } builder.Services .AddDataProtection() .PersistKeysToDbContext() .ProtectKeysWithCertificate(vaultService.Get("")); builder.Services.Configure(JsonSettings.Default); builder.Services.AddQuartz(options => { options.UsePersistentStore(o => { o.UsePostgres(builder.Configuration.GetQuartzDatabaseConnectionString(vaultService.GetCurrentAppConfiguration)); o.UseSerializer(); }); options.UseMicrosoftDependencyInjectionJobFactory(); options.RegisterJobs(); }); builder.Services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; }); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.Cookie.Name = "go_session"; options.Cookie.SameSite = SameSiteMode.Strict; options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = builder.Environment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always; options.Cookie.IsEssential = true; options.SlidingExpiration = true; options.Events.OnRedirectToAccessDenied = options.Events.OnRedirectToLogin = c => { c.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.FromResult(null); }; }) .AddGitHub(options => { options.ClientSecret = configuration.GITHUB_CLIENT_SECRET; options.ClientId = configuration.GITHUB_CLIENT_ID; options.SaveTokens = true; options.CorrelationCookie.Name = "gh_correlation"; options.CorrelationCookie.SameSite = SameSiteMode.Lax; options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always; options.CorrelationCookie.HttpOnly = true; options.Events.OnCreatingTicket = context => GithubAuthenticationHelpers.HandleGithubTicketCreation(context, builder.Configuration, configuration); }) .AddScheme(AppConstants.BASIC_AUTH_SCHEME, default); builder.Services.AddDbContext(options => { options.UseNpgsql(builder.Configuration.GetAppDatabaseConnectionString(vaultService.GetCurrentAppConfiguration), npgsqlDbContextOptionsBuilder => { npgsqlDbContextOptionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); npgsqlDbContextOptionsBuilder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), default); }) .UseSnakeCaseNamingConvention(); if (builder.Environment.IsDevelopment()) { options.EnableSensitiveDataLogging(); } }); builder.Services.AddApiVersioning(options => { options.ApiVersionReader = new UrlSegmentApiVersionReader(); options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = false; }); builder.Services.AddVersionedApiExplorer(options => { options.SubstituteApiVersionInUrl = true; }); builder.Services.AddSwaggerGen(options => { options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, Assembly.GetExecutingAssembly().GetName().Name + ".xml")); options.UseApiEndpoints(); options.OperationFilter(); options.SwaggerDoc(ApiSpecV1.Document.VersionName, ApiSpecV1.Document.OpenApiInfo); options.AddSecurityDefinition("Basic", new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Basic", BearerFormat = "Basic", In = ParameterLocation.Header, Description = "Enter your token in the text input below.\r\n\r\nExample: \"Basic 12345abcdef\"", }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Basic" } }, Array.Empty() } }); }); builder.Services .AddControllers() .AddJsonOptions(JsonSettings.Default); return builder; } public static WebApplication CreateWebApplication(WebApplicationBuilder builder) { var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseCors(cors => { cors.AllowAnyMethod(); cors.AllowAnyHeader(); cors.WithOrigins("http://localhost:3000", "http://localhost:3002", "http://localhost:3001"); cors.AllowCredentials(); }); } if (app.Environment.IsProduction()) { app.UseForwardedHeaders(); } app.UseDefaultFiles() .UseStaticFiles() .UseRouting() .UseSerilogRequestLogging() .UseStatusCodePages() .UseAuthentication() .UseAuthorization() .UseSwagger() .UseSwaggerUI(options => { options.SwaggerEndpoint(ApiSpecV1.Document.SwaggerPath, ApiSpecV1.Document.VersionName); options.DocumentTitle = AppConstants.API_NAME; }) .UseEndpoints(endpoints => { endpoints.MapControllers(); }); return app; } public static int Main(string[] args) { try { CreateWebApplication(CreateAppBuilder(args)).Run(); return 0; } catch (Exception ex) { // This is subject to change in future .net versions, see https://github.com/dotnet/runtime/issues/60600. if (ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) { throw; } Log.Fatal(ex, "Unhandled exception"); return 1; } finally { Log.Information("Shut down complete, flusing logs..."); Log.CloseAndFlush(); } } }