diff --git a/INDEX.md b/INDEX.md index a0893aa..61b97dc 100644 --- a/INDEX.md +++ b/INDEX.md @@ -1,7 +1,8 @@ # 📚 FourSat Documentation Index > آخرین بروزرسانی: February 17, 2026 -> ۲۲۰ فایل → ۳۰ فایل (تجمیع ۳ فازی + cleanup نهایی) +> ۲۲۰ فایل → ۳۰ فایل (تجمیع ۳ فازی + cleanup نهایی) +> آخرین تغییرات: فیکس ZarinPal callback، تخفیف ۱۰۰٪ اجباری، VAT checkout، ExpirePendingOrders، فیکس DeliveryStatus mapping، دیپلوی Production، فیکس CI/CD cross-deploy --- @@ -23,6 +24,10 @@ | **نقشه نوسازی UI فرانت؟** | [`ui-modernization/UI-MODERNIZATION-PLAN.md`](ui-modernization/UI-MODERNIZATION-PLAN.md) | | **معماری مدیریت فایل و تصاویر؟** | [`cms/FILE-MANAGEMENT-ARCHITECTURE.md`](cms/FILE-MANAGEMENT-ARCHITECTURE.md) | | **فیکس فلوی ثبت‌نام FrontOffice؟** | [`cms/REGISTRATION-FLOW-FIXES.md`](cms/REGISTRATION-FLOW-FIXES.md) | +| **باگ cross-deploy چه بود؟** | [`deployment/CICD-PIPELINE-GUIDE.md`](deployment/CICD-PIPELINE-GUIDE.md) | +| **سرور Production کجاست؟** | [`deployment/INFRASTRUCTURE-GUIDE.md`](deployment/INFRASTRUCTURE-GUIDE.md) | +| **تنظیمات VAT/مالیات؟** | [`cms/payment-gateway.md`](cms/payment-gateway.md) (بخش ۱۰) | +| **سرویس انقضای سفارش؟** | [`cms/payment-gateway.md`](cms/payment-gateway.md) (بخش ۱۱) | | **Audit report کامل BackOffice؟** | [`BackOffice/docs/BACKOFFICE-AUDIT.md`](../BackOffice/docs/BACKOFFICE-AUDIT.md) | --- @@ -36,6 +41,7 @@ | [club-membership-contract-system.md](business/club-membership-contract-system.md) | سیستم قرارداد عضویت: امضا، OTP، رفرش توکن | | [package-purchase-system.md](business/package-purchase-system.md) | ۳ سناریو خرید پکیج: وام دایا، پرداخت دستی، درگاه | | [daya-loan-integration.md](business/daya-loan-integration.md) | یکپارچه‌سازی وام دایا + جزئیات API + پیاده‌سازی CMS | +| [DISCOUNT-STORE-STATUS.md](business/DISCOUNT-STORE-STATUS.md) | 🔄 وضعیت فروشگاه تخفیفی: تخفیف ۱۰۰٪ اجباری + ZarinPal + VAT + ExpirePendingOrders — Production Deploy ✅ | | [discount-shop-business.md](business/discount-shop-business.md) | فروشگاه تخفیفی: پرداخت ترکیبی، درصد تخفیف، entity design | | [manual-payment-system.md](business/manual-payment-system.md) | پرداخت دستی: کارت به کارت، تأیید ادمین، آپلود FMS | @@ -48,7 +54,7 @@ | [FRONTOFFICE-CMS-API-COMPATIBILITY.md](cms/FRONTOFFICE-CMS-API-COMPATIBILITY.md) | ماتریس سازگاری API بین FrontOffice و CMS | | [ICURRENTUSERSERVICE-IMPLEMENTATION.md](cms/ICURRENTUSERSERVICE-IMPLEMENTATION.md) | پترن JWT + ICurrentUserService در endpointهای Customer | | [ADMIN-CUSTOMER-SEPARATION-FIX.md](cms/ADMIN-CUSTOMER-SEPARATION-FIX.md) | 🆕 فیکس جداسازی Admin/Customer: حذف JWT fallback از ۸ handler + resolve صریح در ۴ endpoint | -| [payment-gateway.md](cms/payment-gateway.md) | IPaymentGatewayService: Mock vs Daya، پیاده‌سازی payout | +| [payment-gateway.md](cms/payment-gateway.md) | 🔄 IPaymentGatewayService: ZarinPal مستقیم + تخفیف ۱۰۰٪ اجباری + VAT + ExpirePendingOrders + فیکس DeliveryStatus mapping + دیپلوی Production | | [payment-architecture-pyms.md](cms/payment-architecture-pyms.md) | معماری PYMS: جریان پرداخت BFF→PYMS→Gateway→CMS | | [chatika-integration.md](cms/chatika-integration.md) | یکپارچه‌سازی Chatika AI: Hangfire worker، retry logic | | [club-feature-management-services.md](cms/club-feature-management-services.md) | CQRS سرویس‌های مدیریت ClubFeature | @@ -65,9 +71,9 @@ | فایل | توضیح | |------|-------| | [OFFLINE-DEPLOYMENT-GUIDE.md](deployment/OFFLINE-DEPLOYMENT-GUIDE.md) | راهنمای جامع استقرار آفلاین + تنظیمات Nexus | -| [CICD-PIPELINE-GUIDE.md](deployment/CICD-PIPELINE-GUIDE.md) | 🆕 راهنمای CI/CD Pipeline: معماری DinD، فیکس‌های dockerd، Runner ConfigMap، عیب‌یابی | -| [INFRASTRUCTURE-GUIDE.md](deployment/INFRASTRUCTURE-GUIDE.md) | مشخصات سرور، DB credentials، Gitea، وضعیت استقرار | -| [SERVER-MIRRORS-CONFIG.md](deployment/SERVER-MIRRORS-CONFIG.md) | تنظیمات mirror: K3s registries.yaml، containerd | +| [CICD-PIPELINE-GUIDE.md](deployment/CICD-PIPELINE-GUIDE.md) | 🔄 راهنمای CI/CD Pipeline: معماری DinD، فیکس‌های dockerd، Runner ConfigMap، عیب‌یابی، SERVER_PASSWORD، باگ cross-deploy، قالب workflow Production | +| [INFRASTRUCTURE-GUIDE.md](deployment/INFRASTRUCTURE-GUIDE.md) | 🔄 مشخصات سرور Staging + Production، DB credentials دوگانه، Gitea، Kestrel protocol، Ingress annotations، Proto v0.0.179 | +| [SERVER-MIRRORS-CONFIG.md](deployment/SERVER-MIRRORS-CONFIG.md) | 🔄 تنظیمات mirror: K3s registries.yaml Staging + Production، containerd | | [INGRESS-NGINX-WARNING.md](deployment/INGRESS-NGINX-WARNING.md) | ⚠️ هشدار K3s: مشکل hostNetwork در ingress-nginx | ## 📂 ui-modernization/ — مستندات نوسازی UI (۳ فایل) diff --git a/business/DISCOUNT-STORE-STATUS.md b/business/DISCOUNT-STORE-STATUS.md index 670059f..4cbbdf3 100644 --- a/business/DISCOUNT-STORE-STATUS.md +++ b/business/DISCOUNT-STORE-STATUS.md @@ -1,8 +1,8 @@ # فروشگاه تخفیفی — وضعیت پیاده‌سازی و تسک‌ها > **تاریخ:** ۱۴۰۴/۱۱/۲۲ (2026-02-11) -> **آخرین بروزرسانی:** ۱۴۰۴/۱۱/۲۳ -> **وضعیت کلی:** بکند کامل ✅ | بک‌آفیس کامل ✅ | فرانت‌آفیس کامل ✅ +> **آخرین بروزرسانی:** ۱۴۰۴/۱۱/۲۸ (2026-02-17) +> **وضعیت کلی:** بکند کامل ✅ | بک‌آفیس کامل ✅ | فرانت‌آفیس کامل ✅ | Production Deploy ✅ --- @@ -162,22 +162,28 @@ │ هر محصول: قیمت × تعداد │ تخفیف هر محصول: price × count × maxDiscountPercent / 100 │ جمع کل / جمع تخفیف / مبلغ درگاه - ├─ موجودی تخفیفی کاربر: XXX تومان - ├─ کاربر تعیین می‌کند چقدر از تخفیفی استفاده کند (≤ سقف مجاز) + ├─ مالیات ۹٪ روی مبلغ درگاه + ├─ مبلغ قابل پرداخت = مبلغ درگاه + مالیات + ├─ ⚠️ تخفیف اجباری: همیشه حداکثر (MaxDiscountPercent) اعمال می‌شود └─ [پرداخت] ↓ PlaceOrder RPC: - ├─ بررسی موجودی + محاسبه - ├─ ساخت سفارش (Pending) + ├─ بررسی موجودی + محاسبه (MaxDiscountPercent اجباری) + ├─ ساخت سفارش (PaymentStatus=Pending) ├─ رزرو موجودی انبار - ├─ اگر gateway_amount > 0 → payment_url برگردانده می‌شود + ├─ اگر gateway_amount > 0 → ZarinPal payment_url └─ اگر gateway_amount = 0 → سفارش مستقیم تکمیل ↓ -ریدایرکت به درگاه (اگر لازم باشد) +ریدایرکت به ZarinPal ↓ -CompleteOrderPayment RPC (بعد از callback): - ├─ success → کسر DiscountBalance + تأیید فروش + ثبت تراکنش - └─ failure → آزادسازی رزرو انبار + لغو سفارش +Callback → CompleteOrderPayment RPC: + ├─ success → کسر DiscountBalance + تأیید + PaymentTransaction + DeliveryStatus=Pending + └─ failure → آزادسازی رزرو انبار + PaymentStatus=Reject + DeliveryStatus=Cancelled + ↓ +ExpirePendingOrdersService (Background): + ├─ هر ۵ دقیقه چک می‌کند + ├─ سفارشات Pending بالای ۳۰ دقیقه → Reject + Cancelled + └─ آزادسازی رزرو انبار ``` --- diff --git a/cms/payment-gateway.md b/cms/payment-gateway.md index 294fa05..45f798a 100644 --- a/cms/payment-gateway.md +++ b/cms/payment-gateway.md @@ -772,6 +772,289 @@ catch (Exception ex) --- -**Last Updated**: 2024-12-02 -**Version**: 1.0 -**Status**: ✅ Production Ready +--- + +## 🆕 فاز ۲ — ZarinPal + PaymentTransaction (بهمن ۱۴۰۴) + +### ۴. ZarinPalPaymentService (فعال) + +**Purpose**: درگاه پرداخت مستقیم زرین‌پال — بدون PYMS واسط + +**Configuration**: +```json +{ + "UseRealPaymentGateway": true, + "PaymentProvider": "zarinpal", + "ZarinPal": { + "MerchantId": "6b098fc8-f490-47a1-aac3-1de1a1b84404", + "UseSandbox": true + } +} +``` + +**API Endpoints**: + +#### InitiatePayment (درخواست پرداخت) +``` +POST https://sandbox.zarinpal.com/pg/v4/payment/request.json +{ + "merchant_id": "...", + "amount": 100000, + "description": "خرید پکیج طلایی", + "callback_url": "https://cms.se.kbs1.ir/api/payment/callback", + "metadata": { "mobile": "09123456789" } +} + +Response: +{ + "data": { + "authority": "A00000000000000000000000000123456", + "code": 100 + } +} +``` + +#### VerifyPayment (تأیید پرداخت) +``` +POST https://sandbox.zarinpal.com/pg/v4/payment/verify.json +{ + "merchant_id": "...", + "authority": "A00000000000000000000000000123456", + "amount": 100000 +} + +Response: +{ + "data": { + "code": 100, + "ref_id": 123456789, + "card_pan": "6037****1234", + "card_hash": "...", + "fee_type": "Merchant", + "fee": 0 + } +} +``` + +**Sandbox URL**: `https://sandbox.zarinpal.com/pg/StartPay/{Authority}` +**Production URL**: `https://zarinpal.com/pg/StartPay/{Authority}` + +**PaymentVerificationResult (بروز‌شده)**: +```csharp +public class PaymentVerificationResult +{ + public bool IsSuccess { get; set; } + public string RefId { get; set; } + public string? TrackingCode { get; set; } + public decimal Amount { get; set; } + public string? Message { get; set; } + public string? CardPan { get; set; } // 🆕 شماره کارت ماسک‌شده + public string? CardHash { get; set; } // 🆕 هش کارت + public int? VerificationCode { get; set; } // 🆕 کد تأیید زرین‌پال +} +``` + +**Service Registration (بروز‌شده)**: +```csharp +var paymentProvider = configuration.GetValue("PaymentProvider", "zarinpal"); + +if (paymentProvider?.ToLower() == "zarinpal") +{ + services.AddHttpClient(); +} +``` + +--- + +### ۵. جدول PaymentTransaction (جداگانه از Transaction) + +**Purpose**: ذخیره جزئیات سطح درگاه — جدا از Transaction entity اصلی + +**Entity**: `Domain/Entities/Payment/PaymentTransaction.cs` + +```csharp +public class PaymentTransaction : BaseEntity +{ + public string GatewayProvider { get; set; } // "zarinpal" + public string MerchantId { get; set; } + public long Amount { get; set; } + public string? CallbackUrl { get; set; } + public string? Description { get; set; } + public string? Mobile { get; set; } + public long? UserId { get; set; } + + // Request + public int? RequestStatusCode { get; set; } // 100 = success + public string? RequestStatusMessage { get; set; } + public string? Authority { get; set; } // ZarinPal authority + + // Verification + public bool PaymentStatus { get; set; } + public int? VerificationStatusCode { get; set; } + public string? VerificationStatusMessage { get; set; } + public string? CardHash { get; set; } + public string? CardPan { get; set; } // ماسک‌شده: 6037****1234 + public long? RefId { get; set; } + + // Relations + public long? TransactionId { get; set; } + public long? OrderId { get; set; } +} +``` + +**Indexes**: Authority, GatewayProvider, UserId, TransactionId, RefId +**Migration**: `AddPaymentTransactionTable` + +**جریان کامل پرداخت**: +``` +1. PlaceOrderCommandHandler → InitiatePayment → PaymentTransaction ایجاد (PaymentStatus=false) +2. کاربر → ریدایرکت به ZarinPal +3. ZarinPal → Callback به /api/payment/callback +4. PaymentCallbackController → VerifyPayment → PaymentTransaction بروز (PaymentStatus=true, CardPan, RefId) +5. CompleteOrderPaymentCommandHandler → Transaction + Order + Wallet بروز +``` + +**مصرف‌کننده‌ها**: +| سرویس | عملیات | +|--------|--------| +| `PlaceOrderCommandHandler` | ایجاد PaymentTransaction بعد از InitiatePayment | +| `PaymentCallbackController` | بروزرسانی بعد از VerifyPayment | +| `TransactionsService` | ایجاد/بروزرسانی در CustomerPaymentRequest/Verification | +| `PackageService` | ایجاد/بروزرسانی در CustomerPurchasePackage/Verify | + +--- + +### ۶. فیکس نمایش وضعیت پرداخت سفارشات تخفیفی + +**مشکل**: `DiscountOrderService.GetOrderById/GetUserOrders` از `Mapster.Adapt<>()` استفاده می‌کرد. نام‌ها متفاوت بودند: +- Domain: `PaymentStatus` (enum: Success=0, Reject=1, Pending=2) +- Proto: `payment_completed` (bool) + +Mapster نمی‌تونست enum رو به bool مپ کنه → همیشه `false` (در انتظار پرداخت). + +**رفع**: جایگزینی Mapster با مپینگ دستی: +```csharp +PaymentCompleted = result.PaymentStatus == DomainEnums.PaymentStatus.Success +``` + +### ۷. فیکس DeliveryStatus بعد از پرداخت + +**مشکل**: فروشگاه تخفیفی بعد از پرداخت موفق، `DeliveryStatus = InTransit` (ارسال شده) ست می‌کرد. ولی فروشگاه عادی `Pending` نگه می‌داشت. + +**رفع**: هر دو handler (`CompleteOrderPaymentCommandHandler` و `PlaceOrderCommandHandler`) به `DeliveryStatus.Pending` تغییر کردند — ادمین باید وضعیت پستی رو مشخص کنه. + +--- + +### ۸. فیکس ZarinPal Callback URL (اسفند ۱۴۰۴) + +**مشکل**: `PurchasePackageCommandHandler` از `yourdomain.com` به صورت hardcode استفاده می‌کرد. + +**رفع**: خواندن از `IConfiguration`: +```csharp +var cmsBaseUrl = _configuration["CmsBaseUrl"]; +var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"]; +``` + +**appsettings.json (Production)**: +```json +{ + "CmsBaseUrl": "https://cms.kbs1.ir", + "FrontOfficeBaseUrl": "https://foursat.kbs1.ir" +} +``` +**appsettings.json (Staging)**: +```json +{ + "CmsBaseUrl": "https://cms.se.kbs1.ir", + "FrontOfficeBaseUrl": "https://foursat.se.kbs1.ir" +} +``` + +--- + +### ۹. اجبار تخفیف ۱۰۰٪ (اسفند ۱۴۰۴) + +**تغییر بیزینسی**: کاربر دیگه نمی‌تونه درصد تخفیف رو انتخاب کنه — **همیشه حداکثر تخفیف** (MaxDiscountPercent) اعمال می‌شه. + +**تغییرات CMS (بکند):** +- `PlaceOrderCommandHandler`: همیشه `MaxDiscountPercent` محصول استفاده می‌شه +- فیلد `requested_discount_percent` از request نادیده گرفته می‌شه + +**تغییرات FrontOffice:** +- حذف `MudSlider` و `MudNumericField` از `Checkout.razor` +- حذف کامل بخش نمایش موجودی تخفیفی +- Badge محصولات: نمایش درصد واقعی (مثلاً "۳۰٪ تخفیف") بجای "۱۰۰٪ تخفیفی" + +--- + +### ۱۰. نمایش مالیات (VAT) در Checkout (اسفند ۱۴۰۴) + +جدول خلاصه مالی کامل اضافه شد: + +| فیلد | توضیح | +|------|-------| +| جمع کل | قبل از تخفیف | +| تخفیف | مجموع DiscountAmount | +| مبلغ پس از تخفیف | بعد از کسر تخفیف | +| مالیات ۹٪ | `VatCalculator` روی مبلغ درگاه | +| **مبلغ قابل پرداخت** | مبلغ درگاه + مالیات | + +--- + +### ۱۱. سرویس Expire سفارشات معلق (اسفند ۱۴۰۴) + +**فایل**: `ExpirePendingOrdersService.cs` — `BackgroundService` + +| تنظیم | مقدار | +|-------|-------| +| بررسی | هر ۵ دقیقه | +| انقضا | بعد از ۳۰ دقیقه `PaymentStatus=Pending` | +| عملیات | `PaymentStatus=Reject`, `DeliveryStatus=Cancelled`, آزادسازی رزرو انبار | +| ساعت | `DateTime.Now` (نه UtcNow — DB از ساعت محلی استفاده می‌کنه) | + +--- + +### ۱۲. فیکس DeliveryStatus مپینگ (اسفند ۱۴۰۴) + +**مشکل**: Domain `DeliveryStatus.Pending(1)` مستقیم cast به Proto `PROCESSING(1)` می‌شد → سفارشات failed نشون می‌دادن "در حال پردازش". + +**راه‌حل**: `MapDeliveryStatus()` و `MapPaymentStatus()` اضافه شدن: + +| Domain | Proto | +|--------|-------| +| `PaymentStatus.Success(0)` | `COMPLETED(1)` | +| `PaymentStatus.Reject(1)` | `FAILED(2)` | +| `PaymentStatus.Pending(2)` | `PENDING(0)` | +| `DeliveryStatus.None(0)` | `PENDING(0)` | +| `DeliveryStatus.Pending(1)` | `PROCESSING(1)` | +| `DeliveryStatus.InTransit(2)` | `SHIPPED(2)` | +| `DeliveryStatus.Delivered(3)` | `DELIVERED(3)` | +| `DeliveryStatus.Returned/Cancelled(4,5)` | `CANCELLED(4)` | + ++ وقتی پرداخت ناموفقه: `order.DeliveryStatus = DeliveryStatus.Cancelled` + +--- + +### ۱۳. استقرار Production (اسفند ۱۴۰۴) + +**Merge از `kub-stage` به `production`** — هر ۳ ریپو: +- CMS: ۳ conflict حل شد (workflow, Dockerfile, appsettings) +- FrontOffice: ۱ conflict (workflow) +- BackOffice: ۲ conflict (workflow, Dockerfile) + +**Migration دیتابیس Production**: ۷ migration اعمال شد: +1. `AddDiscountProductImages` +2. `AddInventorySystem` +3. `u19` + `u20` +4. `AddBlogAndContentEntities` +5. `RemoveImagePathMaxLength` +6. `AddPaymentTransactionTable` + +**ZarinPal در Production**: `UseSandbox: true` → "درگاه فعال نمیباشد" (عمدی) + +--- + +**Last Updated**: February 17, 2026 +**Version**: 3.0 +**Proto Version**: 0.0.179 +**Status**: ✅ ZarinPal Active (Sandbox) + PaymentTransaction + ExpireOrders + Production Deployed diff --git a/deployment/CICD-PIPELINE-GUIDE.md b/deployment/CICD-PIPELINE-GUIDE.md index 5927d81..86bdc8c 100644 --- a/deployment/CICD-PIPELINE-GUIDE.md +++ b/deployment/CICD-PIPELINE-GUIDE.md @@ -1,6 +1,6 @@ # 🔧 راهنمای CI/CD Pipeline — Gitea Actions + K3s -> آخرین بروزرسانی: February 11, 2026 +> آخرین بروزرسانی: February 17, 2026 --- @@ -230,6 +230,110 @@ DOCKER_BUILDKIT=0 docker build --network host -t ... . --- +### مشکل ۸: `COPY libs/` fails in Docker build (BackOffice) + +**خطا:** +``` +COPY failed: file not found in build context: stat libs/: file does not exist +``` + +**علت:** Dockerfile خط `COPY ["libs/", "libs/"]` داشت ولی `libs/` خارج از Docker build context (`src/`) بود. قبلاً BFF DLLها استفاده می‌شدن، ولی حالا از NuGet package مستقیم استفاده می‌شه. + +**راه‌حل:** +1. حذف `COPY ["libs/", "libs/"]` از Dockerfile +2. تغییر `ProjectReference` به `PackageReference` در csproj: +```xml + + + + +``` + +--- + +### مشکل ۹: ProjectReference خارج از Docker context (FrontOffice/BackOffice) + +**خطا:** +``` +error CS0246: The type or namespace name 'CustomerAddressModel' could not be found +``` + +**علت:** csproj از `ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/..."` استفاده می‌کرد. در Docker build context فقط `src/` موجوده → CMS قابل دسترسی نیست. + +**راه‌حل:** +1. بامپ نسخه پروتوباف (`0.0.177` → `0.0.178`) +2. `dotnet pack -c Release` و push به Nexus +3. تغییر هر دو پروژه (FrontOffice + BackOffice) به `PackageReference` + +```bash +# Pack & Push +cd CMS/src/CMSMicroservice.Protobuf +dotnet pack -c Release +dotnet nuget push bin/Release/Foursat.CMSMicroservice.Protobuf.0.0.178.nupkg \ + --source http://194.5.195.53:32081/repository/foursat-nuget-hosted/index.json \ + --api-key admin:87zH26nbqT --skip-duplicate +``` + +--- + +### مشکل ۱۰: `nginx:alpine` TLS handshake timeout + +**خطا:** +``` +Get "https://registry-1.docker.io/v2/": net/http: TLS handshake timeout +``` + +**علت:** Dockerfile خط `FROM nginx:alpine` مستقیم از Docker Hub پول می‌کرد ولی سرور به Docker Hub دسترسی نداره. + +**راه‌حل:** تغییر به رجیستری لوکال: +```dockerfile +# قبل +FROM nginx:alpine AS final +# بعد +FROM 194.5.195.53:32082/nginx:alpine AS final +``` + +--- + +### مشکل ۱۱: SERVER_PASSWORD secret missing → Permission denied + +**خطا:** +``` +Permission denied, please try again. +``` + +**علت:** سکرت `SERVER_PASSWORD` در ریپو Gitea تنظیم نشده بود. Pipeline از `sshpass -e` با `${{ secrets.SERVER_PASSWORD }}` برای SSH استفاده می‌کنه. + +**راه‌حل:** اضافه کردن سکرت از طریق Gitea API: +```bash +curl -sk -u "admin:87zH26nbqT" -X PUT \ + "https://git.se.kbs1.ir/api/v1/repos/admin/BackOffice/actions/secrets/SERVER_PASSWORD" \ + -H "Content-Type: application/json" -d '{"data":"87zH26nbqT"}' +``` + +--- + +### مشکل ۱۲: CMS ingress 502 — backend-protocol: GRPC + +**خطا:** `https://cms.se.kbs1.ir/` → 502 Bad Gateway + +**علت:** CMS ingress annotation `backend-protocol: GRPC` داشت + Kestrel فقط `Http2`. مرورگر HTTP/1.1 می‌فرسته → nginx نمی‌تونه به gRPC backend فوروارد کنه. + +**راه‌حل (دو تغییر):** +1. Kestrel protocol → `Http1AndHttp2` (هم gRPC هم REST): +```bash +kubectl set env deployment/cms Kestrel__EndpointDefaults__Protocols=Http1AndHttp2 +``` +2. حذف GRPC annotations از ingress: +```bash +kubectl annotate ingress cms-ingress nginx.ingress.kubernetes.io/backend-protocol- +kubectl annotate ingress cms-ingress nginx.ingress.kubernetes.io/grpc-backend- +``` + +> ⚠️ FrontOffice از gRPC-Web استفاده می‌کنه که روی HTTP/1.1 هم کار می‌کنه. + +--- + ## 🔄 تغییرات prod-deploy (قدیم → جدید) | مورد | قدیم (prod-deploy) | جدید | @@ -290,7 +394,15 @@ ubuntu-22.04 → docker://docker.gitea.com/runner-images:ubuntu-22.04 | Secret | استفاده | |--------|---------| | `REGISTRY_PASSWORD` | پسورد Docker registry (admin) | -| `SERVER_PASSWORD` | پسورد SSH سرور (root) | +| `SERVER_PASSWORD` | پسورد SSH سرور (root) — ⚠️ باید در هر ۳ ریپو ست بشه | + +> **نکته:** اگر `SERVER_PASSWORD` ست نباشه، مرحله Deploy با `Permission denied` فیل می‌شه. +> با API اضافه کنید: +> ```bash +> curl -sk -u "admin:PASSWORD" -X PUT \ +> "https://git.se.kbs1.ir/api/v1/repos/admin/REPO/actions/secrets/SERVER_PASSWORD" \ +> -H "Content-Type: application/json" -d '{"data":"PASSWORD"}' +> ``` --- @@ -416,3 +528,89 @@ jobs: kubectl rollout status deployment/ --timeout=180s " ``` +--- + +## 🐛 باگ بحرانی: Cross-Deployment — Push به Production ری‌دیپلوی Staging (اسفند ۱۴۰۴) + +### علائم: +- Push به برنچ `production` → هم production و هم staging ری‌دیپلوی شدند +- CMS staging pod بعد از push ریستارت شد +- Runner log: **۲ تسک CMS** بجای ۱ تسک اجرا شد + +### علت ریشه‌ای: +Gitea Act Runner **تمام فایل‌های workflow** داخل `.gitea/workflows/` برنچ push شده رو اجرا می‌کنه — حتی اگه `on.push.branches` برنچ دیگه‌ای باشه. وقتی production push شد، `kub-deploy.yml` (trigger: `kub-stage`) هم اجرا شد و ایمیج `admin/cms:latest` رو با کد production ساخت → staging از `latest` pull کرد → **staging با DB production بالا اومد!** + +### راه‌حل: +حذف workflow‌های staging از برنچ production (هر ۳ ریپو): +```bash +git rm .gitea/workflows/kub-deploy.yml .gitea/workflows/cms-stage.yml # CMS +git rm .gitea/workflows/fo-stage.yml .gitea/workflows/kub-deploy.yml # FrontOffice +git rm .gitea/workflows/bo-stage.yml .gitea/workflows/kub-deploy.yml # BackOffice +``` + +> ⚠️ **قانون طلایی:** هر برنچ فقط workflow مربوط به خودش رو داشته باشه. + +--- + +## 🐛 مشکل ۱۳: Production deploy ایمیج pull نمی‌شد + +**علت:** Production K8s از `git.foursat.afrino.co/admin/cms:prod` pull می‌کرد، ولی CI ایمیج رو به `194.5.195.53:30080` push می‌کرد. + +**راه‌حل:** +1. اضافه کردن `194.5.195.53:30080` به `/etc/rancher/k3s/registries.yaml` پروداکشن + ری‌استارت K3s +2. آپدیت deployment image: `kubectl set image deployment/cms cms=194.5.195.53:30080/admin/cms:prod` +3. فیکس `prod-deploy.yml`: `K8S_SSH_PASSWORD` → `SERVER_PASSWORD`, `rollout restart` → `set image :sha` + +--- + +## 🔄 Production Workflow Template (فعلی) + +```yaml +name: Build and Deploy to Production +on: + push: + branches: [production] +env: + REGISTRY: 194.5.195.53:30080 + IMAGE_NAME: admin/ + K8S_SERVER: 45.149.79.127 +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: 194.5.195.53:32082/docker-sshpass:latest + options: --privileged + steps: + # ... (Start Docker, Checkout, Login — مشابه staging) + - name: Build Docker Image + run: | + DOCKER_BUILDKIT=0 docker build --network host \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod . + - name: Push to Registry + run: | + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod + - name: Deploy to Production + run: | + export SSHPASS="${{ secrets.SERVER_PASSWORD }}" + sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} " + kubectl set image deployment/ =${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} + kubectl rollout status deployment/ --timeout=300s + " +``` + +> تفاوت staging vs production: staging = tag `latest` + `rollout restart` | production = tag `sha` + `set image` + +--- + +## 🏗️ مشخصات دو محیط + +| | Staging | Production | +|--|---------|------------| +| **سرور** | `194.5.195.53` | `45.149.79.127` | +| **DB** | `mssql-svc@Foursat` | `45.149.79.127,31433@KBS` | +| **Registry** | `194.5.195.53:30080` (local) | همان staging registry | +| **Image Tags** | `:latest` | `:prod` + `:sha` | +| **Branch** | `kub-stage` | `production` | +| **Domains** | `*.se.kbs1.ir` | `*.kbs1.ir` | \ No newline at end of file diff --git a/deployment/INFRASTRUCTURE-GUIDE.md b/deployment/INFRASTRUCTURE-GUIDE.md index 21fc5ee..28c10ff 100644 --- a/deployment/INFRASTRUCTURE-GUIDE.md +++ b/deployment/INFRASTRUCTURE-GUIDE.md @@ -2,12 +2,23 @@ ## 📌 Server Information +### سرور Staging | Item | Value | |------|-------| | Server IP | `194.5.195.53` | | SSH Access | `root / 87zH26nbqT` | | Kubernetes | K3s with local-path storage | | ServiceLB | K3s svclb (built-in) | +| Domains | `*.se.kbs1.ir` | + +### سرور Production +| Item | Value | +|------|-------| +| Server IP | `45.149.79.127` | +| SSH Access | `root / 87zH26nbqT` | +| Kubernetes | K3s with local-path storage | +| ServiceLB | K3s svclb (built-in) | +| Domains | `*.kbs1.ir` | --- @@ -22,13 +33,22 @@ | PVC | `mssql-pvc` (10Gi) | ### Databases: + +#### Staging (194.5.195.53): - `gitea` - Gitea metadata -- `Foursat` - Application database +- `Foursat` - Application database (staging) - `Hosein` - Application database -### Connection String: +#### Production (45.149.79.127): +- `KBS` - Application database (production) + +### Connection Strings: ``` +# Staging Server=mssql-svc,1433;Database=Foursat;User Id=sa;Password=87zH26nbqT;TrustServerCertificate=true + +# Production (env override) +Server=45.149.79.127,31433;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=true ``` --- @@ -170,11 +190,13 @@ kubectl logs -f deploy/nexus ## 🔐 Credentials Summary -| Service | Username | Password | -|---------|----------|----------| -| Server SSH | root | 87zH26nbqT | -| MSSQL | sa | 87zH26nbqT | -| Gitea | admin | (set during install) | +| Service | Username | Password | Server | +|---------|----------|----------|--------| +| Staging SSH | root | 87zH26nbqT | 194.5.195.53 | +| Production SSH | root | 87zH26nbqT | 45.149.79.127 | +| Staging MSSQL | sa | 87zH26nbqT | mssql-svc:1433 | +| Production MSSQL | sa | YourStrong@Passw0rd | 45.149.79.127:31433 | +| Gitea | admin | (set during install) | 194.5.195.53 | --- @@ -218,7 +240,42 @@ curl -s http://194.5.195.53:32082/v2/_catalog --- -*Last Updated: 2025-01-18* +--- + +## 🔧 CMS Ingress & Kestrel Protocol (بروز‌شده: February 2026) + +### تنظیمات Kestrel: +| متغیر | مقدار قبلی | مقدار فعلی | +|--------|-----------|------------| +| `Kestrel__EndpointDefaults__Protocols` | `Http2` | `Http1AndHttp2` | + +> با `Http1AndHttp2` هم gRPC (HTTP/2) و هم REST/HTTP (HTTP/1.1) روی یک پورت کار می‌کنن. + +### تنظیمات Ingress CMS: +| Annotation | مقدار قبلی | مقدار فعلی | +|-----------|-----------|------------| +| `backend-protocol` | `GRPC` | حذف شد | +| `grpc-backend` | `true` | حذف شد | +| `ssl-redirect` | `true` | `true` | +| `cert-manager.io/cluster-issuer` | `letsencrypt-prod` | `letsencrypt-prod` | + +> ⚠️ FrontOffice از gRPC-Web استفاده می‌کنه که روی HTTP/1.1 هم کار می‌کنه — نیازی به annotation GRPC نیست. + +### NuGet Package (Proto): +| پکیج | نسخه | رجیستری | +|-------|-------|--------| +| `Foursat.CMSMicroservice.Protobuf` | `0.0.179` | Nexus (`foursat-nuget-hosted`) | + +### Gitea Secrets (هر ۳ ریپو): +| Secret | CMS | FrontOffice | BackOffice | +|--------|-----|-------------|------------| +| `REGISTRY_PASSWORD` | ✅ | ✅ | ✅ | +| `SERVER_PASSWORD` | ✅ | ✅ | ✅ | +| `KUBECONFIG` | ✅ | ✅ | ✅ | + +--- + +*Last Updated: February 17, 2026* --- diff --git a/deployment/SERVER-MIRRORS-CONFIG.md b/deployment/SERVER-MIRRORS-CONFIG.md index c7826ec..c2819e6 100644 --- a/deployment/SERVER-MIRRORS-CONFIG.md +++ b/deployment/SERVER-MIRRORS-CONFIG.md @@ -1,13 +1,14 @@ # Server Mirrors Configuration -**Server:** 194.5.195.53 -**Date:** 2026-01-29 +**Staging Server:** 194.5.195.53 +**Production Server:** 45.149.79.127 +**Date:** 2026-02-17 --- ## 1. Docker Registry Mirrors (K3s) -فایل: `/etc/rancher/k3s/registries.yaml` +### Staging — `/etc/rancher/k3s/registries.yaml` (194.5.195.53) ### ترتیب Pull کردن ایمیج‌ها: 1. **Nexus** (194.5.195.53:32082) - لوکال @@ -66,6 +67,55 @@ configs: systemctl restart k3s ``` +### Production — `/etc/rancher/k3s/registries.yaml` (45.149.79.127) + +Production server از staging registry ها pull می‌کنه: + +```yaml +mirrors: + "docker.io": + endpoint: + - "http://194.5.195.53:32082" + - "https://docker.arvancloud.ir" + - "https://registry-1.docker.io" + "194.5.195.53:32082": + endpoint: + - "http://194.5.195.53:32082" + "194.5.195.53:30080": + endpoint: + - "http://194.5.195.53:30080" + "git.foursat.afrino.co": + endpoint: + - "https://git.foursat.afrino.co" + "git.se.kbs1.ir": + endpoint: + - "https://git.se.kbs1.ir" + +configs: + "194.5.195.53:32082": + auth: + username: admin + password: 87zH26nbqT + "194.5.195.53:30080": + auth: + username: admin + password: 87zH26nbqT + "git.foursat.afrino.co": + auth: + username: admin + password: 87zH26nbqT + tls: + insecure_skip_verify: true + "git.se.kbs1.ir": + auth: + username: admin + password: 87zH26nbqT + tls: + insecure_skip_verify: true +``` + +> ⚠️ Production از `194.5.195.53:30080` (Gitea container registry) برای pull ایمیج‌های CI/CD استفاده می‌کنه. + --- ## 2. APT Package Mirrors (Ubuntu 24.04 Noble)