public void ConfigureServices(IServiceCollection services) { ShokoServer.ConfigureServices(services); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CustomAuthOptions.DefaultScheme; options.DefaultChallengeScheme = CustomAuthOptions.DefaultScheme; }).AddScheme <CustomAuthOptions, CustomAuthHandler>(CustomAuthOptions.DefaultScheme, _ => { }); services.AddAuthorization(auth => { auth.AddPolicy("admin", policy => policy.Requirements.Add(new UserHandler(user => user.IsAdmin == 1))); auth.AddPolicy("init", policy => policy.Requirements.Add(new UserHandler(user => user.JMMUserID == 0 && user.UserName == "init"))); }); services.AddSwaggerGen( options => { // resolve the IApiVersionDescriptionProvider service // note: that we have to build a temporary service provider here because one has not been created yet var provider = services.BuildServiceProvider().GetRequiredService <IApiVersionDescriptionProvider>(); // add a swagger document for each discovered API version // note: you might choose to skip or document deprecated API versions differently foreach (var description in provider.ApiVersionDescriptions) { options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); } options.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme() { Description = "Shoko API Key Header", Name = "apikey", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, Scheme = "apikey", }); options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ApiKey", }, }, new string[] {} }, }); // add a custom operation filter which sets default values //options.OperationFilter<SwaggerDefaultValues>(); // integrate xml comments //Locate the XML file being generated by ASP.NET... var xmlFile = "Shoko.Server.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); if (File.Exists(xmlPath)) { options.IncludeXmlComments(xmlPath); } foreach (Type type in Loader.Instance.Plugins.Keys) { var assembly = type.Assembly; var location = assembly.Location; var xml = Path.Combine(Path.GetDirectoryName(location), $"{Path.GetFileNameWithoutExtension(location)}.xml"); if (File.Exists(xml)) { options.IncludeXmlComments(xml); //Include the XML comments if it exists. } } options.MapType <v3.Models.Shoko.SeriesType>(() => new OpenApiSchema { Type = "string" }); options.MapType <v3.Models.Shoko.EpisodeType>(() => new OpenApiSchema { Type = "string" }); options.CustomSchemaIds(x => x.FullName); }); services.AddSwaggerGenNewtonsoftSupport(); services.AddSignalR(o => { o.EnableDetailedErrors = true; }); services.AddSingleton <QueueEmitter>(); services.AddSingleton <LegacyAniDBEmitter>(); services.AddSingleton <LoggingEmitter>(); // allow CORS calls from other both local and non-local hosts services.AddCors(options => { options.AddDefaultPolicy( builder => { builder .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); // this caused issues with auth. https://stackoverflow.com/questions/43574552 var mvc = services.AddMvc(options => { options.EnableEndpointRouting = false; options.AllowEmptyInputInBodyModelBinding = true; foreach (var formatter in options.InputFormatters) { if (formatter.GetType() == typeof(NewtonsoftJsonInputFormatter)) { ((NewtonsoftJsonInputFormatter)formatter).SupportedMediaTypes.Add( MediaTypeHeaderValue.Parse("text/plain")); } } options.Filters.Add(typeof(DatabaseBlockedFilter)); options.Filters.Add(typeof(ServerNotRunningFilter)); EmitEmptyEnumerableInsteadOfNullAttribute.MvcOptions = options; }) .SetCompatibilityVersion(CompatibilityVersion.Version_3_0) .AddNewtonsoftJson(json => { json.SerializerSettings.MaxDepth = 10; json.SerializerSettings.ContractResolver = new OmitEmptyEnumerableResolver { NamingStrategy = new DefaultNamingStrategy() }; json.SerializerSettings.NullValueHandling = NullValueHandling.Include; json.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Populate; json.SerializerSettings.DateFormatString = "yyyy-MM-dd"; }); foreach (Type type in Loader.Instance.Plugins.Keys) { var assembly = type.Assembly; if (assembly == Assembly.GetCallingAssembly()) { continue; //Skip the current assembly, this is implicitly added by ASP. } mvc.AddApplicationPart(assembly).AddControllersAsServices(); } services.AddApiVersioning(o => { o.ReportApiVersions = true; o.AssumeDefaultVersionWhenUnspecified = true; o.DefaultApiVersion = ApiVersion.Default; o.ApiVersionReader = ApiVersionReader.Combine( new QueryStringApiVersionReader(), new HeaderApiVersionReader("api-version"), new ShokoApiReader() ); }); services.AddVersionedApiExplorer(); services.AddResponseCaching(); services.Configure <KestrelServerOptions>(options => { options.AllowSynchronousIO = true; }); }