Files

992 lines
49 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Aeroflot.Common.ApiWrappers;
using Aeroflot.Common2.ApiWrapper.Aeroflot;
using Aeroflot.Common2.ApiWrapper.Aeroflot.FallbackService;
using Aeroflot.Common2.ApiWrapper.Aeroflot.Proxies;
using Aeroflot.Common2.DataAccess.S3;
using Aeroflot.Common2.Extensions;
using Aeroflot.Common2.Observability.AspNetCore;
using Aeroflot.Flights.BL;
using Aeroflot.Flights.BL.Helpers;
using Aeroflot.Flights.BL.Models;
using Aeroflot.Flights.BL.Models.FlightFilter;
using Aeroflot.Flights.DataSpace;
using Aeroflot.Flights.Interfaces.Models;
using Aeroflot.Flights.WebApi;
using Aeroflot.Flights.WebApi.Models;
using Aeroflot.Flights.WebApi.Models.Onlineboard.ApiV1;
using Aeroflot.Flights.WebApi.Providers;
//using Aeroflot.Flights.WebApi.SignalR;
using Aeroflot.Security2;
using Aeroflot.Security2.DataAccess;
using Aeroflot.Security2.DataSpace;
using Aeroflot.Security2.Entities;
using Aeroflot.Security2.Entities.DataSpace;
using Aeroflot.Security2.Services.EmailService;
using Amazon.Runtime;
using Amazon.S3;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
//using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using Prometheus;
using Serilog;
namespace Aeroflot.Flights.Web
{
public class Startup
{
private const string UserAgent = "Aeroflot.Flights / 1.0.001"; //"Aeroflot.Passbook / 1.7.013";
//private IHubContext<FlightHub> hubContext;
public Startup(IWebHostEnvironment env)
{
Environment = env;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public class AuthMessageHandler : DelegatingHandler
{
public AuthMessageHandler()
{
InnerHandler = new HttpClientHandler();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("User-Agent", UserAgent);
return base.SendAsync(request, cancellationToken);
}
}
public void ConfigureServices(IServiceCollection services)
{
var keysFolder = System.IO.Path.Combine(Environment.ContentRootPath, "App_Data");
services.AddDataProtection()
.SetApplicationName("Flights")
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
.SetDefaultKeyLifetime(TimeSpan.FromDays(36500)) // 100 years
.DisableAutomaticKeyGeneration();
//IdentityModelEventSource.ShowPII = true;
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSpaStaticFiles(config =>
{
config.RootPath = "ClientApp/dist";
});
//var apiAssembly = typeof(WebApi.Controllers.FlightsController).Assembly;
services.AddMvc()
.AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
opt.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
opt.AllowInputFormatterExceptionMessages = true;
})
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
services.AddSession();
services.AddResponseCaching();
services.AddControllers(options =>
{
options.CacheProfiles.Add(
"DefaultBoard",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultBoard"], out int cacheBoardSeconds) ? cacheBoardSeconds : 60,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"DefaultDictionary",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultDictionary"], out int cacheDictionarySeconds) ? cacheDictionarySeconds : 3600,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"DefaultSchedule",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultSchedule"], out int cacheScheduleSeconds) ? cacheScheduleSeconds : 600,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"DefaultDetails",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultDetails"], out int cacheDetailsSeconds) ? cacheDetailsSeconds : 30,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"DefaultAirport",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultAirport"], out int cacheAirportSeconds) ? cacheAirportSeconds : 600,
Location = ResponseCacheLocation.Any,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"Default1HourBasedOnQueryKeys",
new CacheProfile()
{
Duration = 3600,
VaryByQueryKeys = new[] { "*" },
});
options.CacheProfiles.Add(
"DefaultRequests",
new CacheProfile()
{
Duration = int.TryParse(Configuration["Options:Cache:DefaultRequest"], out int cacheRequestsSeconds) ? cacheRequestsSeconds : 600,
VaryByQueryKeys = new[] { "*" },
});
})
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
services.AddWebApi();
services.AddMemoryCache();
services.AddSwaggerGen(c =>
{
//c.TagActionsBy(tagSelector => tagSelector.);
c.CustomSchemaIds(type => type.FullName.ToString());
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Aeroflot Flights", Version = "v1" });
c.AddSecurityDefinition(
"Bearer",
new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
},
});
c.DescribeAllParametersInCamelCase();
});
services.Configure<SwaggerBasicAuthConfig>(Configuration.GetSection("Swagger"));
services.AddEndpointsApiExplorer();
//ConfigureAuthService(services, Configuration);
#region //ToDo: refactor this
//var connectionString = Configuration.GetConnectionString("ConnectionString");
//services.AddDbContext<FlightDbContext>(
// builder =>
// builder.EnableSensitiveDataLogging().UseSqlServer(connectionString, options => options.EnableRetryOnFailure()),
// ServiceLifetime.Transient);
//services.AddDbContext<SecurityDbContext>(
// builder =>
// builder.UseSqlServer(connectionString, options => options.EnableRetryOnFailure()),
// ServiceLifetime.Transient);
var scheduleAppUri = new Uri(Configuration["Api:ScheduleAppUri"]);
var scheduleV2AppUri = new Uri(Configuration["Api:ScheduleV2AppUri"]);
Func<string, Uri> uriAccessor = key =>
{
switch (key)
{
case Common.ApiWrappers.Schedule.Schedule.AppUri:
return scheduleAppUri;
case Common.ApiWrappers.Schedule.Schedule.AppV2Uri:
return scheduleV2AppUri;
default:
throw new ArgumentOutOfRangeException();
}
};
services.AddSingleton(uriAccessor);
//services.AddSingleton(Options.Create<FlightsApiWrapperSettings>(new FlightsApiWrapperSettings { FlightsUri = new Uri(Configuration["Dictionary:FlightsApiAppUri"]) }));
#endregion
services.Configure<AflApiProviderOptions>(Configuration.GetSection("Options:RefreshableApiProvider"));
services.Configure<ApiWrapperProxyOptions>(Configuration.GetSection("Options:ApiWrapperProxy"));
services.Configure<DatesProviderOptions>(Configuration.GetSection("Options:DatesProviderOptions"));
services.Configure<AeroflotApiWrapperOptions>(Configuration.GetSection("Options:AeroflotApiWrapper"));
services.Configure<BL.Models.TransitionProviderOptions>(Configuration.GetSection("Options:TransitionProvider"));
services.Configure<BL.Models.LegServiceOptions>(Configuration.GetSection("Options:LegService"));
services.Configure<ConnectionsProviderOptions>(Configuration.GetSection("Options:ConnectionsProvider"));
services.Configure<ValidationOptions>(Configuration.GetSection("Options:Validation"));
services.Configure<WhiteListCarriersOptions>(Configuration.GetSection("Options:WhiteListCarriers"));
Enum.TryParse<FallbackServiceType>(Configuration.GetSection("Options:AeroflotFallbackOptions:ServiceType").Value, true, out FallbackServiceType serviceType);
var options = Configuration.GetAWSOptions("YandexAWS:AWS");
options.Credentials = new BasicAWSCredentials(Configuration.GetSection("YandexAWS:AWS:AccessKey").Value, Configuration.GetSection("YandexAWS:AWS:SecretKey").Value);
services.AddDefaultAWSOptions(options);
services.AddAWSService<IAmazonS3>();
services.AddSingleton<IS3BlobContainer<string>>(sp => ActivatorUtilities.CreateInstance<S3BlobContainer<string>>(sp, Configuration.GetSection("YandexAWS:Bucket").Value));
services.AddSingleton<IS3BlobContainer<byte[]>>(sp => ActivatorUtilities.CreateInstance<S3BlobContainer<byte[]>>(sp, Configuration.GetSection("YandexAWS:Bucket").Value));
services.AddOptions<FlightFilterProviderOptions>().Bind(Configuration.GetSection("Options:FlightFilter"));
services.AddOptions<HeadersOptions>().Bind(Configuration.GetSection("HeadersOptions"));
services.AddBlWeb(serviceType);
//add dataspace
services.AddFlightsClientGraphQL(Configuration["DataSpace:FlightsModelEndpoint"]);
services.AddSecurity2ClientGraphQL(Configuration["DataSpace:FlightsModelEndpoint"]);
//services.AddScoped<IdentityRepository, IdentityRepository>(); // (_ => new IdentityRepository((IAsyncGenericRepository)_.GetService(typeof(AsyncGenericRepository))));
//services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
//services.AddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
//services.AddScoped<IdentityErrorDescriber, IdentityErrorDescriber>();
//services.AddScoped<UserManager, UserManager>();
services.AddScoped<IIdentityRepository, IdentityRepository>();
services.AddScoped<ISearchFlightParamsBinder, SearchFlightParamsBinder>();
services.AddAeroflotIdentityCore<User>(o =>
{
o.User.RequireUniqueEmail = true;
o.User.AllowedUserNameCharacters = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗКИЙКЛМНОПРСТУФЧЦЧШЩЪЫЬЭЮЯ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
o.Lockout.MaxFailedAccessAttempts = 7;
})
.AddDefaultTokenProviders();
services.AddDefaultPasswordValidators<User>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<Security2.Entities.User>>();
var configEws = Configuration.GetSection("EwsServiceOptions");
var ewsOptions = new EWSOptions()
{
AsmxUri = configEws.GetValue<string>("EwsAsmxUri"),
Password = configEws.GetValue<string>("EwsPassword"),
Username = configEws.GetValue<string>("EwsUserName"),
};
var configEmail = Configuration.GetSection("EmailOptions");
services.AddEWSEmailService(new EmailOptions()
{
AppName = configEmail.GetValue<string>("AppName"),
AppUrl = configEmail.GetValue<string>("AppUrl"),
InvitationUrl = configEmail.GetValue<string>("InvitationUrl"),
EWSOptions = ewsOptions,
});
services.TryAddScoped<IUserConfirmation<Security2.Entities.User>, DefaultUserConfirmation<Security2.Entities.User>>();
services.TryAddScoped<SignInManager<Security2.Entities.User>>();
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue;
});
services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = Configuration["Identity:AuthorityUri"];
options.Audience = Configuration["Identity:Audience"];
options.RequireHttpsMetadata = false;
options.BackchannelHttpHandler = new AuthMessageHandler();
})
.AddIdentityCookies(o =>
{
if (Environment.IsDevelopment())
{
o.ApplicationCookie.Configure(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
o.ExternalCookie.Configure(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.AddPolicy("policyjwt", policy =>
{
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
});
options.AddPolicy("policycookie", policy =>
{
policy.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme)
.RequireAuthenticatedUser();
});
});
services.AddHealthChecks();
services.AddCors(options =>
{
options.AddPolicy(
"Aeroflot",
builder =>
{
builder
.WithOrigins("http://*.aeroflot.ru", "https://*.aeroflot.ru", "http://aeroflot.baccasoft.ru", "https://aeroflot.baccasoft.ru", "http://*.test.aeroflot.ru", "https://*.test.aeroflot.ru")
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
options.AddPolicy(
"AllowAnyHeader",
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
//services.AddTransient<IFlightHubClient>(c => new FlightHubClient(hubContext));
//services.AddSignalR();
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Robots", "/robots.txt");
options.Conventions.AddPageRoute("/Sitemap", "/sitemap.xml");
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardLimit = 3;
foreach (var ip in Configuration.GetSection("IpSettings").Get<List<IpOptions>>())
{
options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse(ip.Ip), ip.Mask));
}
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
services.AddOpenTelemetryAspNetCore(Configuration["OpenTelemetry:ServiceName"], Configuration["OpenTelemetry:ServiceNamespace"], Configuration["OpenTelemetry:OtlpExporterEndpoint"]);
services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(o =>
{
o.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
o.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// DataSpace ids are 64 bit and need to be strings in Json
// ints in Json has only about 53bit precision
o.SerializerOptions.NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString;
o.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory logger)
{
app.UseForwardedHeaders();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
app.UseHealthChecks("/healthy");
app.UseResponseCaching();
app.UseStaticFiles();
//if (!env.IsDevelopment())
//{
// app.UseSpaStaticFiles();
//}
app.UseSpaStaticFiles();
app.Use(async (context, next) =>
{
context.Request.EnableBuffering();
//var hubContext = context.RequestServices
// .GetRequiredService<IHubContext<FlightHub>>();
//this.hubContext = hubContext;
await next.Invoke();
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("Aeroflot");
app.UseHttpMetrics();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseSerilogRequestLogging();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet(
"api/v{version}/{lang}/airports", (
[FromServices] WebApi.Controllers.Api.AirportController controller,
HttpContext httpContext,
[FromRoute] double version,
[FromRoute] CaseInsensitive<Lang> lang) =>
controller.GetAirports(httpContext, version, lang.Value, "json")).Produces<WebApi.Models.Response<List<AirportModel>>>(StatusCodes.Status200OK)
.CacheOutput("DefaultAirport").WithTags("Airports").WithSummary("Get Airports");
endpoints.MapGet(
"api/v{version}/{lang}/airports.{format}", (
[FromServices] WebApi.Controllers.Api.AirportController controller,
HttpContext httpContext,
[FromRoute] double version,
[FromRoute] CaseInsensitive<Lang> lang,
[FromRoute] string format) =>
controller.GetAirports(httpContext, version, lang.Value, format)).Produces<WebApi.Models.Response<List<AirportModel>>>(StatusCodes.Status200OK)
.CacheOutput("DefaultAirport").WithTags("Airports").WithSummary("Get Airports");
endpoints.MapGet(
"api/v{version}/{lang}/airports/pairs", (
[FromServices] WebApi.Controllers.Api.AirportController controller,
HttpContext httpContext,
[FromRoute] double version,
[FromRoute] CaseInsensitive<Lang> lang) =>
controller.GetAirportPairs(httpContext, version, lang.Value)).Produces<WebApi.Models.Response<WrapperAirlineProfileRS>>(StatusCodes.Status200OK)
.CacheOutput("DefaultAirport").WithTags("Airports").WithSummary("Get Airports");
endpoints.MapGet(
"/api/AppSettings",
([FromServices] WebApi.Controllers.App.AppSettingsController controller) =>
controller.Get()).Produces<string>(StatusCodes.Status200OK)
.WithTags("AppSettings").WithSummary("Get App settings");
endpoints.MapPost(
"/api/Meal/checkspecial",
([FromServices] WebApi.Controllers.App.MealController controller,
[FromBody] MealApiRequestModel model) =>
controller.CheckMealAsync(model)).Produces<bool>(StatusCodes.Status200OK)
.WithTags("Meal").WithSummary("Get meal information");
endpoints.MapGet(
"api/v{version}/{language}/delays/statistics", (
[FromServices] WebApi.Controllers.Api.DelaysController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[FromRoute] double version,
[FromRoute] string language) =>
controller.GetDelayStatistics(httpContext, user, version, language)).Produces<string>(StatusCodes.Status200OK)
.WithTags("Delays").WithSummary("Get delays statistics");
endpoints.MapGet(
"api/Healthcheck", (
[FromServices] WebApi.Controllers.App.HealthcheckController controller,
HttpContext httpContext,
ClaimsPrincipal user) =>
controller.Get()).Produces<string>(StatusCodes.Status200OK)
.WithTags("Healthcheck").WithSummary("Health check");
endpoints.MapGet(
"api/Dictionary/{version}/{dictionaryName}", (
[FromServices] WebApi.Controllers.App.DictionaryController controller,
HttpContext httpContext,
ClaimsPrincipal user,
string dictionaryName,
string version) =>
controller.GetData(dictionaryName, version)).Produces<string>(StatusCodes.Status200OK)
.CacheOutput("DefaultDictionary").WithTags("Dictionary").WithSummary("Get dictionaries");
endpoints.MapGet(
"api/Flights/{version}/{lang}/board", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetOnlineBoard(searchParams, version, lang, httpContext))
.CacheOutput("DefaultBoard").WithTags("Flights").WithSummary("Get online board");
endpoints.MapGet(
"api/Flights/{version}/{lang}/schedule", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetSchedule(searchParams, version, lang, httpContext))
.CacheOutput("DefaultSchedule").WithTags("Flights").WithSummary("Get schedule");
endpoints.MapGet(
"api/Flights/{version}/{lang}/schedule_pdf", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetSchedulePdf(searchParams, version, lang, httpContext))
.CacheOutput("DefaultSchedule").WithTags("Flights").WithSummary("Get schedule PDF");
endpoints.MapGet(
"api/Flights/{version}/{lang}/get_partner", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetPartner(searchParams, version, lang))
.WithTags("Flights").WithSummary("Get partner (obsolete)");
endpoints.MapGet(
"api/Flights/{version}/{lang}/actual", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetActual(searchParams, version, lang))
.WithTags("Flights").WithSummary("Get actual (obsolete)");
endpoints.MapGet(
"api/Flights/{version:regex(^(1|1.0|v1|v1.0)$)}/{lang}/detailed", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang) =>
controller.GetDetailed(searchParams, version, lang))
.CacheOutput("DefaultDetails").WithTags("Flights").WithSummary("Get detailed (obsolete)");
endpoints.MapGet(
"api/Flights/{version:regex(^(1|1.0|v1|v1.0)$)}/{lang}/{requestType}/details", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
string version,
string lang,
string requestType) =>
controller.GetDetails(searchParams, version, lang, requestType))
.CacheOutput("DefaultDetails").WithTags("Flights").WithSummary("Get details (obsolete)");
endpoints.MapGet(
"api/Flights/v{majorVersion:int}.{minorVersion:int:min(1)}/{lang}/{requestType}/details", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
//[FromQuery] string[] flights, -- does not work as it worked in controllers!
//[FromQuery] DateTime[] dates,
[FromQuery] string departure,
[FromQuery] string arrival,
[FromRoute] int majorVersion,
[FromRoute] int minorVersion,
[FromRoute] string lang,
[FromRoute] string requestType) =>
{
var flights = new List<string>();
var dates = new List<DateTime>();
for (int i = 0; ; i++) {
if (httpContext.Request.Query.ContainsKey("flights[" + i + "]") && httpContext.Request.Query.ContainsKey("dates[" + i + "]"))
{
if (DateTime.TryParseExact(httpContext.Request.Query["dates[" + i + "]"].ToString(), "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt)) {
flights.Add(httpContext.Request.Query["flights[" + i + "]"]);
dates.Add(dt);
}
}
else
{
break;
}
}
if (httpContext.Request.Query.ContainsKey("flights") && httpContext.Request.Query.ContainsKey("dates"))
{
if (DateTime.TryParseExact(httpContext.Request.Query["dates"].ToString(), "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt))
{
flights.Add(httpContext.Request.Query["flights"]);
dates.Add(dt);
}
}
return controller.GetDetails(flights.ToArray(), dates.ToArray(), departure, arrival, majorVersion, minorVersion, lang, requestType);
})
.CacheOutput("DefaultDetails").WithTags("Flights").WithSummary("Get details");
//endpoints.MapPost(
// "api/Flights/updates", (
// [FromServices] WebApi.Controllers.App.FlightsController controller,
// HttpContext httpContext,
// ClaimsPrincipal user,
// [FromBody] List < FlightUpdateModel > flightsUpdate) =>
// controller.PostUpdateFlight(flightsUpdate))
// .WithTags("Flights").WithSummary("Post flight updates");
endpoints.MapGet(
"api/Flights/{version:regex(^(1|1.0|v1|v1.0)$)}/{lang}/destinations", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
[AsParameters] WebApi.Providers.SearchFlightParams searchParams,
[FromRoute] string version,
[FromRoute] string lang) =>
controller.GetDestinations(searchParams, lang))
.CacheOutput("DefaultSchedule").WithTags("Flights").WithSummary("Get destinations");
endpoints.MapGet(
"api/Flights/{version:regex(^(1|1.0|v1|v1.0)$)}/{lang}/days/{date}/{daysNum}/{type}/{request}/{requestType}", (
[FromServices] WebApi.Controllers.App.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[FromRoute] string version,
[FromRoute] string lang,
[FromRoute] DateTime date,
[FromRoute] int daysNum,
[FromRoute] string type,
[FromRoute] string request,
[FromRoute] string requestType) =>
controller.GetDays(version, lang, date, daysNum, type, request, requestType))
.CacheOutput("DefaultDetails").WithTags("Flights").WithSummary("Get flight days");
endpoints.MapGet(
"api/{version:regex(^(v1|v1.0)$)}/{language}/flights", (
[FromServices] WebApi.Controllers.Api.V1.Onlineboard.FlightsController controller,
HttpContext httpContext,
[FromRoute] string language,
[FromRoute] string version) =>
controller.SearchFlightsXml(httpContext, version, language))
.WithTags("Flights V1").WithSummary("Search onlineboard flights in xml");
endpoints.MapGet(
"api/{version:regex(^(v1|v1.0)$)}/{language}/flights.{format}", (
[FromServices] WebApi.Controllers.Api.V1.Onlineboard.FlightsController controller,
HttpContext httpContext,
[FromRoute] string language,
[FromRoute] string format,
[FromRoute] string version) =>
controller.SearchFlights(httpContext, version, language, format))
.WithTags("Flights V1").WithSummary("Search onlineboard flights in requested format");
endpoints.MapGet(
"api/v{version}/{language}/flight", (
[FromServices] WebApi.Controllers.Api.V1.Schedule.FlightController controller,
HttpContext httpContext,
[AsParameters] WebApi.Models.Schedule.ApiV1.SearchFlightParams searchParams,
[FromRoute] string language,
[FromRoute] double version) =>
controller.GetFlightXml(httpContext, version, language, searchParams))
.WithTags("Flights V1").WithSummary("Search schedule flights in xml");
endpoints.MapGet(
"api/v{version}/{language}/flight.{format}", (
[FromServices] WebApi.Controllers.Api.V1.Schedule.FlightController controller,
HttpContext httpContext,
[AsParameters] WebApi.Models.Schedule.ApiV1.SearchFlightParams searchParams,
[FromRoute] string language,
[FromRoute] string format,
[FromRoute] double version) =>
controller.GetFlight(httpContext, version, language, format, searchParams))
.WithTags("Flights V1").WithSummary("Search schedule flights in requested format");
endpoints.MapGet(
"api/v{version}/{language}/aircraft", (
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
[FromRoute] string language,
[FromRoute] double version) =>
controller.GetAircraft(httpContext, version, language, "json"))
.WithTags("Flights V2").WithSummary("Flight aircraft");
endpoints.MapGet(
"api/v{version}/{language}/aircraft.{format}", (
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
string format,
[FromRoute] string language,
[FromRoute] double version) =>
controller.GetAircraft(httpContext, version, language, format))
.WithTags("Flights V2").WithSummary("Flight aircraft");
endpoints.MapGet(
"api/v{version}/{language}/dates", (
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
[FromRoute] string language,
[FromRoute] double version) =>
controller.GetDates(httpContext, version, language, "json"))
.WithTags("Flights V2").WithSummary("Flight dates");
endpoints.MapGet(
"api/v{version}/{language}/dates.{format}", (
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
string format,
[FromRoute] string language,
[FromRoute] double version) =>
controller.GetDates(httpContext, version, language, format))
.WithTags("Flights V2").WithSummary("Flight dates");
endpoints.MapGet(
"api/v{version:regex(^2)}/{lang}/flights",
[AllowAnonymous]
[Authorize(Policy = "policyjwt")]
[Authorize(Policy = "policycookie")]
(
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[FromRoute] string lang,
[FromRoute] double version) =>
controller.GetFlights(httpContext, user, version, lang, "json"))
.WithTags("Flights V2").WithSummary("Search flights in xml");
endpoints.MapGet(
"api/v{version:regex(^2)}/{lang}/flights.{format}",
[AllowAnonymous]
[Authorize(Policy = "policyjwt")]
[Authorize(Policy = "policycookie")]
(
[FromServices] WebApi.Controllers.Api.FlightsController controller,
HttpContext httpContext,
ClaimsPrincipal user,
[FromRoute] string lang,
[FromRoute] string format,
[FromRoute] double version) =>
controller.GetFlights(httpContext, user, version, lang, format))
.WithTags("Flights V2").WithSummary("Search flights in requested format");
endpoints.MapGet(
"/api/Requests/{version}/GetPopular",
([FromServices] WebApi.Controllers.App.RequestsController controller,
string version) =>
controller.GetPopular()).Produces<IEnumerable<IPopularRequest>>(StatusCodes.Status200OK)
.CacheOutput("Requests").WithTags("Requests").WithSummary("Get populer requests");
endpoints.MapGet(
"/api/Version",
([FromServices] WebApi.Controllers.App.VersionController controller) =>
controller.Get()).Produces<string>(StatusCodes.Status200OK)
.WithTags("Version").WithSummary("Get version");
endpoints.MapControllerRoute(
name: "Flights",
pattern: "api/{version}/{**url}",
defaults: new { controller = "ExtFlights", action = "GetNotFound" },
constraints: new { version = @"v1(\.0)?", url = @".*/extflights.*" });
endpoints.MapControllerRoute(
name: "Flights",
pattern: "api/{version}/{**url}",
defaults: new { controller = "Airport", action = "GetNotFound" },
constraints: new { url = @".*/airport.*", version = @"v1(\.(1|0))?" });
endpoints.MapControllerRoute(
name: "Flights",
pattern: "{**url}",
defaults: new { controller = "Airport", action = "GetNotFound" },
constraints: new { url = @".*/airports.*" });
endpoints.MapControllerRoute(
name: "Flights",
pattern: "api/{version}/{**url}",
defaults: new { controller = "Schedule", action = "GetNotFound" },
constraints: new { url = @".*/schedule.*", version = @"v1(\.0)?" });
endpoints.Map("/{country}-{lang}/404", context =>
{
// this is because it asked to return 404 (nobody cares that we are on SPA, and it should be handled by SSR + ng Universal)
context.Response.StatusCode = 404;
return Task.CompletedTask;
});
endpoints.Map("/{country}-{lang}/500", context =>
{
// this is because it asked to return 500 (nobody cares that we are on SPA, and it should be handled by SSR + ng Universal)
context.Response.StatusCode = 500;
return Task.CompletedTask;
});
endpoints.MapControllers();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
//endpoints.MapHub<FlightHub>("/flights");
endpoints.MapMetrics(Configuration["Prometheus:Url"]);
});
if (!String.IsNullOrEmpty(Configuration["Swagger:Password"]))
{
app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Flights");
c.EnableTryItOutByDefault();
});
}
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.Options.StartupTimeout = new TimeSpan(0, 5, 0);
if (env.IsDevelopment() && !Configuration.GetValue<bool>("DOTNET_RUNNING_IN_CONTAINER"))
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
private void ConfigureAuthService(IServiceCollection services, IConfiguration configuration)
{
var identityAuthority = configuration.GetSection("Identity").GetValue<string>("Authority");
var identityClientId = configuration.GetSection("Identity").GetValue<string>("ClientId");
var identityClientSecret = configuration.GetSection("Identity").GetValue<string>("ClientSecret");
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie()
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityAuthority;
options.ClientId = identityClientId;
options.ClientSecret = identityClientSecret;
options.ResponseType = OpenIdConnectResponseType.Code;
options.CallbackPath = "/admin/signin-oidc";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = true };
options.Scope.Add("profile");
options.Scope.Add("openid");
options.Scope.Add("offline_access");
//options.Events = new OpenIdConnectEvents
//{
// OnAuthorizationCodeReceived = async context =>
// {
// var code = context.ProtocolMessage.Code;
// var identifier = context.Principal;
// await Task.Yield();
// },
// OnMessageReceived = async ctxt =>
// {
// string bodyContent = new StreamReader(ctxt.Request.Body).ReadToEnd();
// await Task.Yield();
// },
// OnTicketReceived = async ctxt =>
// {
// await Task.Yield();
// },
// OnTokenValidated = async ctx =>
// {
// var userID = ctx.Principal.FindFirstValue("sub");
// },
//OnRemoteFailure = ctx => {
// ctx.HandleResponse();
// //ctx.Response.Redirect("~/Information");
// return Task.FromResult(0);
// }
//};
});
}
public record CaseInsensitive<T>(T Value)
where T : struct, Enum
{
public static bool TryParse(string? value, IFormatProvider? provider, out CaseInsensitive<T>? val)
{
if (Enum.TryParse<T>(value, ignoreCase: true, out var enumVal))
{
val = new CaseInsensitive<T>(enumVal);
return true;
}
val = null;
return false;
}
}
}
}