diff --git a/README.md b/README.md index 3072ccd..f7e4857 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ SDK для створення плагінів ERP-платформи. Міст - [Інтерфейси](#інтерфейси) - [Attribute Access Control (ABAC)](#attribute-access-control-abac) - [Події та RabbitMQ](#події-та-rabbitmq) +- [Міжплагінна комунікація](#міжплагінна-комунікація) - [CLI — create-erp-plugin](#cli--create-erp-plugin) - [Експорт SDK](#експорт-sdk) @@ -64,24 +65,32 @@ npx create-erp-plugin my-plugin --port 3007 ``` ┌───────────────────┐ HTTP/REST ┌──────────────┐ -│ Plugin (BE) │ ◄──────────────────► │ Core (BE) │ +│ Plugin A (BE) │ ◄──────────────────► │ Core (BE) │ │ CoreService │ RabbitMQ │ Port 3001 │ │ AttributeAccess │ ◄──────────────────► │ │ -└────────┬──────────┘ └──────────────┘ - │ - Module Federation - │ -┌────────▼──────────┐ -│ Plugin (FE) │ -│ React + Vite │ -│ host/UIKit │ -└───────────────────┘ +└────────┬──────────┘ └──────────┬───┘ + │ ┌───────────────────────▲ │ + │ Plugin→Plugin│ HTTP RPC Proxy │ │ + │ v1.3.0 │ │ │ + │ ┌──────┴───────┐ │ │ + │ │ Plugin B (BE)│ │ │ + │ │ callPlugin │───────────────┘ │ + │ └──────────────┘ │ + │ │ + Module Federation Module Fed. + │ │ +┌────────▼──────────┐ ┌──────────▼───┐ +│ Plugin A (FE) │ │ Plugin B(FE)│ +│ React + Vite │ │ React + Vite│ +│ host/UIKit │ │ host/UIKit │ +└───────────────────┘ └──────────────┘ ``` **Комунікація:** 1. **HTTP (REST)** — `CoreService.fetchCore()` для запитів до ядра 2. **RabbitMQ (асинхронно)** — `CoreService.emit()` для подій, `@EventPattern()` для отримання -3. **Module Federation** — фронтенд плагіна монтується як remote module всередині host-застосунку +3. **Plugin→Plugin RPC** (v1.3.0+) — `CoreService.callPluginApi()` для виклику API іншого плагіна через core-proxy +4. **Module Federation** — фронтенд плагіна монтується як remote module всередині host-застосунку --- @@ -152,6 +161,40 @@ await coreService.sendNotification({ }); ``` +#### `callPluginApi(pluginId, path, body?)` + +Викликати API іншого плагіна через core RPC-проксі. Запит проксується ядром на `backendUrl` цільового плагіна методом POST. JWT-токен автоматично прокидyється. + +```typescript +// Викликати ендпоінт плагіна notifications +const result = await coreService.callPluginApi('notifications', 'stats', { period: 'week' }); + +// Викликати з вкладеним шляхом +const user = await coreService.callPluginApi('user-management', 'users/42'); +``` + +#### `discoverPlugins()` + +Отримати список усіх зареєстрованих плагінів з їхніми можливостями. Повертає масив маніфестів з полями: `id`, `name`, `label`, `icon`, `routePath`, `backendUrl`, `events`, `objectTypes`, `permissions`, `roles`, `database`, `menu`, `headerActions`. + +```typescript +const plugins = await coreService.discoverPlugins(); + +// Знайти плагін за id +const notifPlugin = plugins.find(p => p.id === 'notifications'); +console.log(notifPlugin.backendUrl); // http://notifications:3008 + +// Знайти всі плагіни, що оголосили певну подію +const listeners = plugins.filter(p => + p.events?.some(e => e.name === 'core.user.created') +); + +// Знайти плагіни, що працюють з певним типом об'єктів +const dealPlugins = plugins.filter(p => + p.objectTypes?.includes('deal') +); +``` + #### `getDbName()` Отримати назву БД (після `setupDatabase`). @@ -552,6 +595,83 @@ async handle(@Payload() data: any) { --- +## Міжплагінна комунікація + +До версії 1.3.0 плагіни могли спілкуватись тільки через ядро (HTTP до core + RabbitMQ broadcast). Починаючи з v1.3.0 додано підтримку прямого плагін→плагін зв'язку через HTTP RPC проксі. + +### Архітектура + +``` +Plugin A Core Backend Plugin B + | | | + |-- discoverPlugins() ----------->| | + |<-- [{ id:'notifications', ... }]| | + | | | + |-- callPluginApi('notifications', | | + | 'stats', { period:'week' })-->| | + | |-- POST /stats -------------->| + | |<-- { sent: 42 } ------------| + |<-- { sent: 42 } ----------------| | +``` + +Всі запити проходять через Core, який: +1. **Перевіряє JWT** (як і будь-який захищений ендпоінт) +2. **Визначає `backendUrl`** цільового плагіна з реєстру +3. **Проксує POST-запит** з автентифікацією та тілом + +### Discover — дізнатись які плагіни доступні + +```typescript +const plugins = await coreService.discoverPlugins(); +``` + +Кожен плагін має: +- `id`, `name`, `label`, `backendUrl` — адреса для RPC +- `events: { name, description }[]` — які події оголошує +- `objectTypes: string[]` — з якими об'єктами працює +- `permissions`, `roles` — права доступу + +### RPC — викликати API іншого плагіна + +```typescript +const stats = await coreService.callPluginApi('notifications', 'stats'); +const user = await coreService.callPluginApi('user-management', 'users/42'); +const result = await coreService.callPluginApi('global-store', 'store/set', { + key: 'my-key', + value: { foo: 'bar' }, +}); +``` + +**Обмеження:** +- Тільки POST-метод (ядро проксує POST на `backendUrl` цільового плагіна). Для GET-ендпоінтів цільового плагіна — використовуйте `@Post()` контролер з потрібною логікою. +- Потребує JWT-токена (як і будь-який захищений API). Токен автоматично прокидyється з контексту запиту. +- Якщо плагін не зареєстрований або не має `backendUrl` — повертається 404. + +### Типовий сценарій використання + +```typescript +// В сервісі плагіна A +async function notifyAndLog() { + // 1. Discovery — знайти потрібний плагін + const plugins = await this.coreService.discoverPlugins(); + const notifPlugin = plugins.find(p => p.events?.some(e => e.name === 'notification.send')); + + // 2. RPC — викликати API плагіна B + if (notifPlugin) { + await this.coreService.callPluginApi('notifications', 'send', { + userId: 1, + title: 'Event occurred', + message: 'Plugin A triggered a notification', + }); + } + + // 3. Або викликати будь-який кастомний ендпоінт + const status = await this.coreService.callPluginApi('audit-logs', 'stats/daily'); +} +``` + +--- + ## CLI — create-erp-plugin Генерація нового плагіна: @@ -612,7 +732,7 @@ my-plugin/ ``` @erp/plugin-sdk -├── CoreService # Головний сервіс (fetchCore, emit, register, ...) +├── CoreService # Головний сервіс (fetchCore, emit, register, callPluginApi, discoverPlugins, ...) ├── CoreClientService # RabbitMQ RPC клієнт ├── AttributeAccessService # ABAC — керування атрибутами та перевірка доступу ├── getRabbitMQOptions() # NestJS Microservice options