8.4 KiB
8.4 KiB
Архитектура расширений в MonoCoop
Основные принципы
- Расширения построены на модулях NestJS с использованием шаблона "Порты и адаптеры"
- Каждое расширение наследуется от
BaseExtModuleи реализует интерфейсOnModuleInit - Расширения могут взаимодействовать с блокчейном через соответствующие порты
- Конфигурации расширений хранятся в БД и описываются с помощью Zod-схем
- Расширения регистрируются в глобальном реестре
AppRegistry
Структура расширения
Минимальная структура расширения включает:
XXX-extension.module.ts- основной модуль расширенияpackage.json- информация о пакетеREADME.md- документацияINSTALL.md- инструкции по установкеCHANGELOG.md- история изменений
Создание нового расширения
- Создайте директорию для расширения в
components/controller/src/extensions/ - Создайте основной класс расширения, наследующийся от
BaseExtModule - Определите Zod-схему для конфигурации
- Реализуйте метод
initialize() - Зарегистрируйте расширение в
extensions.registry.ts - Добавьте расширение в список дефолтных приложений в
extension-domain.service.ts
Пример структуры модуля расширения
// 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();
}
}
Управление отображением конфигурации
Zod-схемы используются для автоматического отображения формы настроек в интерфейсе пользователя.
Через интерфейс DeserializedDescriptionOfExtension из components/controller/src/types/shared/extension.types.ts
можно управлять отображением полей формы:
export const Schema = z.object({
// Базовое поле с меткой
simpleField: z.string().describe(
describeField({
label: 'Название поля',
note: 'Подсказка под полем'
})
),
// Скрытое поле для служебного использования
hiddenField: z.string().describe(
describeField({
label: 'Скрытое поле',
visible: false
})
),
// Поле с проверкой значения
validatedField: z.number().describe(
describeField({
label: 'Поле с валидацией',
rules: ['val >= 5', 'val <= 100'],
})
),
// Форматированное поле с префиксом и суффиксом
formattedField: z.number().describe(
describeField({
label: 'Форматированное поле',
prepend: '$',
append: 'USD',
})
),
// Многострочное текстовое поле
multilineField: z.string().describe(
describeField({
label: 'Многострочное поле',
maxRows: 5,
minLength: 10,
maxLength: 1000
})
),
});
Доступные поля для управления отображением:
label- название поля (обязательное)note- пояснение или подсказкаvisible- видимость поля (по умолчанию true)rules- правила валидации в виде строковых выраженийmask- маска для вводаfillMask- автозаполнение маскиminLength/maxLength- ограничения длины для текстовых полейmaxRows- количество строк для многострочного вводаappend/prepend- текст до/после значения поля
Взаимодействие с блокчейном
Для взаимодействия с блокчейном:
- Определите порт в доменном слое (например,
SovietBlockchainPort) - Инжектируйте порт в конструкторе расширения через DI
- Используйте методы порта для взаимодействия с блокчейном
@Inject(SOVIET_BLOCKCHAIN_PORT) private readonly sovietBlockchainPort: SovietBlockchainPort
// ...
const decisions = await this.sovietBlockchainPort.getDecisions(coopname);
Настройка планировщика задач
Расширения могут использовать cron-задачи для периодического выполнения операций:
import cron from 'node-cron';
// Регистрация cron-задачи (каждые N минут)
const cronExpression = `*/${this.plugin.config.checkInterval} * * * *`;
cron.schedule(cronExpression, () => {
this.logger.info('Запуск запланированной задачи');
this.runTask();
});
Работа с конфигурацией
- Определите Zod-схему для конфигурации
- Используйте
describeFieldдля добавления UI-метаданных к полям - Получайте и обновляйте конфигурацию через репозиторий
extensionRepository
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);
Логирование действий
Расширения должны логировать свои действия:
- Используйте
WinstonLoggerServiceдля системного логирования - Используйте
LogExtensionDomainRepositoryдля хранения логов в БД
// Системное логирование
this.logger.info(`Выполнение операции для ${id}`);
// Сохранение лога в БД
await this.logExtensionRepository.push(this.name, {
type: 'operation',
timestamp: new Date().toISOString(),
data: { ... },
});
Регистрация расширения
После создания расширения, добавьте его в extensions.registry.ts:
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:
getDefaultApps(): Partial<ExtensionDomainEntity>[] {
return [
// ...
{
name: 'myExtension',
enabled: true,
config: {
// Дефолтные значения конфигурации
parameter1: 'value1',
parameter2: 42,
},
},
// ...
];
}