Files
Atomx/Atomx.Admin/Atomx.Admin/Program.cs
2025-12-05 00:27:43 +08:00

199 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Atomx.Admin.Client.Services;
using Atomx.Admin.Client.Utils;
using Atomx.Admin.Components;
using Atomx.Admin.Extensions;
using Atomx.Admin.Middlewares;
using Atomx.Admin.Models;
using Atomx.Admin.Services;
using Atomx.Admin.Utils;
using Atomx.Common.Models;
using Atomx.Data;
using Atomx.Data.Services;
using Atomx.Utils.Json.Converts;
using Blazored.LocalStorage;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
using Scalar.AspNetCore;
using Serilog;
using System;
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
var builder = WebApplication.CreateBuilder(args);
// Serilog 配置(保持原样)
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", "Atomx.Admin")
.Enrich.WithProperty("Environment", builder.Environment.EnvironmentName)
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
// 基本服务注册
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.Converters.Add(new LongJsonConverter());
});
builder.Services.AddRouting(p => p.LowercaseUrls = true);
builder.Services.AddMapster();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddHttpContextAccessor();
// 权限服务 & 授权处理器
builder.Services.AddScoped<IPermissionService, PermissionService>();
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
// AuthenticationStateProviderServer 使用可重新验证的实现
builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();
builder.Services.AddScoped<PersistentAuthenticationStateProvider>();
// 基本工具服务
builder.Services.AddScoped<IIdCreatorService, IdCreatorService>();
builder.Services.AddScoped<IIdentityService, IdentityService>();
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
builder.Services.AddScoped<LocalizationFile, LocalizationFile>();
builder.Services.AddScoped<AuthHeaderHandler>();
// SignalR启用服务端 Hub 支持注意JWT 的 OnMessageReceived 已在 AuthorizationExtension 中处理)
builder.Services.AddSignalR();
// HttpClient 与数据服务
builder.Services.AddHttpClientApiService(builder.Configuration["WebApi:ServerUrl"] ?? "http://localhost");
builder.Services.AddDataService();
builder.Services.AddAuthorize(builder.Configuration, builder.Environment); // 引入我们配置好的认证/授权
// EF Core DbContext
var connection = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<DataContext>(options => options.UseNpgsql(connection, p => p.MigrationsHistoryTable("__DbMigrationsHistory")));
// Redis 缓存
var redisConnection = builder.Configuration.GetConnectionString("cache");
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = redisConnection;
options.InstanceName = builder.Configuration["RedisCache:InstanceName"];
});
// 响应压缩(保留)
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes
.Where(m => !string.Equals(m, "text/html", StringComparison.OrdinalIgnoreCase))
.ToArray();
});
// CORS生产环境要求显式配置允许来源以支持 CookieAllowCredentials
var corsOrigins = builder.Configuration["Cors:AllowedOrigins"];
if (!string.IsNullOrEmpty(corsOrigins))
{
var origins = corsOrigins.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
builder.Services.AddCors(options =>
{
options.AddPolicy("DefaultCors", policy =>
{
policy.WithOrigins(origins)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
}
else
{
// 开发时快速允许任意 origin不推荐生产
builder.Services.AddCors(options =>
{
options.AddPolicy("DefaultCors", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
// 注意AllowAnyOrigin 与 AllowCredentials 不能同时使用
});
});
}
builder.Services.AddOpenApi();
builder.Services.AddAntDesign();
builder.Services.Configure<MonitoringOptions>(builder.Configuration.GetSection("Monitoring"));
var app = builder.Build();
app.AddDataMigrate();
// Forwarded headers反向代理
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.MapScalarApiReference();
app.MapOpenApi();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}
app.UseResponseCompression();
// 使用命名的 CORS 策略
app.UseCors("DefaultCors");
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append("Cache-Control", $"public, max-age={31536000}");
}
});
// 中间件顺序:认证 -> 授权
app.UseAuthentication();
app.UseAuthorization();
// Antiforgery & 其他中间件
app.UseAntiforgery();
app.MapStaticAssets();
app.UseMiddleware<MonitoringMiddleware>();
app.UseMiddleware<ExceptionHandlingMiddleware>();
// SignalR endpoints如项目包含 Hub请在此处映射
// 如果存在 Hub 类如 ChatHub、NotificationHub请在此取消注释并映射
// app.MapHub<ChatHub>("/hubs/chat");
// app.MapHub<NotificationHub>("/hubs/notification");
app.MapControllers();
// Blazor 配置Server + WASM render modes
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Atomx.Admin.Client._Imports).Assembly);
app.Run();