diff options
| author | ivarlovlie <git@ivarlovlie.no> | 2023-02-25 13:15:44 +0100 |
|---|---|---|
| committer | ivarlovlie <git@ivarlovlie.no> | 2023-02-25 13:15:44 +0100 |
| commit | 900bb5e845c3ad44defbd427cae3d44a4a43321f (patch) | |
| tree | df3d96a93771884add571e82336c29fc3d9c7a1c /code/api/src/Program.cs | |
| download | greatoffice-900bb5e845c3ad44defbd427cae3d44a4a43321f.tar.xz greatoffice-900bb5e845c3ad44defbd427cae3d44a4a43321f.zip | |
feat: Initial commit
Diffstat (limited to 'code/api/src/Program.cs')
| -rw-r--r-- | code/api/src/Program.cs | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/code/api/src/Program.cs b/code/api/src/Program.cs new file mode 100644 index 0000000..3da1111 --- /dev/null +++ b/code/api/src/Program.cs @@ -0,0 +1,249 @@ +global using System; +global using System.Linq; +global using System.IO; +global using System.Threading; +global using System.Threading.Tasks; +global using System.Collections.Generic; +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.Enums; +global using IOL.GreatOffice.Api.Data.Models; +global using IOL.GreatOffice.Api.Data.Static; +global using IOL.GreatOffice.Api.Services; +global using IOL.GreatOffice.Api.Utilities; +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.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.Localization; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Serilog; +global using Quartz; +global using IOL.GreatOffice.Api.Resources; +using IOL.GreatOffice.Api.Endpoints.V1; +using IOL.GreatOffice.Api.Jobs; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Localization; +using Microsoft.AspNetCore.Mvc.Versioning; +using Serilog.Events; + +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<MailService>(); + builder.Services.AddScoped<PasswordResetService>(); + builder.Services.AddScoped<UserService>(); + builder.Services.AddScoped<TenantService>(); + builder.Services.AddScoped<EmailValidationService>(); + builder.Services.AddSingleton<VaultService>(); + builder.Services.AddHttpClient<VaultService>(); + builder.Services.AddHttpClient<MailService>(); + var vaultService = builder.Services.BuildServiceProvider().GetRequiredService<VaultService>(); + var configuration = vaultService.GetCurrentAppConfiguration(); + var logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .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); + + if (builder.Environment.IsDevelopment()) { + builder.Services.AddCors(); + } + + if (builder.Environment.IsProduction()) { + builder.Services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; }); + } + + builder.Services.AddLocalization(); + builder.Services.AddRequestLocalization(options => { + var supportedCultures = new[] {"en", "nb"}; + options.SetDefaultCulture(supportedCultures[0]) + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures); + options.ApplyCurrentCultureToResponseHeaders = true; + }); + + builder.Services.Configure<RequestLocalizationOptions>(options => { + options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context => + // Get culture from specific cookie + await Task.FromResult(new ProviderCultureResult(context.Request.Cookies[AppCookies.Locale] ?? "en"))) + ); + }); + + builder.Services + .AddDataProtection() + .ProtectKeysWithCertificate(configuration.CERT1()) + .PersistKeysToDbContext<MainAppDatabase>(); + + builder.Services.Configure(JsonSettings.Default); + builder.Services.AddQuartz(options => { + options.UsePersistentStore(o => { + o.UsePostgres(builder.Configuration.GetQuartzDatabaseConnectionString(vaultService.GetCurrentAppConfiguration)); + o.UseSerializer<QuartzJsonSerializer>(); + }); + 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 = AppCookies.Session; + options.Cookie.Domain = builder.Environment.IsDevelopment() ? "localhost" : ".greatoffice.app"; + options.Cookie.HttpOnly = true; + options.Cookie.IsEssential = true; + options.SlidingExpiration = true; + options.Events.OnRedirectToAccessDenied = + options.Events.OnRedirectToLogin = c => { + c.Response.StatusCode = StatusCodes.Status401Unauthorized; + return Task.FromResult<object>(null); + }; + }) + .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>(AppConstants.BASIC_AUTH_SCHEME, default); + + builder.Services.AddDbContext<MainAppDatabase>(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, "IOL.GreatOffice.Api.xml")); + options.UseApiEndpoints(); + options.OperationFilter<SwaggerDefaultValues>(); + options.OperationFilter<PaginationOperationFilter>(); + 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<string>() + } + }); + }); + + builder.Services.AddPagination(options => { + options.DefaultSize = 50; + options.MaxSize = 100; + options.CanChangeSizeFromQuery = true; + }); + + builder.Services + .AddControllers() + .AddDataAnnotationsLocalization() + .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.SetIsOriginAllowed(_ => true); + cors.AllowCredentials(); + cors.WithExposedHeaders(AppHeaders.IS_KNOWN_PROBLEM); + }); + } + + if (app.Environment.IsProduction()) { + app.UseForwardedHeaders(); + } + + app.UseDefaultFiles() + .UseStaticFiles() + .UseRequestLocalization() + .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) { + Log.Fatal(ex, "Unhandled exception"); + return 1; + } + finally { + Log.Information("Shut down complete, flushing logs..."); + Log.CloseAndFlush(); + } + } +}
\ No newline at end of file |
