Program.cs 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. using Application.Abstractions.Cache;
  2. using Application.Abstractions.Data;
  3. using Infrastructure.Cache;
  4. using Infrastructure.Messaging.Email;
  5. using Infrastructure.Persistence;
  6. using MailWorker;
  7. using Serilog;
  8. using Serilog.Events;
  9. using SharedKernel;
  10. using StackExchange.Redis;
  11. using Microsoft.EntityFrameworkCore;
  12. using Microsoft.Extensions.Configuration;
  13. using Microsoft.Extensions.DependencyInjection;
  14. using Microsoft.Extensions.Hosting;
  15. using Microsoft.Extensions.Hosting.WindowsServices;
  16. using Microsoft.Extensions.Logging;
  17. var builder = Host.CreateApplicationBuilder(args);
  18. var settings = builder.Configuration.Get<AppSettings>()!;
  19. if (!WindowsServiceHelpers.IsWindowsService())
  20. {
  21. Console.Title = "DPOT MailWorker";
  22. }
  23. Console.WriteLine($"ENV={builder.Environment.EnvironmentName}");
  24. Console.WriteLine($"현재 시간: {DateTime.Now} / {TimeZoneInfo.Local.Id}");
  25. builder.Services.AddWindowsService();
  26. if (builder.Environment.IsProduction())
  27. {
  28. Log.Logger = new LoggerConfiguration()
  29. .MinimumLevel.Information()
  30. .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
  31. .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
  32. .WriteTo.Console()
  33. .WriteTo.File("logs/mailworker-.log",
  34. rollingInterval: RollingInterval.Day,
  35. retainedFileCountLimit: 30,
  36. outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
  37. .CreateLogger();
  38. builder.Logging.ClearProviders();
  39. builder.Logging.AddSerilog(Log.Logger);
  40. }
  41. builder.Services.Configure<AppSettings>(builder.Configuration);
  42. // DB
  43. var dbConn = builder.Configuration.GetConnectionString("DefaultConnection");
  44. if (string.IsNullOrWhiteSpace(dbConn))
  45. {
  46. throw new InvalidOperationException("Connection string 'DefaultConnection' is not configured.");
  47. }
  48. builder.Services.AddDbContext<AppDbContext>(options => options
  49. .UseSqlServer(dbConn)
  50. .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)));
  51. builder.Services.AddScoped<IAppDbContext>(sp => sp.GetRequiredService<AppDbContext>());
  52. // Redis (Config 캐시 공유)
  53. var redis = ConnectionMultiplexer.Connect(settings.Redis.DefaultConnection);
  54. builder.Services.AddSingleton<IConnectionMultiplexer>(redis);
  55. builder.Services.AddStackExchangeRedisCache(options =>
  56. {
  57. options.Configuration = settings.Redis.DefaultConnection;
  58. options.InstanceName = settings.Redis.CachePrefix;
  59. });
  60. builder.Services.AddSingleton<ICacheService, RedisCacheService>();
  61. // SMTP 직발 (Worker는 항상 Direct)
  62. builder.Services.AddScoped<DirectMailService>();
  63. // Hosted services
  64. builder.Services.AddHostedService<MailQueueWorker>();
  65. builder.Services.AddHostedService<EmailLogJanitor>();
  66. builder.Build().Run();