93 lines
3.5 KiB
C#
93 lines
3.5 KiB
C#
using System.Text.Json.Nodes;
|
|
using JJServiceManagerRelay;
|
|
|
|
var config = ServerConfig.FromEnvironment();
|
|
var log = new ServerLog(config.LogDir);
|
|
var db = new Db(config.ConnectionString, log);
|
|
var hub = new Hub(db, config, log);
|
|
|
|
log.Info($"JJServiceManagerRelay {hub.Version} 시작 (포트 {config.Port})");
|
|
|
|
using var startCts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
|
|
await db.InitializeAsync(startCts.Token);
|
|
await db.CloseDanglingConnectionsAsync();
|
|
|
|
// 티켓 서명 키 (처음 한 번 만들고 DB에 보관)
|
|
hub.Signer = new TicketSigner(await db.GetOrCreateSettingAsync("ticket_private_key", TicketSigner.NewPrivateKey));
|
|
|
|
// 첫 관리자: 사용자가 하나도 없을 때만 환경 변수로 만듦
|
|
if (await db.CountUsersAsync() == 0)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(config.AdminUser) && !string.IsNullOrEmpty(config.AdminPassword))
|
|
{
|
|
if (config.AdminPassword.Length < 8)
|
|
{
|
|
log.Error("JJSM_ADMIN_PASSWORD는 8자 이상이어야 합니다. 관리자를 만들지 않았습니다.");
|
|
}
|
|
else
|
|
{
|
|
await db.CreateUserAsync(config.AdminUser.Trim(), Secrets.HashPassword(config.AdminPassword), "admin", true, "첫 관리자 (환경 변수)");
|
|
await db.AuditAsync(null, "system", null, "", "userCreate", config.AdminUser.Trim(), "ok", "첫 관리자 (환경 변수)", "");
|
|
log.Info($"첫 관리자 계정을 만들었습니다: {config.AdminUser.Trim()} (첫 로그인 때 비밀번호를 바꿔야 합니다)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log.Warn("사용자가 없습니다. JJSM_ADMIN_USER / JJSM_ADMIN_PASSWORD 환경 변수를 넣고 다시 시작하세요.");
|
|
}
|
|
}
|
|
|
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions { Args = args });
|
|
builder.Logging.ClearProviders();
|
|
builder.Logging.AddSimpleConsole(o => o.SingleLine = true);
|
|
builder.Logging.SetMinimumLevel(LogLevel.Warning);
|
|
builder.WebHost.UseUrls($"http://0.0.0.0:{config.Port}");
|
|
builder.WebHost.ConfigureKestrel(k => k.AddServerHeader = false);
|
|
|
|
var app = builder.Build();
|
|
app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(30) });
|
|
|
|
var agentHandler = new AgentHandler(hub);
|
|
var managerHandler = new ManagerHandler(hub);
|
|
var relayHandler = new RelayHandler(hub);
|
|
|
|
app.MapGet("/", () => Results.Text("JJServiceManagerRelay\n", "text/plain; charset=utf-8"));
|
|
|
|
app.MapGet("/health", async () =>
|
|
{
|
|
bool dbOk = await db.PingAsync();
|
|
var o = new JsonObject
|
|
{
|
|
["status"] = dbOk ? "ok" : "db-error",
|
|
["version"] = hub.Version,
|
|
["db"] = dbOk,
|
|
["onlineDevices"] = hub.Agents.Count,
|
|
["onlineManagers"] = hub.Managers.Count
|
|
};
|
|
return Results.Text(o.ToJsonString(), "application/json; charset=utf-8", statusCode: dbOk ? 200 : 503);
|
|
});
|
|
|
|
app.Map("/ws/agent", async ctx =>
|
|
{
|
|
if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = 400; return; }
|
|
await agentHandler.RunAsync(ctx);
|
|
});
|
|
|
|
app.Map("/ws/manager", async ctx =>
|
|
{
|
|
if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = 400; return; }
|
|
await managerHandler.RunAsync(ctx);
|
|
});
|
|
|
|
app.Map("/relay/{sid}", async (HttpContext ctx, string sid) =>
|
|
{
|
|
if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = 400; return; }
|
|
await relayHandler.RunAsync(ctx, sid);
|
|
});
|
|
|
|
var stop = app.Lifetime.ApplicationStopping;
|
|
_ = Task.Run(() => hub.HousekeepingLoopAsync(stop));
|
|
app.Lifetime.ApplicationStopping.Register(() => log.Info("서버 종료 중"));
|
|
|
|
await app.RunAsync();
|