619 lines
27 KiB
C#
619 lines
27 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
using Microsoft.OpenApi.Models;
|
|
|
|
|
|
using AyaNova.Models;
|
|
using AyaNova.Util;
|
|
using AyaNova.Generator;
|
|
using AyaNova.Biz;
|
|
|
|
using Swashbuckle.AspNetCore.Swagger;
|
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
|
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Newtonsoft.Json.Serialization;
|
|
|
|
|
|
namespace AyaNova
|
|
{
|
|
|
|
public class Startup
|
|
{
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//
|
|
public Startup(ILogger<Startup> logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IWebHostEnvironment hostingEnvironment)
|
|
{
|
|
_log = logger;
|
|
_hostingEnvironment = hostingEnvironment;
|
|
AyaNova.Util.ApplicationLogging.LoggerFactory = logFactory;
|
|
//this must be set here
|
|
ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH = hostingEnvironment.ContentRootPath;
|
|
|
|
}
|
|
|
|
private readonly ILogger<Startup> _log;
|
|
private string _connectionString = "";
|
|
private readonly Microsoft.AspNetCore.Hosting.IWebHostEnvironment _hostingEnvironment;
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// This method gets called by the runtime. Use this method to add services to the container.
|
|
//
|
|
public void ConfigureServices(IServiceCollection services)
|
|
{
|
|
_log.LogDebug("BOOT: initializing services...");
|
|
|
|
//dotnet 3.x added this wasn't here before
|
|
services.AddControllers();
|
|
|
|
services.AddSwaggerGen(c =>
|
|
{
|
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
|
|
});
|
|
|
|
//Server state service for shutting people out of api
|
|
_log.LogDebug("BOOT: init ApiServerState service");
|
|
services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState());
|
|
|
|
|
|
//dotnet 3 commented this out, new project doesn't have it
|
|
//Init mvc
|
|
// _log.LogDebug("BOOT: init MVC Core service");
|
|
// var mvc = services.AddMvcCore();
|
|
// _log.LogDebug("BOOT: add json service");
|
|
// mvc.AddNewtonsoftJson();
|
|
|
|
|
|
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
|
|
// note: the specified format code will format the version as "'v'major[.minor][-status]"
|
|
_log.LogDebug("BOOT: init ApiExplorer service");
|
|
// services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");
|
|
|
|
_log.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations...");
|
|
FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath);
|
|
|
|
#region DATABASE
|
|
_connectionString = ServerBootConfig.AYANOVA_DB_CONNECTION;
|
|
|
|
//Check DB server exists and can be connected to
|
|
_log.LogDebug("BOOT: Testing database server connection...");
|
|
|
|
//parse the connection string properly
|
|
DbUtil.ParseConnectionString(_log, _connectionString);
|
|
|
|
//Probe for database server
|
|
//Will retry every 10 seconds for up to 5 minutes before bailing
|
|
if (!DbUtil.DatabaseServerExists(_log, "BOOT: waiting for db server "))
|
|
{
|
|
var err = $"BOOT: E1000 - AyaNova can't connect to the database server after trying for 5 minutes (connection string is:\"{DbUtil.DisplayableConnectionString}\")";
|
|
_log.LogCritical(err);
|
|
throw new System.ApplicationException(err);
|
|
}
|
|
|
|
|
|
|
|
_log.LogInformation("BOOT: Connected to database server - {0}", DbUtil.DisplayableConnectionString);
|
|
|
|
|
|
//ensure database is ready and present
|
|
DbUtil.EnsureDatabaseExists(_log);
|
|
|
|
bool LOG_SENSITIVE_DATA = false;
|
|
|
|
#if (DEBUG)
|
|
// LOG_SENSITIVE_DATA = true;
|
|
|
|
#endif
|
|
|
|
_log.LogDebug("BOOT: init EF service");
|
|
|
|
services.AddEntityFrameworkNpgsql().AddDbContext<AyContext>(
|
|
options => options.UseNpgsql(_connectionString
|
|
//,opt => opt.EnableRetryOnFailure()//REMOVED THIS BECAUSE IT WAS INTEFERING WITH TRANSACTIONS BUT THEN DIDN'T USE THE TRANSACTION BUT IT SEEMS FASTER WITHOUT IT AS WELL SO...??
|
|
)//http://www.npgsql.org/efcore/misc.html?q=execution%20strategy#execution-strategy
|
|
.ConfigureWarnings(warnings => //https://livebook.manning.com/#!/book/entity-framework-core-in-action/chapter-12/v-10/85
|
|
warnings.Throw( //Throw an exception on client eval, not necessarily an error but a smell
|
|
// Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.QueryClientEvaluationWarning
|
|
))
|
|
.EnableSensitiveDataLogging(LOG_SENSITIVE_DATA)
|
|
);
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
_log.LogDebug("BOOT: init ApiVersioning service");
|
|
// services
|
|
// .AddApiVersioning(options =>
|
|
// {
|
|
// options.AssumeDefaultVersionWhenUnspecified = true;
|
|
// options.DefaultApiVersion = Microsoft.AspNetCore.Mvc.ApiVersion.Parse("8.0");
|
|
// options.ReportApiVersions = true;
|
|
// });
|
|
|
|
|
|
//v3 commented out to try to figure out why the swagger docs aren't generating and may be superfluous
|
|
// // Add service and create Policy with options
|
|
// _log.LogDebug("BOOT: init CORS service");
|
|
// services.AddCors(options =>
|
|
// {
|
|
// options.AddPolicy("CorsPolicy",
|
|
// builder => builder.AllowAnyOrigin()
|
|
// .AllowAnyMethod()
|
|
// .AllowAnyHeader()
|
|
// //.AllowCredentials()
|
|
// );
|
|
// });
|
|
|
|
|
|
_log.LogDebug("BOOT: init MVC service");
|
|
_log.LogDebug("BOOT: init Metrics service");
|
|
|
|
|
|
// services.AddMvc(config =>
|
|
// {
|
|
// //was this but needed logging, not certain about the new way of adding so keeping this in case it all goes sideways in testing
|
|
// //config.Filters.Add(typeof(AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter));
|
|
// config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory));
|
|
|
|
// }).AddMetrics().AddJsonOptions(options =>
|
|
// {
|
|
// //2019-10-15 - removed this due to fuckery after update, is it required??? Not sure at this point
|
|
// //if metrics have wrong dates then it's important I guess
|
|
// //options.JsonSerializerOptions.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
|
|
// });
|
|
|
|
|
|
#region Swagger
|
|
//https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio-code
|
|
//https://swagger.io/
|
|
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore
|
|
|
|
_log.LogDebug("BOOT: init API explorer service");
|
|
// services.AddSwaggerGen(c =>
|
|
// {
|
|
// c.SwaggerDoc("v8", new OpenApiInfo { Title = "My API", Version = "v8" });
|
|
// c.OperationFilter<AuthResponsesOperationFilter>();
|
|
|
|
// });
|
|
// services.AddSwaggerGen(
|
|
// c =>
|
|
// {
|
|
|
|
|
|
// c.DocInclusionPredicate((docName, apiDesc) =>
|
|
// {
|
|
// if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
|
|
|
|
// // Get the MapToApiVersion attributes of the action
|
|
// var mapApiVersions = methodInfo
|
|
// .GetCustomAttributes(true)
|
|
// .OfType<MapToApiVersionAttribute>()
|
|
// .SelectMany(attr => attr.Versions);
|
|
|
|
// //if it contains MapToApiVersion attributes, then we should check those as the ApiVersion ones are ignored
|
|
// if (mapApiVersions.Any() && mapApiVersions.Any(v => $"v{v.ToString()}" == docName))
|
|
// return true;
|
|
|
|
// // Get the ApiVersion attributes of the controller
|
|
// var versions = methodInfo.DeclaringType
|
|
// .GetCustomAttributes(true)
|
|
// .OfType<ApiVersionAttribute>()
|
|
// .SelectMany(attr => attr.Versions);
|
|
|
|
// return versions.Any(v => $"v{v.ToString()}" == docName);
|
|
// });
|
|
|
|
|
|
|
|
// // 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)
|
|
// {
|
|
// c.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
|
|
// }
|
|
|
|
// // add a custom operation filter which sets default values
|
|
// // c.OperationFilter<SwaggerDefaultValues>();
|
|
|
|
// //test filter WTF?
|
|
// //c.OperationFilter<AuthResponsesOperationFilter>();
|
|
|
|
// // integrate xml comments
|
|
// c.IncludeXmlComments(XmlCommentsFilePath);
|
|
|
|
|
|
|
|
// // //2019-10-15 - Removed this because apikeyscheme is no longer recognized and it appears it *may* not be necessary... TWT
|
|
// // //If I have any issues with bearer tokens in swagger then this is probably necessary but in a new way
|
|
// // //this is required to allow authentication when testing secure routes via swagger UI
|
|
// // c.AddSecurityDefinition("Bearer", new ApiKeyScheme
|
|
// // {
|
|
// // Description = "JWT Authorization header using the Bearer scheme. Get your token by logging in via the Auth route then enter it here with the \"Bearer \" prefix. Example: \"Bearer {token}\"",
|
|
// // Name = "Authorization",
|
|
// // In = "header",
|
|
// // Type = "apiKey"
|
|
|
|
// // });
|
|
|
|
|
|
|
|
|
|
// //Obsolete way
|
|
// // c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>
|
|
// // {
|
|
// // { "Bearer", new string[] { } }
|
|
// // });
|
|
|
|
// //https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5
|
|
// //First we define the security scheme
|
|
// c.AddSecurityDefinition("Bearer", //Name the security scheme
|
|
// new OpenApiSecurityScheme
|
|
// {
|
|
// Description = "JWT Authorization header using the Bearer scheme.",
|
|
// Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
|
|
// Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
|
|
// });
|
|
|
|
// c.AddSecurityRequirement(new OpenApiSecurityRequirement{
|
|
// {
|
|
// new OpenApiSecurityScheme{
|
|
// Reference = new OpenApiReference{
|
|
// Id = "Bearer", //The name of the previously defined security scheme.
|
|
// Type = ReferenceType.SecurityScheme
|
|
// }
|
|
// },new List<string>()
|
|
// }
|
|
// });
|
|
|
|
|
|
// //https://github.com/domaindrivendev/Swashbuckle.AspNetCore
|
|
// //ARGGHHHHHHHH!!!!!
|
|
|
|
|
|
// });
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region JWT AUTHENTICATION
|
|
//get the key if specified
|
|
var secretKey = ServerBootConfig.AYANOVA_JWT_SECRET;
|
|
|
|
//If no key specified make a unique one
|
|
//This means the jwt creds won't survive a server reboot
|
|
//so in that case users need to specify an AyaNova_JWT_SECRET environment variable
|
|
if (string.IsNullOrWhiteSpace(secretKey))
|
|
{
|
|
secretKey = Util.Hasher.GenerateSalt();
|
|
}
|
|
//WAS "UNLICENSED5G*QQJ8#bQ7$Xr_@sXfHq4"
|
|
|
|
|
|
//If secretKey is less than 32 characters, pad it
|
|
if (secretKey.Length < 32)
|
|
{
|
|
secretKey = secretKey.PadRight(32, '-');
|
|
}
|
|
|
|
ServerBootConfig.AYANOVA_JWT_SECRET = secretKey;
|
|
var signingKey = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(ServerBootConfig.AYANOVA_JWT_SECRET));
|
|
|
|
_log.LogDebug("BOOT: init Authorization service");
|
|
services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
}).AddJwtBearer(options =>
|
|
{
|
|
// options.AutomaticAuthenticate = true;
|
|
// options.AutomaticChallenge = true;
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
// Token signature will be verified using a private key.
|
|
ValidateIssuerSigningKey = true,
|
|
RequireSignedTokens = true,
|
|
IssuerSigningKey = signingKey,
|
|
ValidateIssuer = true,
|
|
ValidIssuer = "ayanova.com",
|
|
ValidateAudience = false,
|
|
//ValidAudience = "http://localhost:7575/"
|
|
|
|
// Token will only be valid if not expired yet, with 5 minutes clock skew.
|
|
ValidateLifetime = true,
|
|
RequireExpirationTime = true,
|
|
ClockSkew = new TimeSpan(0, 5, 0),
|
|
};
|
|
});
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
_log.LogDebug("BOOT: init Generator service");
|
|
services.AddSingleton<IHostedService, GeneratorService>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
//
|
|
public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env,
|
|
AyContext dbContext, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider)
|
|
{
|
|
//This was in constructor right after dbcontext: IApiVersionDescriptionProvider provider,
|
|
_log.LogDebug("BOOT: configuring request pipeline...");
|
|
|
|
//dotnet3 test added
|
|
if (env.IsDevelopment())
|
|
{
|
|
app.UseDeveloperExceptionPage();
|
|
}
|
|
|
|
//This is in new templates generated for webapi but it errors out for me so removing it for now
|
|
// app.UseHttpsRedirection();
|
|
|
|
//Store a reference to the dependency injection service for static classes
|
|
ServiceProviderProvider.Provider = app.ApplicationServices;
|
|
|
|
//Enable ability to handle reverse proxy
|
|
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
|
{
|
|
ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor | Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
|
|
});
|
|
|
|
#region STATIC FILES
|
|
_log.LogDebug("BOOT: pipeline - static files");
|
|
app.UseDefaultFiles();
|
|
app.UseStaticFiles();
|
|
|
|
#endregion
|
|
|
|
_log.LogDebug("BOOT: pipeline - ROUTING");
|
|
app.UseRouting();//this wasn't here for 2.2 but added for 3.0, needs to come before the stuff after
|
|
|
|
//v3test
|
|
// _log.LogDebug("BOOT: pipeline - CORS");
|
|
// app.UseCors("CorsPolicy");
|
|
|
|
|
|
#region AUTH / ROLES
|
|
_log.LogDebug("BOOT: pipeline - authentication");
|
|
//Use authentication middleware
|
|
app.UseAuthentication();
|
|
|
|
_log.LogDebug("BOOT: pipeline - authorization");
|
|
app.UseAuthorization();
|
|
|
|
//Custom middleware to get user roles and put them into the request so
|
|
//they can be authorized in routes.
|
|
app.Use(async (context, next) =>
|
|
{
|
|
if (!context.User.Identity.IsAuthenticated)
|
|
{
|
|
context.Request.HttpContext.Items["AY_ROLES"] = 0;
|
|
}
|
|
else
|
|
{
|
|
//Get user ID from claims
|
|
long userId = Convert.ToInt64(context.User.FindFirst(c => c.Type == "id").Value);
|
|
|
|
//Get the database context
|
|
var ct = context.RequestServices.GetService<AyContext>();
|
|
|
|
//get the user record
|
|
var u = ct.User.AsNoTracking().Where(a => a.Id == userId).Select(m => new { roles = m.Roles, name = m.Name, id = m.Id, localeId = m.LocaleId }).First();
|
|
context.Request.HttpContext.Items["AY_ROLES"] = u.roles;
|
|
context.Request.HttpContext.Items["AY_USERNAME"] = u.name;
|
|
context.Request.HttpContext.Items["AY_USER_ID"] = u.id;
|
|
context.Request.HttpContext.Items["AY_LOCALE_ID"] = u.localeId;
|
|
|
|
|
|
|
|
}
|
|
await next.Invoke();
|
|
});
|
|
|
|
#endregion
|
|
|
|
//According to https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-2.2&tabs=visual-studio#migrate-startupconfigure
|
|
_log.LogDebug("BOOT: pipeline - ENDPOINTS");
|
|
app.UseEndpoints(endpoints =>
|
|
{
|
|
endpoints.MapControllers();
|
|
});
|
|
|
|
|
|
#region SWAGGER
|
|
|
|
_log.LogDebug("BOOT: pipeline - api explorer");
|
|
// // Enable middleware to serve generated Swagger as a JSON endpoint.
|
|
// app.UseSwagger();
|
|
|
|
// app.UseSwaggerUI(c =>
|
|
// {
|
|
// // build a swagger endpoint for each discovered API version
|
|
// foreach (var description in provider.ApiVersionDescriptions)
|
|
// {
|
|
// c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
|
|
// }
|
|
|
|
// //clean up the swagger explorer UI page and remove the branding
|
|
// //via our own css
|
|
// //NOTE: this broke when updated to v2.x of swagger and it can be fixed according to docs:
|
|
// //https://github.com/domaindrivendev/Swashbuckle.AspNetCore#inject-custom-css
|
|
// // c.InjectStylesheet("/api/sw.css");
|
|
|
|
// c.DefaultModelsExpandDepth(-1);
|
|
// c.DocumentTitle = "AyaNova API explorer";
|
|
// c.RoutePrefix = "api-docs";
|
|
|
|
// });
|
|
|
|
app.UseSwagger();
|
|
|
|
app.UseSwaggerUI(c =>
|
|
{
|
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
|
c.DefaultModelsExpandDepth(-1);
|
|
c.DocumentTitle = "AyaNova API explorer";
|
|
c.RoutePrefix = "api-docs";
|
|
});
|
|
#endregion swagger
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ******************************************************************
|
|
// ******************** TESTING WIPE DB *****************************
|
|
//
|
|
//Set this to true to wipe the db and reinstall a trial license and re-seed the data
|
|
var TESTING_REFRESH_DB = false;//#######################################################################################
|
|
|
|
#if (DEBUG)
|
|
//TESTING
|
|
if (TESTING_REFRESH_DB)
|
|
ServerBootConfig.AYANOVA_PERMANENTLY_ERASE_DATABASE = TESTING_REFRESH_DB;
|
|
//TESTING
|
|
#endif
|
|
|
|
|
|
if (ServerBootConfig.AYANOVA_PERMANENTLY_ERASE_DATABASE)
|
|
{
|
|
_log.LogWarning("BOOT: AYANOVA_PERMANENTLY_ERASE_DATABASE is true, dropping and recreating database");
|
|
Util.DbUtil.DropAndRecreateDb(_log);
|
|
AySchema.CheckAndUpdate(dbContext, _log);
|
|
}
|
|
|
|
//Check schema
|
|
_log.LogDebug("BOOT: db schema check");
|
|
AySchema.CheckAndUpdate(dbContext, _log);
|
|
|
|
//Check database integrity
|
|
_log.LogDebug("BOOT: db integrity check");
|
|
DbUtil.CheckFingerPrint(AySchema.EXPECTED_COLUMN_COUNT, AySchema.EXPECTED_INDEX_COUNT, _log);
|
|
|
|
//Initialize license
|
|
AyaNova.Core.License.Initialize(apiServerState, dbContext, _log);
|
|
|
|
//Ensure locales are present, not missing any keys and that there is a server default locale that exists
|
|
LocaleBiz lb = new LocaleBiz(dbContext, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.OpsAdminFull);
|
|
lb.ValidateLocales();
|
|
|
|
|
|
|
|
#if (DEBUG)
|
|
//TESTING
|
|
if (TESTING_REFRESH_DB)
|
|
{
|
|
AyaNova.Core.License.Fetch(apiServerState, dbContext, _log);
|
|
Util.Seeder.SeedDatabase(Util.Seeder.SeedLevel.SmallOneManShopTrialDataSet, -7);//#############################################################################################
|
|
}
|
|
//TESTING
|
|
#endif
|
|
|
|
|
|
//AUTOID VALUES INITIALIZATION
|
|
ServerBootConfig.SetMostRecentAutoIdValuesFromDatabase(dbContext);
|
|
|
|
//SPA FALLBACK ROUTE
|
|
app.Use(async (context, next) =>
|
|
{
|
|
//to support html5 pushstate routing in spa
|
|
//this ensures that a refresh at the client will not 404 but rather force back to the index.html app page and then handled internally by the client
|
|
await next();
|
|
if (!context.Response.HasStarted && context.Request.Path.Value != "/docs" && context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
|
|
{
|
|
context.Request.Path = "/index.html";
|
|
context.Response.StatusCode = 200;
|
|
context.Response.ContentType = "text/html";
|
|
await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html"));
|
|
}
|
|
});
|
|
|
|
|
|
//Log the active user count so it's in the log record
|
|
_log.LogInformation($"BOOT: Active techs - {UserBiz.ActiveCount}");
|
|
|
|
//Log the license info so it's on the record
|
|
_log.LogInformation($"BOOT: License -\r\n=-=-=-=-=-=-=-=-=-=-\r\n{AyaNova.Core.License.LicenseInfo}=-=-=-=-=-=-=-=-=-=-");
|
|
|
|
|
|
|
|
//Open up the server for visitors
|
|
apiServerState.SetOpen();
|
|
|
|
//final startup log
|
|
_log.LogInformation("BOOT: COMPLETED - SERVER IS NOW OPEN");
|
|
|
|
}
|
|
|
|
|
|
#region Swagger and API Versioning utilities
|
|
|
|
// static string XmlCommentsFilePath
|
|
// {
|
|
// get
|
|
// {
|
|
// //Obsolete, used new method: https://developers.de/blogs/holger_vetter/archive/2017/06/30/swagger-includexmlcomments-platformservices-obsolete-replacement.aspx
|
|
// //var basePath = PlatformServices.Default.Application.ApplicationBasePath;
|
|
// var basePath = AppContext.BaseDirectory;
|
|
// var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";
|
|
// return Path.Combine(basePath, fileName);
|
|
// }
|
|
// }
|
|
|
|
// static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
|
|
// {
|
|
// var info = new Microsoft.OpenApi.Models.OpenApiInfo()
|
|
// {
|
|
// Title = $"AyaNova API {description.ApiVersion}",
|
|
// Version = description.ApiVersion.ToString()
|
|
// };
|
|
|
|
// if (description.IsDeprecated)
|
|
// {
|
|
// info.Description += " This API version has been deprecated.";
|
|
// }
|
|
|
|
// return info;
|
|
// }
|
|
#endregion
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|