using System.Reflection; namespace IOL.BookmarkThing.Server; public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; } private IWebHostEnvironment WebHostEnvironment { get; } private IConfiguration Configuration { get; } private string AppDatabaseConnectionString() { var host = Configuration.GetValue("DB_HOST"); var port = Configuration.GetValue("DB_PORT"); var database = Configuration.GetValue("DB_NAME"); var user = Configuration.GetValue("DB_USER"); var password = Configuration.GetValue("DB_PASSWORD"); return $"Server={host};Port={port};Database={database};User Id={user};Password={password}"; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDataProtection() .PersistKeysToFileSystem(new(AppPaths.DataProtectionKeys.HostPath)); StartupTasks.Execute(); if (WebHostEnvironment.IsDevelopment()) { services.AddCors(); } services.Configure(AppJsonSettings.Default); services.AddDbContext(options => { options.UseNpgsql(AppDatabaseConnectionString(), builder => { builder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), default); }) .UseSnakeCaseNamingConvention(); if (WebHostEnvironment.IsDevelopment()) { options.EnableSensitiveDataLogging(); } }); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.Name = "bookmarkthing_session"; options.Cookie.SameSite = SameSiteMode.Strict; options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.Cookie.IsEssential = true; options.SlidingExpiration = true; options.Events.OnRedirectToAccessDenied = options.Events.OnRedirectToLogin = c => { c.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.FromResult(null); }; }) .AddScheme(Constants.BASIC_AUTH_SCHEME, default); services.AddLogging(); services.AddHttpClient(); services.AddControllers() .AddJsonOptions(AppJsonSettings.Default); services.AddApiVersioning(options => { options.ApiVersionReader = new UrlSegmentApiVersionReader(); options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = false; }); services.AddVersionedApiExplorer(options => { options.SubstituteApiVersionInUrl = true; }); 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() } }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { if (WebHostEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseCors(x => x .AllowAnyMethod() .AllowAnyHeader() .SetIsOriginAllowed(_ => true) // allow any origin .AllowCredentials()); // allow credentials } app.UseRouting(); app.UseSerilogRequestLogging(); app.UseStatusCodePages(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint(ApiSpecV1.Document.SwaggerPath, ApiSpecV1.Document.VersionName); options.DocumentTitle = Constants.API_NAME; }); } }