wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using BoardApp.Config;
using BoardApp.Middleware;
using BoardApp.Services;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Server.IISIntegration;
 
var builder = WebApplication.CreateBuilder(args);
 
// Load app.config.json early so we can configure session cookie flags.
var earlyCfgLoader = new BoardConfigLoader();
var appCfg = earlyCfgLoader.LoadAppConfig();
 
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();
 
builder.Services.AddSingleton<BoardConfigLoader>();
builder.Services.AddSingleton<ScenarioService>();
builder.Services.AddSingleton<UserScenarioService>();
builder.Services.AddSingleton<SqlDataSourceService>();
builder.Services.AddSingleton<WarehouseCalendarService>();
builder.Services.AddSingleton<LdapAuthService>();
builder.Services.AddSingleton<AdAuthenticationService>();
 
var isIisHost = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH"));
if (isIisHost)
{
    builder.Services.AddAuthentication(IISDefaults.AuthenticationScheme);
}
else
{
    builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
}
 
static SameSiteMode ParseSameSite(object? v)
{
    var s = (v as string)?.Trim().ToLowerInvariant();
    return s switch
    {
        "strict" => SameSiteMode.Strict,
        "none" => SameSiteMode.None,
        _ => SameSiteMode.Lax
    };
}
 
// Session cookie must match Node version for behavior parity.
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
    options.Cookie.Name = "board.sid";
    options.Cookie.Path = "/";
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.Cookie.SecurePolicy = appCfg.CookieSecure
        ? CookieSecurePolicy.Always
        : CookieSecurePolicy.SameAsRequest;
    options.Cookie.SameSite = ParseSameSite(appCfg.CookieSameSite);
    options.Cookie.MaxAge = TimeSpan.FromMilliseconds(appCfg.SessionMaxAgeMs);
    options.IdleTimeout = TimeSpan.FromMilliseconds(appCfg.SessionMaxAgeMs);
});
 
var app = builder.Build();
 
if (appCfg.TrustProxy)
{
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });
}
 
// Keep external URL shape: http://host:3000/board/...
var pathBase = Environment.GetEnvironmentVariable("BOARD_PATH_BASE");
if (string.IsNullOrWhiteSpace(pathBase))
{
    pathBase = "/board";
}
if (!pathBase.StartsWith("/"))
{
    pathBase = "/" + pathBase;
}
app.UsePathBase(pathBase);
 
app.Use(async (ctx, next) =>
{
    ctx.Items["BoardAppConfig"] = appCfg;
    await next();
});
 
// /board/static/x → wwwroot/static/x(勿设 RequestPath=/static,否则会映射到 wwwroot/x 导致 404)
app.UseStaticFiles();
 
app.UseRouting();
app.UseSession();
app.UseAuthentication();
 
app.UseMiddleware<BoardRequireAuthMiddleware>();
 
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Board}/{action=Home}/{id?}");
 
// IIS(含进程内托管)或容器常由宿主注入监听地址,勿再硬编码 Kestrel URL。
var urlsFromEnv = Environment.GetEnvironmentVariable("ASPNETCORE_URLS");
var iisPhysicalPath = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH");
if (!string.IsNullOrWhiteSpace(urlsFromEnv) || !string.IsNullOrWhiteSpace(iisPhysicalPath))
{
    app.Run();
}
else
{
    app.Run($"http://0.0.0.0:{appCfg.Port}");
}