@coopenomics/factory
Фабрика документов кооператива — библиотека для генерации юридически значимых документов (PDF). Объединяет шаблоны из блокчейна, приватные данные из хранилища и рендерит HTML через Handlebars/Nunjucks с последующей генерацией PDF через WeasyPrint.
Основные возможности
- Более 60 шаблонов юридических документов (заявления, протоколы, решения, договоры, акты)
- Рендеринг HTML из шаблонов Handlebars и Nunjucks
- Генерация PDF через WeasyPrint
- Валидация данных по JSON Schema
- Электронная подпись документов через EOSIO-ключи
- Хранение документов и метаданных в MongoDB
- Сборка как ES-модуль и CommonJS (unbuild)
Установка
Компонент является частью монорепозитория. Установка зависимостей из корня проекта:
pnpm install
Или только для этого компонента:
pnpm install --filter @coopenomics/factory
Системные зависимости
Для генерации PDF необходим WeasyPrint.
Скрипты
| Скрипт | Команда | Описание |
|---|---|---|
build |
pnpm run build |
Сборка библиотеки (unbuild) |
dev |
pnpm run dev |
Режим разработки с автопересборкой (nodemon) |
test |
pnpm run test |
Запуск тестов (Vitest) |
lint |
pnpm run lint |
Проверка кода (ESLint) |
typecheck |
pnpm run typecheck |
Проверка типов TypeScript |
setup-indexes |
pnpm run setup-indexes |
Настройка индексов MongoDB |
Из корня монорепозитория:
pnpm run build:lib # Сборка factory + cooptypes
pnpm run dev:lib # Режим разработки factory + cooptypes
pnpm run test:component # Компонентные тесты factory
Конфигурация
Для тестирования и локальной работы необходимо подключение к MongoDB. Переменные окружения:
MONGO_URI— строка подключения к MongoDBSOURCE— источник данных (localдля тестов)
Подробное описание переменных — в файле .env-example.
Тестирование
SOURCE=local NODE_ENV=test pnpm --filter @coopenomics/factory run test
Тестовый набор покрывает генерацию документов различных типов. Тесты требуют запущенный MongoDB и используют таймаут 240 секунд.
Файлы тестов:
| Файл | Описание |
|---|---|
test/index.test.ts |
Основные тесты генерации документов |
test/wallet.test.ts |
Тесты документов кошелька |
test/meet.test.ts |
Тесты документов собраний |
test/blagorost.test.ts |
Тесты документов программы «Благорост» |
test/market.test.ts |
Тесты документов маркетплейса |
test/search.test.ts |
Тесты поиска документов |
test/udata.test.ts |
Тесты пользовательских данных |
test/documents-1000-plus.test.ts |
Тесты документов с registry_id > 1000 |
Архитектура
src/
├── index.ts # Точка входа библиотеки
├── config.ts # Конфигурация подключений
├── Actions/ # Генераторы документов (по registry_id)
│ ├── 1.WalletAgreement.ts
│ ├── 100.ParticipantApplication.ts
│ ├── 300.AnnualGeneralMeetingAgenda.ts
│ ├── 600.FreeDecision.ts
│ ├── 1001.GenerationContract.ts
│ └── ... # Более 60 шаблонов
├── Factory/ # Ядро фабрики документов
├── Models/ # Модели данных MongoDB
│ ├── Cooperative.ts
│ ├── Individual.ts
│ ├── Organization.ts
│ ├── Document.ts
│ └── ...
└── templates/ # HTML-шаблоны (Nunjucks/Handlebars)
test/ # Тесты (Vitest)
Процесс генерации документа
- Загрузка шаблона и JSON Schema из блокчейна
- Получение приватных данных из хранилища (MongoDB)
- Валидация данных по JSON Schema (Ajv)
- Конструирование контекста для шаблона
- Рендеринг HTML из Nunjucks/Handlebars-шаблона
- Подпись хеша документа EOSIO-ключом
- Генерация PDF через WeasyPrint
Приватные данные документа: doc_data
Зарезервированный механизм для документов, содержащих персональные данные (ФИО экспедитора, паспорт, госномер, телефон и т.п.), которые не должны попадать в блокчейн.
Поток
- Контроллер собирает приватный payload и вызывает
generator.saveDocData(payload, registry_id)— возвращается{ hash }(sha256, идемпотентный). hashпередаётся on-chain в Action под зарезервированным именемdoc_data_hash.- При генерации/регенерации фабрика подгружает payload из
doc_private_dataчерезgenerator.getDocData(hash)и подставляет вcombinedData.doc_data. - Шаблон документа использует приватные поля как
{{ doc_data.<field> }}.
Пример
Контроллер:
const { hash } = await generator.saveDocData({
expeditor_full_name: 'Иванов Иван Иванович',
passport: '4509 123456',
phone: '+79991234567',
vehicle_registration_number: 'А123ВВ77',
}, 1103)
// в Action документа 1103 (MarketplaceTransportNote):
await generator.generate({
registry_id: 1103,
coopname: 'voskhod',
username: operatorAccount,
shipment_id,
doc_data_hash: hash,
})
Фабрика (src/Actions/1103.MarketplaceTransportNote.ts):
const doc_data = await this.loadDocData<TransportNoteDocData>(data)
const combinedData = { ...base, doc_data }
Шаблон (Templates/1103.MarketplaceTransportNote/index.ts):
<tr><th>Экспедитор</th><td>{{ doc_data.expeditor_full_name }}</td></tr>
<tr><th>Госномер</th><td>{{ doc_data.vehicle_registration_number }}</td></tr>
Зарезервированные имена шаблона
| Имя | Источник | Применение |
|---|---|---|
meta |
factory | заголовки, дата, lang, hash |
coop |
factory | реквизиты кооператива |
vars |
factory | переменные кооператива |
user |
factory | подписант (по сертификату) |
request / program / decision |
factory | контекст ЦПП |
branch |
factory | данные КУ |
doc_data |
DocDataService | приватные данные документа |
Graceful degradation
Если запись в doc_private_data удалена (например, по запросу пайщика), документ остаётся верифицируемым по хэшу подписи (хэш зафиксирован on-chain), но не регенерируется. Это явное архитектурное решение: ценность блокчейн-подписи сохраняется; восстановление PDF — обратимо deprecated.
Когда применять
- Новые документы с персональными данными — сразу через
doc_data. - Существующие документы (700-1099, 300-304 и т.д.) — остаются без изменений. Паттерн прогрессивный, переводить старые документы при отсутствии необходимости не нужно.
Ключевые зависимости
- nunjucks / handlebars — шаблонизаторы
- ajv — валидация JSON Schema
- mongodb — хранилище данных
- eosjs-ecc — криптографические операции EOSIO
- pdf-lib — работа с PDF
- unbuild — сборка библиотеки