Add domestic vendor push infrastructure

This commit is contained in:
2026-07-26 01:45:59 +08:00
parent 7cca34b331
commit 0738953e6d
77 changed files with 6470 additions and 855 deletions
+39 -9
View File
@@ -9,9 +9,10 @@ using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
builder.Services.AddRateLimiter(options =>
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var authPermitLimit = Math.Max(1, builder.Configuration.GetValue("RateLimiting:AuthPermitLimit", 10));
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddPolicy("auth", context =>
@@ -19,7 +20,7 @@ builder.Services.AddRateLimiter(options =>
context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
_ => new FixedWindowRateLimiterOptions
{
PermitLimit = 10,
PermitLimit = authPermitLimit,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 0,
}));
@@ -50,13 +51,32 @@ builder.Services.AddScoped<ReplyService>();
builder.Services.AddScoped<LedgerResolver>();
builder.Services.AddScoped<AiPermissionService>();
builder.Services.AddScoped<AiChatQuotaService>();
builder.Services.AddScoped<AiPermissionFilter>();
builder.Services.AddHttpClient("LlmClient");
builder.Services.AddScoped<BudgetPushService>();
builder.Services.AddSingleton<PushTokenProtector>();
builder.Services.AddScoped<AiPermissionFilter>();
builder.Services.AddHttpClient("LlmClient");
builder.Services.AddHttpClient("PushProviders", client =>
{
client.Timeout = TimeSpan.FromSeconds(20);
});
foreach (var provider in new[]
{
"huawei", "honor", "xiaomi", "oppo", "vivo", "meizu",
})
{
builder.Services.AddSingleton<IPushProvider>(services => new OfficialPushProvider(
provider,
services.GetRequiredService<IConfiguration>(),
services.GetRequiredService<IHttpClientFactory>(),
services.GetRequiredService<ILogger<OfficialPushProvider>>()));
}
builder.Services.AddSingleton<PushProviderRegistry>();
builder.Services.AddSingleton<OpenAiVisionClient>();
builder.Services.AddSingleton<ILlmClient>(sp => sp.GetRequiredService<OpenAiVisionClient>());
builder.Services.AddHostedService<RecycleBinCleanupService>();
builder.Services.AddScoped<AccountDataEraser>();
builder.Services.AddHostedService<AccountClosureCleanupService>();
builder.Services.AddHostedService<AccountClosureCleanupService>();
builder.Services.AddHostedService<PushDispatchService>();
var conn = builder.Configuration.GetConnectionString("Default");
if (string.IsNullOrWhiteSpace(conn))
@@ -64,9 +84,19 @@ 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"];
var adminKey = builder.Configuration["Admin:Key"];
if (string.IsNullOrWhiteSpace(adminKey) || adminKey.Length < 24)
throw new InvalidOperationException("必须通过 Admin__Key 配置至少 24 位的管理密钥");
throw new InvalidOperationException("必须通过 Admin__Key 配置至少 24 位的管理密钥");
if (builder.Configuration.GetValue<bool>("Push:Enabled"))
{
var pushKey = builder.Configuration["Push:TokenEncryptionKey"];
byte[]? key = null;
try { key = string.IsNullOrWhiteSpace(pushKey) ? null : Convert.FromBase64String(pushKey); }
catch (FormatException) { }
if (key?.Length != 32)
throw new InvalidOperationException(
"启用推送时必须通过 Push__TokenEncryptionKey 配置 base64 编码的 32 字节密钥");
}
builder.Services.AddDbContext<AppDbContext>(o =>
o.UseMySql(conn, ServerVersion.AutoDetect(conn)));