168 lines
6.2 KiB
Plaintext
168 lines
6.2 KiB
Plaintext
---
|
|
description:
|
|
globs: **/controller/src/extensions/**
|
|
alwaysApply: false
|
|
---
|
|
# Архитектура расширений в MonoCoop
|
|
|
|
## Основные принципы
|
|
1. Расширения построены на модулях NestJS с использованием шаблона "Порты и адаптеры"
|
|
2. Каждое расширение наследуется от `BaseExtModule` и реализует интерфейс `OnModuleInit`
|
|
3. Расширения могут взаимодействовать с блокчейном через соответствующие порты
|
|
4. Конфигурации расширений хранятся в БД и описываются с помощью Zod-схем
|
|
5. Расширения регистрируются в глобальном реестре `AppRegistry`
|
|
|
|
## Структура расширения
|
|
Минимальная структура расширения включает:
|
|
- `XXX-extension.module.ts` - основной модуль расширения
|
|
- `package.json` - информация о пакете
|
|
- `README.md` - документация
|
|
- `INSTALL.md` - инструкции по установке
|
|
- `CHANGELOG.md` - история изменений
|
|
|
|
## Создание нового расширения
|
|
1. Создайте директорию для расширения в `components/controller/src/extensions/`
|
|
2. Создайте основной класс расширения, наследующийся от `BaseExtModule`
|
|
3. Определите Zod-схему для конфигурации
|
|
4. Реализуйте метод `initialize()`
|
|
5. Зарегистрируйте расширение в `extensions.registry.ts`
|
|
6. Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`
|
|
|
|
## Пример структуры модуля расширения
|
|
```typescript
|
|
// XXX-extension.module.ts
|
|
export class XXXPlugin extends BaseExtModule {
|
|
constructor(...) {
|
|
super();
|
|
}
|
|
|
|
name = 'xxx';
|
|
plugin!: ExtensionDomainEntity<IConfig>;
|
|
public configSchemas = Schema;
|
|
|
|
async initialize() {
|
|
// Инициализация расширения
|
|
// Настройка cron-задач
|
|
}
|
|
|
|
// Дополнительные методы расширения
|
|
}
|
|
|
|
@Module({
|
|
providers: [XXXPlugin],
|
|
})
|
|
export class XXXPluginModule {
|
|
constructor(private readonly xxxPlugin: XXXPlugin) {}
|
|
|
|
async initialize() {
|
|
await this.xxxPlugin.initialize();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Взаимодействие с блокчейном
|
|
Для взаимодействия с блокчейном:
|
|
1. Определите порт в доменном слое (например, `SovietBlockchainPort`)
|
|
2. Инжектируйте порт в конструкторе расширения через DI
|
|
3. Используйте методы порта для взаимодействия с блокчейном
|
|
|
|
```typescript
|
|
@Inject(SOVIET_BLOCKCHAIN_PORT) private readonly sovietBlockchainPort: SovietBlockchainPort
|
|
// ...
|
|
const decisions = await this.sovietBlockchainPort.getDecisions(coopname);
|
|
```
|
|
|
|
## Настройка планировщика задач
|
|
Расширения могут использовать cron-задачи для периодического выполнения операций:
|
|
|
|
```typescript
|
|
import cron from 'node-cron';
|
|
|
|
// Регистрация cron-задачи (каждые N минут)
|
|
const cronExpression = `*/${this.plugin.config.checkInterval} * * * *`;
|
|
cron.schedule(cronExpression, () => {
|
|
this.logger.info('Запуск запланированной задачи');
|
|
this.runTask();
|
|
});
|
|
```
|
|
|
|
## Работа с конфигурацией
|
|
1. Определите Zod-схему для конфигурации
|
|
2. Используйте `describeField` для добавления UI-метаданных к полям
|
|
3. Получайте и обновляйте конфигурацию через репозиторий `extensionRepository`
|
|
|
|
```typescript
|
|
export const Schema = z.object({
|
|
checkInterval: z.number().describe(
|
|
describeField({
|
|
label: 'Интервал проверки (в минутах)',
|
|
note: 'Минимум: 5 минут',
|
|
rules: ['val >= 5'],
|
|
})
|
|
),
|
|
});
|
|
|
|
// Обновление конфигурации
|
|
this.plugin.config.lastCheckDate = new Date().toISOString();
|
|
await this.extensionRepository.update(this.plugin);
|
|
```
|
|
|
|
## Логирование действий
|
|
Расширения должны логировать свои действия:
|
|
1. Используйте `WinstonLoggerService` для системного логирования
|
|
2. Используйте `LogExtensionDomainRepository` для хранения логов в БД
|
|
|
|
```typescript
|
|
// Системное логирование
|
|
this.logger.info(`Выполнение операции для ${id}`);
|
|
|
|
// Сохранение лога в БД
|
|
await this.logExtensionRepository.push(this.name, {
|
|
type: 'operation',
|
|
timestamp: new Date().toISOString(),
|
|
data: { ... },
|
|
});
|
|
```
|
|
|
|
## Регистрация расширения
|
|
После создания расширения, добавьте его в `extensions.registry.ts`:
|
|
|
|
```typescript
|
|
export const AppRegistry: INamedExtension = {
|
|
myExtension: {
|
|
is_builtin: false,
|
|
is_internal: true,
|
|
is_available: true,
|
|
is_desktop: false,
|
|
title: 'Моё расширение',
|
|
description: 'Описание функциональности.',
|
|
image: 'https://example.com/image.png',
|
|
class: MyExtensionPluginModule,
|
|
schema: MyExtensionSchema,
|
|
tags: ['тег1', 'тег2'],
|
|
readme: getReadmeContent('./myExtension'),
|
|
instructions: getInstructionsContent('./myExtension'),
|
|
},
|
|
};
|
|
```
|
|
|
|
## Дефолтные настройки
|
|
Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`:
|
|
|
|
```typescript
|
|
getDefaultApps(): Partial<ExtensionDomainEntity>[] {
|
|
return [
|
|
// ...
|
|
{
|
|
name: 'myExtension',
|
|
enabled: true,
|
|
config: {
|
|
// Дефолтные значения конфигурации
|
|
parameter1: 'value1',
|
|
parameter2: 42,
|
|
},
|
|
},
|
|
// ...
|
|
];
|
|
}
|
|
``` |