150 lines
4.4 KiB
C#
150 lines
4.4 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
|
using Microsoft.Extensions.Configuration;
|
|
using OpenTelemetry.Metrics;
|
|
using OpenTelemetry.Resources;
|
|
using OpenTelemetry.Trace;
|
|
using Serilog;
|
|
using TakeoutSaaS.Module.Tenancy.Extensions;
|
|
using TakeoutSaaS.Shared.Abstractions.Ids;
|
|
using TakeoutSaaS.Shared.Kernel.Ids;
|
|
using TakeoutSaaS.Shared.Web.Extensions;
|
|
using TakeoutSaaS.Shared.Web.Swagger;
|
|
|
|
// 1. 创建构建器与日志模板
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
const string logTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [TraceId:{TraceId}] [SpanId:{SpanId}] [Service:{Service}] {SourceContext} {Message:lj}{NewLine}{Exception}";
|
|
|
|
// 2. 注册雪花 ID 生成器与 Serilog
|
|
builder.Services.AddSingleton<IIdGenerator>(_ => new SnowflakeIdGenerator());
|
|
builder.Host.UseSerilog((_, _, configuration) =>
|
|
{
|
|
configuration
|
|
.Enrich.FromLogContext()
|
|
.Enrich.WithProperty("Service", "UserApi")
|
|
.WriteTo.Console(outputTemplate: logTemplate)
|
|
.WriteTo.File(
|
|
"logs/user-api-.log",
|
|
rollingInterval: RollingInterval.Day,
|
|
retainedFileCountLimit: 7,
|
|
shared: true,
|
|
outputTemplate: logTemplate);
|
|
});
|
|
|
|
// 3. 注册通用 Web 能力与 Swagger
|
|
builder.Services.AddSharedWebCore();
|
|
builder.Services.AddSharedSwagger(options =>
|
|
{
|
|
options.Title = "外卖SaaS - 用户端";
|
|
options.Description = "C 端用户 API 文档";
|
|
options.EnableAuthorization = true;
|
|
});
|
|
|
|
// 4. 注册多租户与健康检查
|
|
builder.Services.AddTenantResolution(builder.Configuration);
|
|
builder.Services.AddHealthChecks();
|
|
|
|
// 5. 配置 OpenTelemetry 采集
|
|
var otelSection = builder.Configuration.GetSection("Otel");
|
|
var otelEndpoint = otelSection.GetValue<string>("Endpoint");
|
|
var useConsoleExporter = otelSection.GetValue<bool?>("UseConsoleExporter") ?? builder.Environment.IsDevelopment();
|
|
builder.Services.AddOpenTelemetry()
|
|
.ConfigureResource(resource => resource.AddService(
|
|
serviceName: "TakeoutSaaS.UserApi",
|
|
serviceVersion: "1.0.0",
|
|
serviceInstanceId: Environment.MachineName))
|
|
.WithTracing(tracing =>
|
|
{
|
|
tracing
|
|
.SetSampler(new ParentBasedSampler(new AlwaysOnSampler()))
|
|
.AddAspNetCoreInstrumentation()
|
|
.AddHttpClientInstrumentation()
|
|
.AddEntityFrameworkCoreInstrumentation();
|
|
|
|
if (!string.IsNullOrWhiteSpace(otelEndpoint))
|
|
{
|
|
tracing.AddOtlpExporter(exporter =>
|
|
{
|
|
exporter.Endpoint = new Uri(otelEndpoint);
|
|
});
|
|
}
|
|
|
|
if (useConsoleExporter)
|
|
{
|
|
tracing.AddConsoleExporter();
|
|
}
|
|
})
|
|
.WithMetrics(metrics =>
|
|
{
|
|
metrics
|
|
.AddAspNetCoreInstrumentation()
|
|
.AddHttpClientInstrumentation()
|
|
.AddRuntimeInstrumentation()
|
|
.AddPrometheusExporter();
|
|
|
|
if (!string.IsNullOrWhiteSpace(otelEndpoint))
|
|
{
|
|
metrics.AddOtlpExporter(exporter =>
|
|
{
|
|
exporter.Endpoint = new Uri(otelEndpoint);
|
|
});
|
|
}
|
|
|
|
if (useConsoleExporter)
|
|
{
|
|
metrics.AddConsoleExporter();
|
|
}
|
|
});
|
|
|
|
// 6. 配置 CORS
|
|
var userOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:User");
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddPolicy("UserApiCors", policy =>
|
|
{
|
|
ConfigureCorsPolicy(policy, userOrigins);
|
|
});
|
|
});
|
|
|
|
// 7. 构建应用并配置中间件管道
|
|
var app = builder.Build();
|
|
|
|
app.UseCors("UserApiCors");
|
|
app.UseTenantResolution();
|
|
app.UseSharedWebCore();
|
|
app.UseSharedSwagger();
|
|
|
|
app.MapHealthChecks("/healthz");
|
|
app.MapPrometheusScrapingEndpoint();
|
|
app.MapControllers();
|
|
app.Run();
|
|
|
|
// 8. 解析配置中的 CORS 域名
|
|
static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey)
|
|
{
|
|
var origins = configuration.GetSection(sectionKey).Get<string[]>();
|
|
return origins?
|
|
.Where(origin => !string.IsNullOrWhiteSpace(origin))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToArray() ?? [];
|
|
}
|
|
|
|
// 9. 构建 CORS 策略
|
|
static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins)
|
|
{
|
|
if (origins.Length == 0)
|
|
{
|
|
policy.AllowAnyOrigin();
|
|
}
|
|
else
|
|
{
|
|
policy.WithOrigins(origins)
|
|
.AllowCredentials();
|
|
}
|
|
|
|
policy
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod();
|
|
}
|