ShopPlatform.Web 1.0.0.0
농산물, 소프트웨어 라이선스와 개발 용역을 직접 판매하는 CodeMaru 직영 쇼핑몰입니다.
로딩중...
검색중...
일치하는것 없음
Program.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.Identity;
2using Dreamine.Identity.Options;
3using Microsoft.EntityFrameworkCore;
9
10var builder = WebApplication.CreateBuilder(args);
11builder.Configuration.AddUserSecrets("codemaru-oauth-2ba4e1b2");
12
13// ── 옵션 ─────────────────────────────────────────────────────────────
14var shopOpts = ShopOptions.From(builder.Configuration);
15builder.Services.AddSingleton(shopOpts);
16
17// ── Dreamine.Identity (CodeMaru 공용 쿠키 로그인) ─────────────────────
18AuthOptions authOptions =
19 builder.Configuration.GetSection(AuthOptions.SectionName).Get<AuthOptions>() ?? new AuthOptions();
21 builder.Configuration[$"{AuthOptions.SectionName}:UsersDbPath"],
22 Path.Combine(AppContext.BaseDirectory, "App_Data", "codemaru.db"));
23builder.Services.AddDreamineIdentityWeb(authOptions, usersDbPath);
24
25// DataProtection은 Dreamine.Identity가 CodeMaru 공용 경로에 이미 등록.
26// 결제 시크릿 키(PaymentKeyProtector)는 동일 KeyRing을 "ShopPlatform.PaymentKeys"
27// 목적 문자열로 격리해서 그대로 사용한다.
28
29// ── 멀티테넌트 서비스 ─────────────────────────────────────────────────
31builder.Services.AddScoped<TenantContext>();
32builder.Services.AddSingleton<PaymentKeyProtector>();
33builder.Services.AddSingleton<TenantDbContextFactory>();
34
35// ── 결제 ─────────────────────────────────────────────────────────────
36builder.Services.AddHttpClient();
37
38// ── 장바구니 + 고객 세션 (Scoped = Blazor 회로 수명) ──────────────────
39builder.Services.AddScoped<CartService>();
40builder.Services.AddScoped<ShopCustomerSession>();
41builder.Services.AddScoped<ShopUserContext>();
42builder.Services.AddSingleton<ShopCustomerProfileStore>();
43builder.Services.AddScoped<ShopCustomerLoginSync>();
44
45// ── Blazor Server ─────────────────────────────────────────────────────
46builder.Services.AddRazorComponents()
47 .AddInteractiveServerComponents();
48
49// ── 압축 ─────────────────────────────────────────────────────────────
50builder.Services.AddResponseCompression(o => o.EnableForHttps = true);
51
52var app = builder.Build();
53
54// ── 미들웨어 파이프라인 ───────────────────────────────────────────────
55if (!app.Environment.IsDevelopment())
56{
57 app.UseExceptionHandler("/Error");
58 app.UseHsts();
59}
60
61// 리버스 프록시 뒤에서 X-Forwarded-* 헤더 인식 (쿠키 SameSite/Secure 판정용)
62app.UseForwardedHeaders();
63app.UseResponseCompression();
64app.UseStaticFiles();
65
66// 샵별 업로드 이미지 서빙: App_Data/Shops/{slug}/images/ → /shop-img/{slug}/
67app.UseStaticFiles(new StaticFileOptions
68{
69 FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(shopOpts.ResolvedDataPath),
70 RequestPath = "/shop-img"
71});
72
73// 테넌트 미들웨어: 경로에서 slug 추출 → TenantContext 주입
74app.UseMiddleware<TenantMiddleware>();
75
76app.UseAuthentication();
77app.UseAuthorization();
78
79app.UseAntiforgery();
80
81// 카카오·SNS 봇 → OG 메타태그 전용 HTML 반환
82app.UseMiddleware<OgBotMiddleware>();
83
84// ── 헬스체크 ──────────────────────────────────────────────────────────
85app.MapGet("/healthz", () => Results.Text("OK", "text/plain"));
86
87// ── 결제 콜백 엔드포인트 (/pay/{slug}/return, /pay/{slug}/fail) ────────
88app.MapGet("/pay/{slug}/return", async (
89 string slug,
90 string paymentKey, string orderId, int amount,
91 IShopTenantStore store,
92 PaymentKeyProtector protector,
93 IHttpClientFactory httpFactory,
94 TenantDbContextFactory dbFactory) =>
95{
96 var config = await store.GetAsync(slug);
97 if (config == null) return Results.NotFound();
98
99 // 테넌트별 게이트웨이 생성
100 IPaymentGateway gateway = config.Payment.IsEnabled
101 ? new TossPaymentGateway(httpFactory.CreateClient(), config.Payment, protector)
102 : new DummyPaymentGateway();
103
104 var result = await gateway.ConfirmAsync(paymentKey, orderId, amount);
105 if (!result.Succeeded)
106 return Results.Redirect($"/{slug}/checkout?error={Uri.EscapeDataString(result.Error ?? "결제 실패")}");
107
108 // 주문 상태 업데이트 + 재고 차감
109 using var db = dbFactory.Create(slug);
110 var order = db.Orders
111 .Include(o => o.Lines)
112 .FirstOrDefault(o => o.OrderNo == orderId);
113 if (order != null)
114 {
115 order.Status = "paid";
116 order.TransactionId = result.TransactionId;
117 DeductStock(db, order.Lines);
118 await db.SaveChangesAsync();
119 }
120
121 return Results.Redirect($"/{slug}/order/{orderId}?paid=1");
122});
123
124app.MapGet("/pay/{slug}/fail", (string slug, string? code, string? message) =>
125 Results.Redirect($"/{slug}/checkout?error={Uri.EscapeDataString(message ?? "결제가 취소되었습니다.")}"));
126
127// ── Blazor Razor Components ───────────────────────────────────────────
128app.MapRazorComponents<ShopPlatform.Components.App>()
129 .AddInteractiveServerRenderMode();
130
131// ── 기본 OG 이미지 생성 ───────────────────────────────────
132var wwwroot = Path.Combine(app.Environment.ContentRootPath, "wwwroot");
134
135// ── 샘플 데이터 시드 ──────────────────────────────────────
137 app.Services.GetRequiredService<TenantDbContextFactory>(),
138 app.Services.GetRequiredService<IShopTenantStore>(),
139 app.Services.GetRequiredService<PaymentKeyProtector>());
140
141app.Run();
142
143// 재고 차감 — 무한 재고 상품은 건드리지 않음
144#pragma warning disable CS1587
171static void DeductStock(TenantDbContext db, IEnumerable<OrderLine> lines)
172{
173 foreach (var line in lines)
174 {
175 var product = db.Products.Find(line.ProductId);
176 if (product == null || product.IsUnlimitedStock) continue;
177 product.Stock = Math.Max(0, product.Stock - line.Quantity);
178 }
179}
180#pragma warning restore CS1587
181
182// appsettings 경로가 상대 경로면 실행 파일 기준으로 절대 경로로 확장
183#pragma warning disable CS1587
218static string ResolvePath(string? configuredPath, string fallback)
219{
220 if (string.IsNullOrWhiteSpace(configuredPath))
221 {
222 return fallback;
223 }
224
225 return Path.IsPathRooted(configuredPath)
226 ? configuredPath
227 : Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, configuredPath));
228}
229#pragma warning restore CS1587
var wwwroot
Definition Program.cs:132
var builder
Definition Program.cs:10
AuthOptions authOptions
Definition Program.cs:18
var app
Definition Program.cs:52
static void DeductStock(TenantDbContext db, IEnumerable< OrderLine > lines)
Definition Program.cs:171
var shopOpts
Definition Program.cs:14
string usersDbPath
Definition Program.cs:20
static string ResolvePath(string? configuredPath, string fallback)
Definition Program.cs:218
static async Task SeedCodemaruAsync(TenantDbContextFactory dbFactory, IShopTenantStore tenantStore, PaymentKeyProtector keyProtector)
Definition ShopSeeder.cs:75
static ShopOptions From(IConfiguration cfg)
Task< PaymentConfirmResult > ConfirmAsync(string paymentKey, string orderId, int amount)
Task< ShopConfig?> GetAsync(string slug)
static void EnsureDefault(string wwwrootPath)