This commit is contained in:
2026-07-27 14:29:46 +08:00
110 changed files with 13942 additions and 2044 deletions
+59 -5
View File
@@ -7,6 +7,7 @@ using MiGu.Server.Auth;
using MiGu.Server.Configs;
using MiGu.Server.Launcher;
using MiGu.Server.OpenApi;
using MiGu.Server.Ota;
using MiGu.Server.Persistence;
using Yarp.ReverseProxy.Transforms;
@@ -31,12 +32,27 @@ var builder = WebApplication.CreateBuilder(new WebApplicationOptions
ContentRootPath = FindSourceContentRoot(AppContext.BaseDirectory) ?? AppContext.BaseDirectory
});
if (string.IsNullOrWhiteSpace(builder.Configuration["urls"])
&& string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ASPNETCORE_URLS"))
&& string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("DOTNET_URLS")))
// 管理面默认 :8080WatchDog 拉包回传写死 :8000/upload-mdcs/*,必须额外监听 ReceivePort。
{
// 直接运行 MiGu.Server.exe 不读取 launchSettings.json;保持与 dotnet run / 文档一致默认监听 8080。
builder.WebHost.UseUrls("http://0.0.0.0:8080");
var receivePort = builder.Configuration.GetValue("Ota:ReceivePort", 8000);
var urls = builder.Configuration["urls"]
?? Environment.GetEnvironmentVariable("ASPNETCORE_URLS")
?? Environment.GetEnvironmentVariable("DOTNET_URLS");
if (string.IsNullOrWhiteSpace(urls))
urls = "http://0.0.0.0:8080";
var parts = urls.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var hasReceive = parts.Any(u =>
u.Contains($":{receivePort}", StringComparison.OrdinalIgnoreCase)
|| u.EndsWith($":{receivePort}/", StringComparison.OrdinalIgnoreCase));
if (!hasReceive)
{
urls = string.Join(';', parts.Append($"http://0.0.0.0:{receivePort}"));
// 安全提示:该端口挂匿名 upload-mdcs/upload-historyWatchDog 写死回传)。
// upload-mdcs 仅在有进行中的拉取会话时可写入,其余管理端点仍需 JWT。请确保本机处于可信内网。
Console.WriteLine($"[MiGu.Server] OTA 回传端口 {receivePort} 已监听(0.0.0.0):匿名接收车辆包,仅限可信内网。");
}
builder.WebHost.UseUrls(urls);
}
// S2 根治 (会话45):让 MiGu.Server 启动时自动优先把 frontends/apps/simple-platform-vue/dist/
@@ -95,6 +111,11 @@ builder.Services.AddControllers()
opt.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
opt.JsonSerializerOptions.WriteIndented = false;
});
// WatchDog 回传 M/D/C 可较大;放宽 multipart 默认 128MB 限制
builder.Services.Configure<Microsoft.AspNetCore.Http.Features.FormOptions>(o =>
{
o.MultipartBodyLengthLimit = 512_000_000;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
@@ -207,6 +228,25 @@ builder.Services.AddReverseProxy()
var store = rt.HttpContext.RequestServices.GetRequiredService<InternalTokenStore>();
rt.ProxyRequest.Headers.Remove("X-Platform-Internal-Token");
rt.ProxyRequest.Headers.Add("X-Platform-Internal-Token", store.Token);
var user = rt.HttpContext.User;
var username = user.FindFirst("unique_name")?.Value
?? user.Identity?.Name
?? user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
?? user.FindFirst("sub")?.Value;
var userId = user.FindFirst("sub")?.Value
?? user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
var scope = user.FindFirst("scope")?.Value;
rt.ProxyRequest.Headers.Remove("X-Platform-User");
rt.ProxyRequest.Headers.Remove("X-Platform-User-Id");
rt.ProxyRequest.Headers.Remove("X-Platform-Scope");
if (!string.IsNullOrWhiteSpace(username))
rt.ProxyRequest.Headers.TryAddWithoutValidation("X-Platform-User", username);
if (!string.IsNullOrWhiteSpace(userId))
rt.ProxyRequest.Headers.TryAddWithoutValidation("X-Platform-User-Id", userId);
if (!string.IsNullOrWhiteSpace(scope))
rt.ProxyRequest.Headers.TryAddWithoutValidation("X-Platform-Scope", scope);
return ValueTask.CompletedTask;
});
});
@@ -224,6 +264,20 @@ builder.Services.AddPlatformPersistence(builder.Configuration);
builder.Services.Configure<SimpleLiteOptions>(builder.Configuration.GetSection("SimpleLite"));
builder.Services.AddSingleton<SimpleLiteLauncher>();
// OTAWatchDog 编排):包库 / 任务 / 出站客户端
builder.Services.Configure<OtaOptions>(builder.Configuration.GetSection("Ota"));
builder.Services.AddSingleton<OtaStore>();
builder.Services.AddSingleton<InternalTokenStoreAccessor>();
builder.Services.AddSingleton<OtaVehicleSource>();
builder.Services.AddSingleton<WatchDogClient>();
builder.Services.AddSingleton<OtaJobRunner>();
builder.Services.AddSingleton<MiGu.Server.Fleet.FleetHealthService>();
builder.Services.AddSingleton<MiGu.Server.Fleet.CdmTaskSyncer>();
builder.Services.AddHostedService<MiGu.Server.Fleet.CdmTaskSyncService>();
builder.Services.AddSingleton<MiGu.Server.Fleet.AlarmCollector>();
builder.Services.AddHostedService<MiGu.Server.Fleet.AlarmCollectorService>();
builder.Services.AddHttpClient(nameof(WatchDogClient));
var app = builder.Build();
await app.Services.EnsurePlatformDatabaseAsync();