Add transfer tracking and secure admin access

This commit is contained in:
2026-07-26 11:57:57 +08:00
parent 0738953e6d
commit 7df25edd96
111 changed files with 6379 additions and 1934 deletions
+18 -8
View File
@@ -15,7 +15,7 @@ var authPermitLimit = Math.Max(1, builder.Configuration.GetValue("RateLimiting:A
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddPolicy("auth", context =>
options.AddPolicy("auth", context =>
RateLimitPartition.GetFixedWindowLimiter(
context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
_ => new FixedWindowRateLimiterOptions
@@ -23,7 +23,16 @@ builder.Services.AddRateLimiter(options =>
PermitLimit = authPermitLimit,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 0,
}));
}));
options.AddPolicy("admin-auth", context =>
RateLimitPartition.GetFixedWindowLimiter(
context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
_ => new FixedWindowRateLimiterOptions
{
PermitLimit = 5,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 0,
}));
options.AddPolicy("ai", context =>
RateLimitPartition.GetConcurrencyLimiter(
context.User.FindFirst("sub")?.Value ??
@@ -52,6 +61,8 @@ builder.Services.AddScoped<LedgerResolver>();
builder.Services.AddScoped<AiPermissionService>();
builder.Services.AddScoped<AiChatQuotaService>();
builder.Services.AddScoped<BudgetPushService>();
builder.Services.AddScoped<AdminSessionService>();
builder.Services.AddScoped<AdminBootstrapService>();
builder.Services.AddSingleton<PushTokenProtector>();
builder.Services.AddScoped<AiPermissionFilter>();
builder.Services.AddHttpClient("LlmClient");
@@ -84,9 +95,6 @@ if (string.IsNullOrWhiteSpace(conn))
var jwtSecret = builder.Configuration["Jwt:Secret"];
if (string.IsNullOrWhiteSpace(jwtSecret) || jwtSecret.Length < 32)
throw new InvalidOperationException("必须通过 Jwt__Secret 配置至少 32 位的 JWT 密钥");
var adminKey = builder.Configuration["Admin:Key"];
if (string.IsNullOrWhiteSpace(adminKey) || adminKey.Length < 24)
throw new InvalidOperationException("必须通过 Admin__Key 配置至少 24 位的管理密钥");
if (builder.Configuration.GetValue<bool>("Push:Enabled"))
{
var pushKey = builder.Configuration["Push:TokenEncryptionKey"];
@@ -156,7 +164,8 @@ var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync();
await db.Database.MigrateAsync();
await scope.ServiceProvider.GetRequiredService<AdminBootstrapService>().EnsureAsync();
if (app.Environment.IsDevelopment())
await DbSeeder.SeedAsync(db);
}
@@ -167,8 +176,9 @@ var buildTime = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + " UTC";
var apiVersion = builder.Configuration["Build:Version"] ?? "dev";
app.UseAuthentication();
app.UseRateLimiter();
app.UseAuthorization();
app.UseRateLimiter();
app.UseAuthorization();
app.UseMiddleware<AdminAuditMiddleware>();
app.MapControllers();
app.MapGet("/api/ping", () => Results.Ok(new { status = "ok", version = apiVersion, built = buildTime }));
app.MapGet("/api/version", () => Results.Ok(new { app = "记之 API", version = apiVersion, built = buildTime }));