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; } // 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 DirectoryInfo(AppPaths.DataProtectionKeys.HostPath)); StartupTasks.Execute(); if (WebHostEnvironment.IsDevelopment()) { services.AddCors(); } services.Configure(AppJsonSettings.Default); services.AddDbContext(options => { options.UseNpgsql(ConnectionStrings.AppDatabaseConnectionString(Configuration), builder => { builder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), default); }) .UseSnakeCaseNamingConvention(); if (WebHostEnvironment.IsDevelopment()) { options.EnableSensitiveDataLogging(); } }); services.AddQuartz(options => { options.UsePersistentStore(o => { o.UsePostgres(ConnectionStrings.QuartzDatabaseConnectionString(Configuration)); o.UseSerializer(); }); options.UseMicrosoftDependencyInjectionJobFactory(); options.RegisterJobs(); }); services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; }); services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = 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(default); }; }) .AddGitHub(options => { options.ClientSecret = Configuration.GetValue("GH_CLIENT_SECRET"); options.ClientId = Configuration.GetValue("GH_CLIENT_ID"); options.SaveTokens = true; options.CorrelationCookie = new CookieBuilder { Name = "gh_correlation", SameSite = SameSiteMode.Lax, SecurePolicy = CookieSecurePolicy.Always, HttpOnly = true, }; options.Events.OnCreatingTicket = context => HandleGithubCreatingTicket.Handle(context, Configuration); }) .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() .WithOrigins("http://localhost:3000") .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; }); } }