|
|
|
using AX.WebDrillServer.Data;
|
|
|
|
using AX.WebDrillServer.EntityMappers;
|
|
|
|
using AX.WebDrillServer.Errors;
|
|
|
|
using AX.WebDrillServer.Extensions;
|
|
|
|
using AX.WebDrillServer.Hubs;
|
|
|
|
using AX.WebDrillServer.Middlewares;
|
|
|
|
using AX.WebDrillServer.Middlewares.Jwts;
|
|
|
|
using AX.WebDrillServer.Services.FireDeductionHub;
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
using Microsoft.AspNetCore.Mvc.Authorization;
|
|
|
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
|
|
|
using Microsoft.AspNetCore.SignalR;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using Microsoft.IdentityModel.Tokens;
|
|
|
|
using Microsoft.OpenApi.Models;
|
|
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text.Json;
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
|
|
|
const string ProductName = "沙盘推演";
|
|
|
|
const string ProductVersion = "v1.0";
|
|
|
|
const string ApiVersion = "v1";
|
|
|
|
|
|
|
|
#region Banner
|
|
|
|
|
|
|
|
var banners = "Banners";
|
|
|
|
if (Directory.Exists(banners))
|
|
|
|
{
|
|
|
|
var files = Directory.GetFiles(banners);
|
|
|
|
var random = new Random();
|
|
|
|
var file = files[random.Next(0, files.Length)];
|
|
|
|
var banner = File.ReadAllText(file);
|
|
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
|
|
Console.Write(banner);
|
|
|
|
Console.WriteLine("\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Green;
|
|
|
|
Console.WriteLine("按 CTRL+C 键退出程序...\n\n");
|
|
|
|
Console.ForegroundColor = ConsoleColor.White;
|
|
|
|
Console.Title = $"{ProductName} {ProductVersion}";
|
|
|
|
|
|
|
|
#endregion Banner
|
|
|
|
|
|
|
|
#region ServerLicense
|
|
|
|
|
|
|
|
if (!ServerLicense.Initialize())
|
|
|
|
{
|
|
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
|
|
Console.WriteLine(GlobalErrorCodes.Messages[GlobalErrorCodes.E600]);
|
|
|
|
Console.WriteLine("\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion ServerLicense
|
|
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
|
|
|
|
#region Identities
|
|
|
|
|
|
|
|
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("JwtSettings"));
|
|
|
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
|
|
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
|
|
.AddJwtBearer(options =>
|
|
|
|
{
|
|
|
|
var secret = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Secret"]));
|
|
|
|
|
|
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
|
|
{
|
|
|
|
NameClaimType = JwtClaimTypes.Name,
|
|
|
|
RoleClaimType = JwtClaimTypes.Role,
|
|
|
|
|
|
|
|
ValidateIssuer = true,
|
|
|
|
ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
|
|
|
|
ValidateIssuerSigningKey = true,
|
|
|
|
IssuerSigningKey = secret,
|
|
|
|
ValidateAudience = true,
|
|
|
|
ValidAudience = builder.Configuration["JwtSettings:Audience"],
|
|
|
|
RequireExpirationTime = true,
|
|
|
|
ValidateLifetime = true,
|
|
|
|
ClockSkew = TimeSpan.FromSeconds(30)
|
|
|
|
};
|
|
|
|
|
|
|
|
// SignalR Jwt
|
|
|
|
options.Events = new JwtBearerEvents
|
|
|
|
{
|
|
|
|
OnMessageReceived = context =>
|
|
|
|
{
|
|
|
|
var accessToken = context.Request.Query["access_token"];
|
|
|
|
|
|
|
|
var path = context.HttpContext.Request.Path;
|
|
|
|
if (accessToken.HasValue() &&
|
|
|
|
path.StartsWithSegments("/hubs"))
|
|
|
|
context.Token = accessToken;
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
builder.Services.AddSingleton<IJwtService, JwtService>();
|
|
|
|
builder.Services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
|
|
|
|
|
|
|
|
builder.Services.AddAuthorization();
|
|
|
|
|
|
|
|
#endregion Identities
|
|
|
|
|
|
|
|
// Email Service
|
|
|
|
//builder.Services.AddSingleton<IEmailService, EmailService>();
|
|
|
|
builder.Services.AddSingleton<RoomManager>();
|
|
|
|
builder.Services.AddMemoryCache();
|
|
|
|
|
|
|
|
builder.Services
|
|
|
|
.AddSignalR(options =>
|
|
|
|
{
|
|
|
|
#if !RELEASE
|
|
|
|
options.EnableDetailedErrors = true;
|
|
|
|
#endif
|
|
|
|
})
|
|
|
|
.AddJsonProtocol(options =>
|
|
|
|
{
|
|
|
|
options.PayloadSerializerOptions.PropertyNameCaseInsensitive = true;
|
|
|
|
options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
|
|
|
options.PayloadSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
|
|
|
|
//全局配置signalr json 转枚举使用stringName还是intValue
|
|
|
|
options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
|
|
|
|
});
|
|
|
|
|
|
|
|
//#region Custom Services
|
|
|
|
|
|
|
|
//builder.Services.AddTransient<INotificationService, NotificationService>();
|
|
|
|
|
|
|
|
//#endregion Custom Services
|
|
|
|
|
|
|
|
builder.Services
|
|
|
|
.AddControllers(options =>
|
|
|
|
{
|
|
|
|
options.InputFormatters.Insert(0, new RawJsonInputFormatter());
|
|
|
|
options.OutputFormatters.Insert(0, new RawJsonOutputFormatter());
|
|
|
|
//options.Filters.Add(new AuthorizeFilter());
|
|
|
|
})
|
|
|
|
.AddJsonOptions(options =>
|
|
|
|
{
|
|
|
|
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
|
|
|
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
|
|
|
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
|
|
|
|
//全局配置Controller json 转枚举使用stringName还是intValue
|
|
|
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
|
|
|
|
});
|
|
|
|
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
|
|
|
#region Swagger
|
|
|
|
|
|
|
|
if (builder.Environment.IsDevelopment() || builder.Environment.IsStaging())
|
|
|
|
{
|
|
|
|
builder.Services.AddSwaggerGen(options =>
|
|
|
|
{
|
|
|
|
options.CustomSchemaIds(type => type.ToString());
|
|
|
|
options.SwaggerDoc(ApiVersion, new OpenApiInfo { Title = $"{ProductName} API", Version = ApiVersion });
|
|
|
|
|
|
|
|
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
|
|
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
|
|
|
|
|
|
|
options.IncludeXmlComments(xmlPath, true);
|
|
|
|
options.IgnoreObsoleteActions();
|
|
|
|
|
|
|
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
|
|
|
{
|
|
|
|
Description = "使用 JWT Bearer 模式进行授权。示例: \"Bearer {token}\"",
|
|
|
|
Name = "Authorization", //Jwt default param name
|
|
|
|
In = ParameterLocation.Header, //Jwt store address
|
|
|
|
Type = SecuritySchemeType.ApiKey, //Security scheme type
|
|
|
|
Scheme = "Bearer",
|
|
|
|
BearerFormat = "JWT"
|
|
|
|
});
|
|
|
|
|
|
|
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|
|
|
{
|
|
|
|
{
|
|
|
|
new OpenApiSecurityScheme
|
|
|
|
{
|
|
|
|
Reference = new OpenApiReference
|
|
|
|
{
|
|
|
|
Type = ReferenceType.SecurityScheme,
|
|
|
|
Id = "Bearer"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Array.Empty<string>()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// File Filter
|
|
|
|
options.OperationFilter<FileUploadOperationFilter>();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion Swagger
|
|
|
|
|
|
|
|
#region Database Context
|
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddDbContextPool<ApplicationDbContext>((provider, options) =>
|
|
|
|
{
|
|
|
|
if (builder.Environment.IsDevelopment() || builder.Environment.IsStaging())
|
|
|
|
options.EnableSensitiveDataLogging();
|
|
|
|
options.UseNpgsql(builder.Configuration.GetConnectionString("PostgreSQL"),
|
|
|
|
// 全局启用拆分查询, 详见: https://docs.microsoft.com/zh-cn/ef/core/querying/single-split-queries;
|
|
|
|
opts => opts.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
#endregion Database Context
|
|
|
|
|
|
|
|
#region Mapper
|
|
|
|
|
|
|
|
EntityMapperConfig.Initialize();
|
|
|
|
|
|
|
|
#endregion Mapper
|
|
|
|
|
|
|
|
var app = builder.Build();
|
|
|
|
|
|
|
|
#region Setup Database
|
|
|
|
|
|
|
|
app.SetupDatabase();
|
|
|
|
|
|
|
|
#endregion Setup Database
|
|
|
|
|
|
|
|
#region Development
|
|
|
|
|
|
|
|
if (app.Environment.IsDevelopment() || app.Environment.IsStaging())
|
|
|
|
{
|
|
|
|
app.UseDeveloperExceptionPage();
|
|
|
|
app.UseSwagger();
|
|
|
|
app.UseSwaggerUI(options =>
|
|
|
|
{
|
|
|
|
options.SwaggerEndpoint($"/swagger/{ApiVersion}/swagger.json", $"{ProductName} API {ApiVersion}");
|
|
|
|
options.EnablePersistAuthorization();
|
|
|
|
});
|
|
|
|
app.UseReDoc(options =>
|
|
|
|
{
|
|
|
|
options.RoutePrefix = "docs";
|
|
|
|
options.DocumentTitle = $"{ProductName} API {ApiVersion}";
|
|
|
|
|
|
|
|
options.SpecUrl($"/swagger/{ApiVersion}/swagger.json");
|
|
|
|
options.EnableUntrustedSpec();
|
|
|
|
options.ScrollYOffset(10);
|
|
|
|
options.HideHostname();
|
|
|
|
options.HideDownloadButton();
|
|
|
|
options.ExpandResponses("200,201");
|
|
|
|
options.RequiredPropsFirst();
|
|
|
|
//c.NoAutoAuth();
|
|
|
|
//c.PathInMiddlePanel();
|
|
|
|
//c.HideLoading();
|
|
|
|
//c.NativeScrollbars();
|
|
|
|
//c.DisableSearch();
|
|
|
|
//c.OnlyRequiredInSamples();
|
|
|
|
//c.SortPropsAlphabetically();
|
|
|
|
});
|
|
|
|
|
|
|
|
app.UseHttpLogging();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion Development
|
|
|
|
|
|
|
|
//app.UseHttpsRedirection();
|
|
|
|
|
|
|
|
app.UseAuthentication();
|
|
|
|
app.UseAuthorization();
|
|
|
|
|
|
|
|
app.MapControllers();
|
|
|
|
|
|
|
|
//app.MapHub<NotificationHub>("/hubs/notification");
|
|
|
|
//app.MapHub<TaskChatHub>("/hubs/taskchat");
|
|
|
|
app.MapHub<FireDeductionHub>("/hubs/FireDeductionHub");
|
|
|
|
|
|
|
|
app.Run();
|