{
  "title": "استاندارد همگام‌سازی مجوزها و تولید SDK (Permission & SDK Sync Standard)",
  "slug": "team/platform/standards/permission-and-sdk-sync-standard",
  "url": "/docs/team/platform/standards/permission-and-sdk-sync-standard",
  "frontmatter": {},
  "sections": [
    {
      "level": 1,
      "heading": "استاندارد همگام‌سازی مجوزها و تولید SDK (Permission & SDK Sync Standard)",
      "content": "> [!IMPORTANT]\n> **هدف سند:** این سند راهکار قطعی و استاندارد پلتفرم NONS را برای حل مشکل همیشگی مغایرت کلیدهای مجوز (Permission Keys) و هدرهای احراز هویت بین فرانت‌اند و بک‌اند ارائه می‌دهد. این طرح شامل مدل تک‌منبع حقیقت (Single Source of Truth) بر پایه Protobuf، مکانیسم تزریق هدر هوشمند در SDK و خودکارسازی ثبت مجوزها است.\n\n> [!NOTE]\n> **وضعیت پیاده‌سازی:** این سند در تاریخ ۲۰۲۶-۰۷-۲۳ به‌عنوان DRAFT نوشته شد. از آن زمان تاکنون:\n> - ✅ **Proto به‌عنوان SSOT** — پیاده‌سازی شده (فاز ۵: مالکیت توزیع‌شده در `permissions.meta.yaml`)\n> - ✅ **Pipeline codegen** — پیاده‌سازی شده (Go + TS + OpenAPI + SDK + aggregation)\n> - ❌ **`createSmartClient`** — پیاده‌سازی نشده. Browser SDK ساده باقی مانده (Bearer token حذف شد)\n> - ❌ **Self-registration در boot سرویس** — پیاده‌سازی نشده. ثبت مجوزها از طریق `permissions.meta.yaml` + codegen انجام می‌شود\n> - ❌ **متادیتا در Proto** — پیاده‌سازی نشده. متادیتا (name, description, group) در YAML سرویس‌ها نگهداری می‌شود\n> \n> بخش‌های پیاده‌سازی‌نشده برای فازهای بعدی ثبت شده‌اند. برای مستندات وضعیت فعلی، به [مدل مجوزها](/docs/team/platform/permission-model) مراجعه کنید.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱. صورت مسئله و چالش‌ها (The Problem Statement)",
      "content": "در فازهای اولیه توسعه، چند ناهماهنگی اساسی شناسایی شد که توسعه سریع و پایدار را با مشکل مواجه می‌کند:\n1. **کلیدهای مجوز اختراعی (Invented Permission Keys):** فرانت‌اند به صورت دستی کلیدهایی مانند `users.view` یا `roles.manage` را تعریف می‌کرد، در حالی که بک‌اند دیتابیس خود را با کلیدهای واقعی مانند `admin.access` یا `product.create` مقداردهی (Seed) می‌کرد. این مغایرت باعث پنهان شدن منوها یا قفل شدن صفحات می‌شد.\n2. **قراردادهای متفاوت Auth Header:** سرویس‌های مختلف روش‌های متفاوتی برای خواندن هویت داشتند (مانند `X-User-Id` در `user-service` و `X-User-ID` در `iam-service` و کوکی Ory Kratos در `auth-service`).\n3. **شکاف تولید خودکار SDK:** کدهای جنریتور در پوشه `.nons/sdk/generated/` فایل‌های کلاینت خامی تولید می‌کردند که از هدر پیش‌فرض `Authorization: Bearer` (بدون مقدار) استفاده می‌کرد و با قراردادهای واقعی احراز هویت سرویس‌ها سازگار نبود.\n\n---"
    },
    {
      "level": 2,
      "heading": "۲. نمای کلی معماری پیشنهادی (Proposed Architecture Overview)",
      "content": "طرح پیشنهادی بر اساس مدل **«یک‌بار تعریف در پروتو، تولید خودکار و مصرف یکپارچه در فرانت و بک»** طراحی شده است:\n\n```mermaid\ngraph TD\n    A[contracts/permissions.proto] -->|pnpm codegen| B[Go core/platform]\n    A -->|pnpm codegen| C[packages/types & SDK]\n    \n    C -->|Auto-Generated| D[Admin Panel Registry]\n    B -->|Automatic Migrations| E[IAM PostgreSQL Database]\n    \n    D -->|Request + Dynamic Header| F[Traefik Gateway]\n    F -->|X-User-Id / Cookie| G[Microservices]\n    \n    E -->|GET /v1/iam/me/context| D\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۳. منبع واحد حقیقت (Protobuf Registry) — دو سطح",
      "content": "**Proto منبع حقیقت تعریف Keyهاست؛ IAM منبع حقیقت وضعیت runtime.**\n\nبه جای تعریف کلیدهای مجوز در دیتابیس یا فایل‌های ثابت فرانت‌اند، **مرجع رسمی تعریف کلیدها فایل `contracts/permissions.proto` است.** IAM (و نه Proto) مشخص می‌کند که کدام کاربر در runtime کدام مجوز را دارد."
    },
    {
      "level": 3,
      "heading": "پیشنهاد ساختار جدید `permissions.proto`",
      "content": "برای اینکه جزئیات نمایشی و ساختار در فرانت‌اند نیز خودکار باشد، فیلدهای متادیتای غنی به پروتو اضافه می‌شود:\n\n```proto\nsyntax = \"proto3\";\n\npackage platform;\n\noption go_package = \"core/platform;platform\";\n\n// تعریف دامنه سرویس‌ها برای دسته‌بندی مجوزها\nenum ServiceOwner {\n  SERVICE_UNSPECIFIED = 0;\n  SERVICE_IAM = 1;\n  SERVICE_USER = 2;\n  SERVICE_MARKETPLACE = 3;\n  SERVICE_FINANCE = 4;\n}\n\n// تعریف کلیدهای رسمی پلتفرم به عنوان Enum\n// این کار مانع از هاردکد کردن کلیدهای متنی اشتباه می‌شود\nenum PermissionKey {\n  PERM_UNSPECIFIED = 0;\n  \n  // سیستم ادمین عمومی\n  ADMIN_ACCESS = 1; // \"admin.access\"\n  \n  // محصولات (Marketplace)\n  PRODUCT_CREATE = 10; // \"product.create\"\n  PRODUCT_EDIT = 11;\n  PRODUCT_DELETE = 12;\n  PRODUCT_PUBLISH = 13;\n  \n  // مالی (Finance)\n  WALLET_WITHDRAW = 20; // \"wallet.withdraw\"\n  WALLET_VIEW = 21;\n}\n\nmessage PermissionDetail {\n  PermissionKey key = 1;\n  string string_value = 2;      // معادل متنی مانند \"product.create\"\n  string name_fa = 3;           // عنوان فارسی برای نمایش در پنل ادمین\n  string name_en = 4;           // عنوان انگلیسی\n  ServiceOwner owner = 5;       // سرویس مالک این مجوز\n  bool is_admin_only = 6;       // آیا فقط مخصوص ادمین است؟\n}\n```\n\n> [!TIP]\n> **مزیت این روش:** با اجرای `pnpm codegen`، در فرانت‌اند پکیج `@nons/types` ثابت‌های Enum و نگاشت متنی آن‌ها را به صورت کاملاً Type-safe دریافت می‌کند. هرگز کلید اشتباه کامپایل نخواهد شد.\n\n---"
    },
    {
      "level": 2,
      "heading": "۴. چرخه حیات همگام‌سازی خودکار (Automatic Sync Lifecycle)",
      "content": "```\n[توسعه‌دهنده] ──> ویرایش permissions.proto ──> pnpm codegen\n                                                   │\n                ┌──────────────────────────────────┴──────────────────────────────────┐\n                ▼                                                                     ▼\n    [بک‌اند (Go)]                                                        [فرانت‌اند (TypeScript)]\n    ۱. Go تولید کدهای struct در                                         ۱. تولید انواع داده `@nons/types`\n    ۲. خواندن خودکار Enumها در زمان بوت شدن سرویس                       ۲. استفاده از ثابت‌های تولیدشده در منوها\n    ۳. ثبت خودکار مجوزها در دیتابیس IAM از طریق API                     ۳. دریافت داینامیک متادیتا (فارسی/انگلیسی)\n```"
    },
    {
      "level": 3,
      "heading": "ثبت خودکار در بک‌اند (Self-Registration)",
      "content": "هر میکروسرویس در زمان Bootstrapping، از روی کدهای تولیدشده توسط پروتو، لیست مجوزهای مورد نیاز خود را به سرویس IAM گزارش می‌دهد:\n```go\n// اجرای خودکار در زمان استارت‌آپ هر سرویس\nfunc RegisterServicePermissions(client *iam.Client) {\n    client.RegisterPermissions([]platform.PermissionDetail{\n        {Key: platform.PermissionKey_PRODUCT_CREATE, StringValue: \"product.create\", NameFa: \"ایجاد محصول\"},\n    })\n}\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۵. استانداردسازی هدرهای احراز هویت در SDK (Smart Client Interceptor)",
      "content": "برای جلوگیری از ارسال هدرهای اشتباه یا خالی، کلاینت اصلی SDK در فرانت‌اند باید به الگوی **رهگیری و تزریق هوشمند هدرها (Interceptor Pattern)** مجهز شود:\n\n```ts\n// src/sdk/client.ts\nimport { useAuthStore } from '../auth/store'\n\nexport interface APIRequestOptions extends RequestInit {\n  requiresAuth?: boolean\n  serviceType?: 'user-service' | 'iam-service' | 'auth-service' | 'generic'\n}\n\nexport function createSmartClient(baseURL: string) {\n  return async function request<T>(endpoint: string, options?: APIRequestOptions): Promise<T> {\n    const authStore = useAuthStore()\n    const userId = authStore.state.user?.id\n    \n    // ۱. هدرهای پایه\n    const headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      Accept: 'application/json',\n      ...((options?.headers as Record<string, string>) || {}),\n    }\n\n    // ۲. تزریق هوشمند هدر بر اساس قرارداد سرویس مقصد\n    if (userId) {\n      if (options?.serviceType === 'user-service') {\n        headers['X-User-Id'] = userId       // قرارداد user-service (حروف کوچک d)\n      } else if (options?.serviceType === 'iam-service') {\n        headers['X-User-ID'] = userId       // قرارداد iam-service (حروف بزرگ ID)\n      }\n    }\n\n    const response = await fetch(`${baseURL}${endpoint}`, {\n      ...options,\n      credentials: 'include', // ارسال همیشگی کوکی Kratos برای احراز هویت session-based\n      headers,\n    })\n\n    if (!response.ok) {\n      throw new Error(`API Error: ${response.status} ${response.statusText}`)\n    }\n\n    return response.json()\n  }\n}\n```"
    },
    {
      "level": 3,
      "heading": "قانون تولید کد کلاینت (CodeGen Rule)",
      "content": "فایل‌های جنریتور در `.nons/sdk/generated/` باید به جای هدرهای هاردکد شده از این نمونه کلاینت توسعه‌یافته ارث‌بری کنند. بدین ترتیب توسعه‌دهنده دیگر درگیر تنظیم هدرها یا فراموش کردن `credentials: 'include'` نخواهد شد.\n\n---"
    },
    {
      "level": 2,
      "heading": "۶. گام‌های پیاده‌سازی و یکپارچه‌سازی (Next Steps Checklist)",
      "content": "برای نهایی کردن این طرح، اقدامات زیر به عنوان فازهای بعدی پیشنهاد می‌شود:\n\n- [ ] **فاز اول:** اصلاح فایل `contracts/permissions.proto` و ثبت رسمی تمام کلیدها.\n- [ ] **فاز دوم:** آپدیت ابزار Buf جنریتور برای خروجی متادیتای مجوزها در `@nons/types`.\n- [ ] **فاز سوم:** بازنویسی تمپلیت تولید خودکار کلاینت SDK در پروژه با الگوی `createSmartClient`.\n- [ ] **فاز چهارم:** فعال‌سازی مکانیسم Self-Registration در بوت میکروسرویس‌های Go.\n- [ ] **فاز پنجم:** تغییر فرمت منوی پنل ادمین از ساختار استاتیک به دریافت داینامیک از IAM."
    }
  ]
}