docs: بروزرسانی کامل مستندات — باگ‌ها، فیکس‌ها، دیپلوی Production، CI/CD cross-deploy

- payment-gateway.md: سکشن ۸-۱۳ (ZarinPal callback, تخفیف ۱۰۰٪, VAT, ExpirePendingOrders, DeliveryStatus mapping, Production deploy)
- CICD-PIPELINE-GUIDE.md: باگ cross-deploy, قالب workflow Production, جدول مقایسه دو محیط
- INFRASTRUCTURE-GUIDE.md: سرور Production (45.149.79.127), DB KBS, Proto v0.0.179
- DISCOUNT-STORE-STATUS.md: وضعیت Production Deploy, فلوی پرداخت جدید
- SERVER-MIRRORS-CONFIG.md: registries.yaml سرور Production
- INDEX.md: تاریخ, توضیحات بروز, لینک‌های سریع جدید
This commit is contained in:
masoodafar-web
2026-02-17 01:44:05 +03:30
parent ad31c8be97
commit 4ef4bfbeef
6 changed files with 632 additions and 32 deletions
+11 -5
View File
@@ -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 (۳ فایل)
+17 -11
View File
@@ -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
└─ آزادسازی رزرو انبار
```
---
+286 -3
View File
@@ -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<string>("PaymentProvider", "zarinpal");
if (paymentProvider?.ToLower() == "zarinpal")
{
services.AddHttpClient<IPaymentGatewayService, ZarinPalPaymentService>();
}
```
---
### ۵. جدول 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
+200 -2
View File
@@ -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 Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />
<!-- بعد -->
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.178" />
```
---
### مشکل ۹: 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/<service> --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/<service>
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/<svc> <svc>=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
kubectl rollout status deployment/<svc> --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` |
+65 -8
View File
@@ -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*
---
+53 -3
View File
@@ -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)