using Application.Abstractions.Cache; using Application.Abstractions.Data; using Infrastructure.Cache; using Infrastructure.Messaging.Email; using Infrastructure.Persistence; using MailWorker; using Serilog; using Serilog.Events; using SharedKernel; using StackExchange.Redis; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting.WindowsServices; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.DataProtection; var builder = Host.CreateApplicationBuilder(args); var settings = builder.Configuration.Get()!; if (!WindowsServiceHelpers.IsWindowsService()) { Console.Title = "DPOT MailWorker"; } Console.WriteLine($"ENV={builder.Environment.EnvironmentName}"); Console.WriteLine($"현재 시간: {DateTime.Now} / {TimeZoneInfo.Local.Id}"); builder.Services.AddWindowsService(); if (builder.Environment.IsProduction()) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .WriteTo.Console() .WriteTo.File("logs/mailworker-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}") .CreateLogger(); builder.Logging.ClearProviders(); builder.Logging.AddSerilog(Log.Logger); } builder.Services.Configure(builder.Configuration); // DB var dbConn = builder.Configuration.GetConnectionString("DefaultConnection"); if (string.IsNullOrWhiteSpace(dbConn)) { throw new InvalidOperationException("Connection string 'DefaultConnection' is not configured."); } builder.Services.AddDbContext(options => options .UseSqlServer(dbConn) .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))); builder.Services.AddScoped(sp => sp.GetRequiredService()); // Redis (Config 캐시 공유) var redis = ConnectionMultiplexer.Connect(settings.Redis.DefaultConnection); builder.Services.AddSingleton(redis); builder.Services.AddDataProtection() .SetApplicationName(settings.App.Name) .PersistKeysToStackExchangeRedis(redis, settings.Redis.DataProtectionKey) .ProtectKeysWithDpapi(protectToLocalMachine: true) // key를 암호화하여 저장 (로컬 머신에서만 복호화 가능, 서버간 공유 불가) .SetDefaultKeyLifetime(settings.Redis.DefaultKeyLifetime); // 기본 90일 주기 builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = settings.Redis.DefaultConnection; options.InstanceName = settings.Redis.CachePrefix; }); builder.Services.AddSingleton(); // SMTP 직발 (Worker는 항상 Direct) builder.Services.AddScoped(); // Hosted services builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Build().Run();