docs: add inter-plugin communication (RPC + discovery) docs to README

This commit is contained in:
Кирилл Осипков 2026-05-28 09:17:15 +03:00
parent d9c87aef0a
commit 11e1827031
1 changed files with 132 additions and 12 deletions

144
README.md
View File

@ -19,6 +19,7 @@ SDK для створення плагінів ERP-платформи. Міст
- [Інтерфейси](#інтерфейси) - [Інтерфейси](#інтерфейси)
- [Attribute Access Control (ABAC)](#attribute-access-control-abac) - [Attribute Access Control (ABAC)](#attribute-access-control-abac)
- [Події та RabbitMQ](#події-та-rabbitmq) - [Події та RabbitMQ](#події-та-rabbitmq)
- [Міжплагінна комунікація](#міжплагінна-комунікація)
- [CLI — create-erp-plugin](#cli--create-erp-plugin) - [CLI — create-erp-plugin](#cli--create-erp-plugin)
- [Експорт SDK](#експорт-sdk) - [Експорт SDK](#експорт-sdk)
@ -64,24 +65,32 @@ npx create-erp-plugin my-plugin --port 3007
``` ```
┌───────────────────┐ HTTP/REST ┌──────────────┐ ┌───────────────────┐ HTTP/REST ┌──────────────┐
│ Plugin (BE) │ ◄──────────────────► │ Core (BE) │ │ Plugin A (BE) │ ◄──────────────────► │ Core (BE) │
│ CoreService │ RabbitMQ │ Port 3001 │ │ CoreService │ RabbitMQ │ Port 3001 │
│ AttributeAccess │ ◄──────────────────► │ │ │ AttributeAccess │ ◄──────────────────► │ │
└────────┬──────────┘ └──────────────┘ └────────┬──────────┘ └──────────┬───┘
│ ┌───────────────────────▲ │
Module Federation │ Plugin→Plugin│ HTTP RPC Proxy │ │
│ v1.3.0 │ │ │
┌────────▼──────────┐ │ ┌──────┴───────┐ │ │
│ Plugin (FE) │ │ │ Plugin B (BE)│ │ │
│ React + Vite │ │ │ callPlugin │───────────────┘ │
│ host/UIKit │ │ └──────────────┘ │
└───────────────────┘ │ │
Module Federation Module Fed.
│ │
┌────────▼──────────┐ ┌──────────▼───┐
│ Plugin A (FE) │ │ Plugin B(FE)│
│ React + Vite │ │ React + Vite│
│ host/UIKit │ │ host/UIKit │
└───────────────────┘ └──────────────┘
``` ```
**Комунікація:** **Комунікація:**
1. **HTTP (REST)**`CoreService.fetchCore()` для запитів до ядра 1. **HTTP (REST)**`CoreService.fetchCore()` для запитів до ядра
2. **RabbitMQ (асинхронно)**`CoreService.emit()` для подій, `@EventPattern()` для отримання 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<T>(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()` #### `getDbName()`
Отримати назву БД (після `setupDatabase`). Отримати назву БД (після `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 ## CLI — create-erp-plugin
Генерація нового плагіна: Генерація нового плагіна:
@ -612,7 +732,7 @@ my-plugin/
``` ```
@erp/plugin-sdk @erp/plugin-sdk
├── CoreService # Головний сервіс (fetchCore, emit, register, ...) ├── CoreService # Головний сервіс (fetchCore, emit, register, callPluginApi, discoverPlugins, ...)
├── CoreClientService # RabbitMQ RPC клієнт ├── CoreClientService # RabbitMQ RPC клієнт
├── AttributeAccessService # ABAC — керування атрибутами та перевірка доступу ├── AttributeAccessService # ABAC — керування атрибутами та перевірка доступу
├── getRabbitMQOptions() # NestJS Microservice options ├── getRabbitMQOptions() # NestJS Microservice options