using AutoMapper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using System; using System.Text.Json.Serialization; using Timeline.Auth; using Timeline.Configs; using Timeline.Entities; using Timeline.Formatters; using Timeline.Helpers; using Timeline.Models.Converters; using Timeline.Routes; using Timeline.Services; namespace Timeline { public class Startup { private readonly bool disableFrontEnd; public Startup(IConfiguration configuration, IWebHostEnvironment environment) { Environment = environment; Configuration = configuration; disableFrontEnd = Configuration.GetValue(ApplicationConfiguration.DisableFrontEndKey) ?? false; } public IWebHostEnvironment Environment { get; } public 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.AddControllers(setup => { setup.InputFormatters.Add(new StringInputFormatter()); setup.UseApiRoutePrefix("api"); }) .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = true; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()); }) .ConfigureApiBehaviorOptions(options => { options.InvalidModelStateResponseFactory = InvalidModelResponseFactory.Factory; }); services.Configure(Configuration.GetSection("Jwt")); services.AddAuthentication(AuthenticationConstants.Scheme) .AddScheme(AuthenticationConstants.Scheme, AuthenticationConstants.DisplayName, o => { }); services.AddAuthorization(); services.AddScoped(); services.AddAutoMapper(GetType().Assembly); services.AddTransient(); services.AddTransient(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddUserAvatarService(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.TryAddSingleton(); services.AddDbContext((services, options) => { var pathProvider = services.GetRequiredService(); options.UseSqlite($"Data Source={pathProvider.GetDatabaseFilePath()}"); }); if (!disableFrontEnd && !Environment.IsDevelopment()) { services.AddSpaStaticFiles(config => { config.RootPath = "ClientApp/dist"; }); } } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { if (string.Equals(System.Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"), "true", StringComparison.OrdinalIgnoreCase)) { var options = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }; // Only loopback proxies are allowed by default. // Clear that restriction because forwarders are enabled by explicit // configuration. options.KnownNetworks.Clear(); options.KnownProxies.Clear(); app.UseForwardedHeaders(options); } app.UseRouting(); if (!disableFrontEnd && !Environment.IsDevelopment()) { app.UseSpaStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true }); } app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); if (!disableFrontEnd) { app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; if (Environment.IsDevelopment()) { if (Configuration.GetValue(ApplicationConfiguration.FrontEndProxyOnlyKey) ?? false) { spa.UseProxyToSpaDevelopmentServer(new UriBuilder("http", "localhost", 3000).Uri); } else { SpaServices.SpaDevelopmentServerMiddlewareExtensions.UseSpaDevelopmentServer(spa, packageManager: "yarn", npmScript: "install-and-start", port: 3000); } } }); } } } }