Program.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using Microsoft.Extensions.Hosting.WindowsServices;
  2. using Serilog;
  3. using SharedKernel;
  4. using Admin.Extensions;
  5. using Admin.Pages.Shared.Layout;
  6. using Admin.Middlewares;
  7. using Application;
  8. using Infrastructure;
  9. var builder = WebApplication.CreateBuilder(args);
  10. var settings = builder.Configuration.Get<AppSettings>()!;
  11. if (!WindowsServiceHelpers.IsWindowsService())
  12. {
  13. Console.Title = settings.App.Name;
  14. }
  15. Console.WriteLine($"ENV={builder.Environment.EnvironmentName}");
  16. Console.WriteLine($"현재 시간: {DateTime.Now} / {TimeZoneInfo.Local.Id}");
  17. builder.Host.UseWindowsService();
  18. // Serilog — Production에서만 파일 로그
  19. if (builder.Environment.IsProduction())
  20. {
  21. Log.Logger = new LoggerConfiguration()
  22. .ReadFrom.Configuration(builder.Configuration)
  23. .WriteTo.File("logs/admin-.log",
  24. rollingInterval: RollingInterval.Day,
  25. retainedFileCountLimit: 30,
  26. outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
  27. .CreateLogger();
  28. builder.Host.UseSerilog();
  29. }
  30. // Add services to the container.
  31. var mvcBuilder = builder.Services.AddRazorPages(options =>
  32. {
  33. options.Conventions.AuthorizeFolder("/");
  34. options.Conventions.AllowAnonymousToAreaFolder("Identity", "/Account");
  35. });
  36. // 프로그램 설정 값 배치
  37. builder.Services.Configure<AppSettings>(builder.Configuration);
  38. // DB 연결
  39. builder.Services.AddApplication().AddAdminInfrastructure(builder.Configuration);
  40. // 관리자 레이아웃
  41. builder.Services.AddScoped<ILayoutDataProvider, LayoutDataProvider>();
  42. // 인증 및 권한 부여
  43. builder.Services.AddAdminForwardedHeaders(settings); // 리버스 프록시 및 접근 제어
  44. builder.Services.AddAdminIdentity(settings); // 관리자단 Identity 인증 및 비밀번호 정책
  45. builder.Services.AddHealthChecks();
  46. builder.Logging.AddConsole(); // 터미널에 로그 출력
  47. if (builder.Environment.IsDevelopment())
  48. {
  49. builder.Logging.AddDebug(); // 디버깅 창에 로그 출력
  50. }
  51. /**
  52. * =======================================================================================================================================================
  53. */
  54. var app = builder.Build();
  55. // 초기 관리자 계정 시드
  56. await app.SeedAdminAccountAsync();
  57. /**
  58. * =======================================================================================================================================================
  59. */
  60. // 환경변수 불러오기
  61. var env = Environment.GetEnvironmentVariable("environmentVariables");
  62. app.Use(async (context, next) =>
  63. {
  64. context.Items["env"] = env; // 환경변수를 static 변수로 저장
  65. await next();
  66. });
  67. /**
  68. * =======================================================================================================================================================
  69. */
  70. // Configure the HTTP request pipeline.
  71. if (!app.Environment.IsDevelopment())
  72. {
  73. app.UseExceptionHandler("/Error");
  74. // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  75. app.UseHsts();
  76. }
  77. app.UseHttpsRedirection();
  78. app.UseRouting();
  79. app.UseAuthentication();
  80. app.UseAuthorization();
  81. // 메뉴 기반 역할 권한 체크
  82. app.UseMiddleware<MenuAuthorizationMiddleware>();
  83. // 관리자 접속 기록
  84. app.UseMiddleware<AdminAccessLogMiddleware>();
  85. app.MapHealthChecks("/health").AllowAnonymous();
  86. // 외부 storage 경로 (deploy 영향 받지 않음) 의 업로드 파일을 정적 서빙.
  87. // StorageOptions.BasePath 미설정 시 wwwroot 기본 동작으로 폴백.
  88. var storageBase = builder.Configuration.GetSection(SharedKernel.Storage.StorageOptions.SectionName).Get<SharedKernel.Storage.StorageOptions>()?.BasePath;
  89. if (!string.IsNullOrWhiteSpace(storageBase) && Directory.Exists(storageBase))
  90. {
  91. var uploadsDir = Path.Combine(storageBase, "uploads");
  92. if (Directory.Exists(uploadsDir))
  93. {
  94. app.UseStaticFiles(new Microsoft.AspNetCore.Builder.StaticFileOptions
  95. {
  96. FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(uploadsDir),
  97. RequestPath = "/uploads"
  98. });
  99. }
  100. var editorsDir = Path.Combine(storageBase, "editors");
  101. if (Directory.Exists(editorsDir))
  102. {
  103. app.UseStaticFiles(new Microsoft.AspNetCore.Builder.StaticFileOptions
  104. {
  105. FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(editorsDir),
  106. RequestPath = "/editors"
  107. });
  108. }
  109. }
  110. // wwwroot 의 빌드 산출 정적 자산 (CSS/JS/이미지 등). 핑거프린트된 자산은 MapStaticAssets 가 처리.
  111. app.UseStaticFiles();
  112. app.MapStaticAssets();
  113. app.MapRazorPages().WithStaticAssets();
  114. app.Run();