Compare commits

...

1316 Commits

Author SHA1 Message Date
Alex Ant 947547b4d8 chore(release): publish 2026-04-06 22:11:11 +05:00
Alex Ant 29133c0e37 chore(release): publish 2026-04-06 18:52:12 +05:00
Alex Ant 8850b7a9f6 normalize user emails 2026-04-06 18:50:37 +05:00
Alex Ant a9b62fafa7 chore(release): publish 2026-04-06 15:54:47 +05:00
Alex Ant 05de97d20c chore(release): publish 2026-04-06 15:50:03 +05:00
Alex Ant 8106b089c0 chat coop event calendar notifier & bugfix for novu email sender 2026-04-06 15:48:01 +05:00
Alex Ant 986146d5a1 chore(release): publish 2026-04-05 16:51:11 +05:00
Alex Ant 939b8a9dee chore(release): publish 2026-04-05 16:49:26 +05:00
Alex Ant 8a12e1b50a blago-cli MVP release 2026-04-05 16:46:58 +05:00
Alex Ant 17ddefd9a2 chore(release): publish 2026-04-04 14:50:58 +05:00
Alex Ant 27470da264 chore(release): publish 2026-04-04 14:06:18 +05:00
Alex Ant f7404f37c0 down log level on subscription check 2026-04-04 14:03:19 +05:00
Alex Ant 41e6a6986b novu: quotes in subject title deletion 2026-04-04 14:00:49 +05:00
Alex Ant 63e062d8f8 meet css refactoring 2026-04-04 13:53:33 +05:00
Alex Ant 6bc8d793f8 notifications for meet with details 2026-04-04 01:38:54 +05:00
Alex Ant 94ab81fa64 controller meets v2 2026-04-04 00:13:22 +05:00
Alex Ant 333553d283 chore(release): publish 2026-04-02 00:38:32 +05:00
Alex Ant 4b4e88102e chore(release): publish 2026-04-02 00:19:41 +05:00
Alex Ant aa18df8724 milkdown editor 2026-04-02 00:18:14 +05:00
Alex Ant 250193a881 calendar timezone fix 2026-04-01 14:08:41 +05:00
Alex Ant c890376c5d scrub sensitive data from sentry log 2026-03-31 13:26:11 +05:00
Alex Ant 4050da5b13 chore(release): publish 2026-03-31 13:12:50 +05:00
Alex Ant b5b4769116 chore(release): publish 2026-03-31 13:11:24 +05:00
Alex Ant 943714cb47 sync delay on capital extension & correct sync removed entities from github 2026-03-31 13:10:02 +05:00
Alex Ant ac5e679eea chore(release): publish 2026-03-31 09:42:46 +05:00
Alex Ant a3e2cd892f prohibit delete project with components 2026-03-31 09:41:12 +05:00
Alex Ant 68066a5321 sync deletion projects, components, issues from db to github 2026-03-30 13:04:04 +05:00
Alex Ant ce93a90e89 fix project deletion 2026-03-30 12:26:59 +05:00
Alex Ant ac8a0c4466 xss skip for some routes 2026-03-30 12:02:39 +05:00
Alex Ant 86a4bdc999 chore(release): publish 2026-03-29 18:19:31 +05:00
Alex Ant 4636ced3a4 chore(release): publish 2026-03-29 17:52:31 +05:00
Alex Ant 646b1c90a2 calendar of events 2026-03-29 17:50:44 +05:00
Alex Ant 48ecb49a59 calendar of events 2026-03-28 21:09:57 +05:00
Alex Ant 396e17f698 chore(release): publish 2026-03-27 18:07:43 +05:00
Alex Ant c4230233bb chatcoop safe room storage 2026-03-27 17:56:15 +05:00
Alex Ant d14c0890fa unencrypt participant room & save coop rooms to a repository 2026-03-27 14:25:00 +05:00
Alex Ant d9f5017ade chatcoop -> matrix project requirements sync 2026-03-27 13:35:38 +05:00
Alex Ant 553a2e68cc chore(release): publish 2026-03-26 17:36:27 +05:00
Alex Ant 0de793bb80 chore(release): publish 2026-03-26 17:35:14 +05:00
Alex Ant 7f8c6d6dc1 ignore messages and transcriptions on sync from git 2026-03-26 17:34:23 +05:00
Alex Ant 880b52ed52 chore(release): publish 2026-03-26 16:19:46 +05:00
Alex Ant b33c3530b0 down log level for message sync 2026-03-26 16:18:33 +05:00
Alex Ant 705ecb8f18 chore(release): publish 2026-03-26 14:37:52 +05:00
Alex Ant cd812f2a3a not sync the messages and meetings from github 2026-03-26 14:33:19 +05:00
Alex Ant 842d104431 chore(release): publish 2026-03-26 14:03:30 +05:00
Alex Ant c031d3e34c add secretary to the rooms after they created 2026-03-26 14:02:10 +05:00
Alex Ant 30a7fe25d9 chore(release): publish 2026-03-26 12:29:33 +05:00
Alex Ant 7e6756f99e @coopenomics/inter package & messages & calls github sync 2026-03-26 12:28:31 +05:00
Alex Ant 9621d38ddd matrix: room message history 2026-03-26 10:20:58 +05:00
Alex Ant bffb3a95fc chore(release): publish 2026-03-25 23:49:05 +05:00
Alex Ant c9324271f6 transcriptions in project rooms 2026-03-25 23:46:20 +05:00
Alex Ant 2eb006fa70 secretary transcription service 2026-03-25 21:03:17 +05:00
Alex Ant b803016077 chore(release): publish 2026-03-24 21:12:03 +05:00
Alex Ant ecab9a4be1 chore(release): publish 2026-03-24 21:05:34 +05:00
Alex Ant 029b0fd08a update workflow 2026-03-24 21:04:05 +05:00
Alex Ant 2cd85b7440 chore(release): publish 2026-03-24 19:47:45 +05:00
Alex Ant c3b0ec99cd chore(release): publish 2026-03-24 19:46:01 +05:00
Alex Ant 59c207ef29 publish docs fix 2026-03-24 19:44:40 +05:00
Alex Ant 0cf8b64a4b chore(release): publish 2026-03-24 18:37:30 +05:00
Alex Ant 69ab3f9770 upgrade node 2026-03-24 18:36:22 +05:00
Alex Ant 3f08b6949e chore(release): publish 2026-03-24 18:22:44 +05:00
Alex Ant 17e3e8ad63 chore(release): publish 2026-03-24 18:18:52 +05:00
Alex Ant ceb6b63e2d fast buttons for capital extension 2026-03-24 18:15:25 +05:00
Alex Ant e377d40104 create blagorost project -> create matrix room -> invite participants 2026-03-24 17:51:26 +05:00
Alex Ant de8d33e715 create matrix room on create capital project 2026-03-24 15:09:31 +05:00
Alex Ant 6864518bd9 chore(release): publish 2026-03-23 23:53:45 +05:00
Alex Ant b75f0b92a1 disable NOVU blocker 2026-03-23 23:52:52 +05:00
Alex Ant 2e32ed7fd1 chore(release): publish 2026-03-23 22:55:31 +05:00
Alex Ant 30d2333cad remove moment-legacy & upgrade express 2026-03-23 22:54:34 +05:00
Alex Ant 1271ccce77 chore(release): publish 2026-03-23 20:35:44 +05:00
Alex Ant 82d4bb34b7 upgrade to vite 8 2026-03-23 20:34:35 +05:00
Alex Ant f8ca89ada6 what the fuck 2026-03-23 17:44:50 +05:00
Alex Ant f85fa7d786 chore(release): publish 2026-03-23 15:54:29 +05:00
Alex Ant 45c91ccc7f MERMAID & DRAWIO integration 2026-03-23 15:52:27 +05:00
Alex Ant c17efc489c chore(release): publish 2026-03-23 00:42:42 +05:00
Alex Ant f7fffe2859 docs & bpmn story type 2026-03-23 00:40:49 +05:00
Alex Ant 62ab4ac379 chore(release): publish 2026-03-20 09:36:15 +05:00
Alex Ant 1744fab580 refactor: replace common.hpp with index.hpp in multiple contract headers and remove unused files 2026-03-20 09:35:38 +05:00
Alex Ant 01de5de342 chore(release): publish 2026-03-19 18:54:14 +05:00
Alex Ant d69451c9e6 chore(release): publish 2026-03-19 18:51:23 +05:00
Alex Ant fceb0bbb23 update memo in contract 2026-03-19 18:50:42 +05:00
Alex Ant f5a3e41190 chore(release): publish 2026-03-11 17:23:59 +05:00
Alex Ant 3113346b47 chore(release): publish 2026-03-11 17:08:55 +05:00
Alex Ant 0dc9691557 freedecision notification fix & contact display fix 2026-03-11 17:07:54 +05:00
Alex Ant cef796272c publish-docs with callback 2026-03-07 20:19:45 +05:00
Alex Ant dc45089908 chore(release): publish 2026-03-04 12:43:40 +05:00
Alex Ant 395bfa3bb3 chore(release): publish 2026-03-04 12:34:50 +05:00
Alex Ant c37e9a2a15 add blagorost agreement fields to contributor input process 2026-03-04 12:33:08 +05:00
Alex Ant 90b0204205 Merge branch 'audio-stream' into dev 2026-03-03 21:35:30 +05:00
Alex Ant 904218aa4c refactor(chatcoop): enhance secretary functionality and logging
- Updated the LiveKitWebhookController route to /v1/extensions/chatcoop/livekit-webhook.
- Improved error logging in MatrixApiService for better clarity.
- Added secretaryPasswordEncrypted field to the ChatCoopPlugin configuration.
- Implemented delayed initialization for the secretary in the ChatCoopPlugin.
- Enhanced the SecretaryAgentService to manage secretary access tokens and send messages on behalf of the secretary.
- Refactored message sending logic to fallback to admin credentials if secretary credentials are unavailable.
2026-02-28 20:26:49 +05:00
Alex Ant f08d1b68ab upgrade typedoc in sdk 2026-02-28 19:28:47 +05:00
Alex Ant cfd7c72fd2 upgrade typedoc 2026-02-28 19:26:43 +05:00
Alex Ant 509471c97d chore(release): publish 2026-02-28 19:21:54 +05:00
Alex Ant f748d14d25 chore(release): publish 2026-02-28 18:39:30 +05:00
Alex Ant ae2bdb48cf fix removed extensions methods 2026-02-28 18:38:52 +05:00
Alex Ant dcc5a7a253 chore(release): publish 2026-02-28 17:39:57 +05:00
Alex Ant 25ed795ff6 refactor(soviet): remove vote copy mutations, queries, and selectors
- Deleted createVoteCopy, deactivateVoteCopy, and deleteVoteCopy mutations.
- Removed getMyVoteCopySettings and getWhoCopiesToMe queries.
- Eliminated voteCopySettingSelector and its related exports.
- Cleaned up index files for mutations and queries to reflect these removals.
2026-02-28 17:38:53 +05:00
Alex Ant 431db5f7d4 chore(release): publish 2026-02-28 17:26:31 +05:00
Alex Ant 24253f189c Merge branch 'dev' into testnet 2026-02-28 17:25:51 +05:00
Alex Ant a59192d95d bugfixes 2026-02-28 16:16:47 +05:00
Alex Ant d734ed3677 cherry-pick and replace. does not works yet after update 2026-02-28 15:31:34 +05:00
Cursor Agent 1379ebec06 rollback to 2026-02-28 12:57:15 +05:00
Cursor Agent c5eded9f7b feat(reports): add Desktop UI extension and register reports in backend
- Create reports extension for desktop with ReportsPage
- ReportsPage shows report schedule, generation dialog, XML result viewer
- Download generated XML files directly from browser
- Register reports in extensions-registry.ts on desktop side
- Register ReportsExtensionModule in backend AppRegistry
- Extension available for chairman role
2026-02-28 11:56:27 +05:00
Cursor Agent 68c2efdb5c feat(reports): rewrite FNS report generators with XSD-compliant XML
- Rewrite BuhotchGenerator with proper Баланс and ЦелИсп sections per XSD
- Rewrite Ndfl6Generator with ОКТМО, РасчСумНал zero structure
- Create individual generators: RsvGenerator, PsvGenerator, DusnGenerator, Fss4Generator, UvVznosyGenerator, UusnGenerator
- Add xml-utils.ts with shared XML building helpers
- Update ReportInput interface with oktmo, address, signerSnils fields
- Add OrganizationDataInputDTO for frontend data input
- Integrate LedgerInteractor for real balance data in reports
- Add 48 unit tests covering all 8 generators
2026-02-28 11:56:18 +05:00
Cursor Agent 00a8461b5b docs: update TASKS.md — reports 11/15 subtasks complete 2026-02-25 23:07:54 +00:00
Cursor Agent 7154c7f0e0 feat(reports): complete FNS/FSS report generation system
Report types (8):
- Бухгалтерский баланс (yearly, with ledger data: accounts 51, 80, 86)
- 6-НДФЛ (quarterly, zero)
- РСВ (quarterly, zero)
- ПСВ (monthly, zero)
- ДУСН (yearly, zero)
- 4-ФСС/ЕФС-1 (quarterly, zero)
- Уведомление о взносах (monthly, zero)
- Уведомление УСН (quarterly, zero)

Architecture:
- ReportRegistryService: factory pattern for report generators
- IReportGenerator interface: generate() + generateFileName()
- BuhotchGenerator: full balance sheet with ledger integration
- Zero report generator factory for all other types
- GraphQL: getAvailableReports query + generateReport mutation
- Deadline checker: isGenerationAvailable()
- XML in windows-1251 with Cyrillic tags per FNS standards
2026-02-25 23:04:18 +00:00
Cursor Agent 0f07666aeb feat(reports): extension skeleton + XSD schemas for all FNS reports
XSD schemas:
- NO_BUHOTCH — Бухгалтерский баланс (годовой)
- NO_NDFL6.2 — 6-НДФЛ (квартальный)
- NO_PERSSVFL — ПСВ (ежемесячный)
- NO_RASCHSV — РСВ (квартальный)
- NO_USN — ДУСН (годовой)
- UT_UVISCHSUMNAL — Уведомление о взносах (ежемесячный)

Extension structure: domain, infrastructure, application layers
2026-02-25 22:50:40 +00:00
Cursor Agent 489dc482e8 docs: TASKS.md — plan for FNS reports system 2026-02-25 22:48:34 +00:00
Cursor Agent 55c4759780 fix: resolve all lint/tsc errors in desktop
- DocumentSearchDialog: v-html on div instead of component, fix onSearch type
- Process API: cast data to any for updateProcessTemplate mutation
- quasar.config: overlay: false for dev (pre-existing TS warnings)
2026-02-25 22:40:16 +00:00
Alex Ant 16b4fe0080 add reports-standarts 2026-02-26 03:13:19 +05:00
Cursor Agent 9cd995c5f2 fix(capital/processes): simplify issue ID generation for process tasks 2026-02-25 21:59:10 +00:00
Cursor Agent e60d6a5d67 fix(capital/processes): fix TS errors — IssueIdGenerationService method name, IBaseDatabaseData fields 2026-02-25 21:57:14 +00:00
Cursor Agent b274af05c3 docs: update TASKS.md — process system 8/9 subtasks done 2026-02-25 21:53:19 +00:00
Cursor Agent 40e725f2ed feat(capital/processes): frontend — ProcessesPage with Vue Flow
- ProcessesPage with sidebar (process list) + Vue Flow canvas
- Process entity: API client, types
- Sidebar: list of templates, create dialog, select/deselect
- Vue Flow: nodes (steps) with edges (connections)
- Step adding, edge creation via drag
- Save/activate/delete templates
- Start nodes highlighted green
- Registered in capital extension install.ts as 'Процессы'
- Role-based: edit for chairman/member, view for all
2026-02-25 21:52:28 +00:00
Cursor Agent 0f091eefc9 feat(capital/processes): full backend CRUD + execution engine
- ProcessService: create/update/delete templates, start/complete process instances
- ProcessResolver: GraphQL mutations & queries with role-based access
  - chairman/member: create/update/delete templates
  - chairman/member/user: start processes, complete steps
- Repositories: TypeORM with CAPITAL_DATABASE_CONNECTION
- DTOs: ProcessTemplate, ProcessInstance, ProcessStep, ProcessEdge
- Auto-creates issues when steps become active
- Tracks step completion and cascading activation
- Process completes when all terminal steps done
- Registered in capital-database.module and capital-extension.module
2026-02-25 21:47:17 +00:00
Cursor Agent 2b00c58e9b feat(capital/processes): data model foundation
TypeORM entities:
- ProcessTemplateTypeormEntity: template with steps (Vue Flow nodes) and edges
- ProcessInstanceTypeormEntity: running instance with step_states

Domain entities:
- ProcessTemplateDomainEntity with ProcessStepTemplate and ProcessEdge
- ProcessInstanceDomainEntity with ProcessStepState

Enums:
- ProcessTemplateStatus: draft/active/archived
- ProcessInstanceStatus: running/completed/cancelled
- ProcessStepStatus: pending/active/completed/cancelled

Vue Flow installed for desktop (process designer)
2026-02-25 21:31:52 +00:00
Cursor Agent 6d9681fb8a docs: update TASKS.md with process system plan 2026-02-25 21:27:14 +00:00
Cursor Agent acc4d27677 fix(search): index documents on blockchain newsubmitted event, not on generation
- Removed indexing from GeneratorInfrastructureService.generateDocument()
- Created SearchEventService listening to action::soviet::newsubmitted
- Documents are indexed only when signed and submitted to blockchain
- Fetches full document from MongoDB by hash, indexes into OpenSearch
2026-02-25 21:26:26 +00:00
Cursor Agent ffe581a92b feat(search): полностью рабочая поисковая система документов
Визуально протестировано:
- Поиск 'кошелёк' → 1 результат с подсветкой КОШЕЛЕК
- Поиск 'соглашение' → 4 результата с fuzzy matching (соглашения, соглашается, согласию)
- Кнопка Поиск в header actions на страницах документов
- OpenSearch 2.18.0 с аутентификацией (admin/password)
- Zeus types перегенерированы из schema.gql
- /etc/hosts: opensearch → 127.0.0.1
2026-02-25 21:15:42 +00:00
Cursor Agent 6e643515ce fix(search): OpenSearch 2.18.0 with auth, Zeus types regenerated
- OpenSearch 2.18.0 with OPENSEARCH_INITIAL_ADMIN_PASSWORD
- SearchRegistryService: auth + SSL support
- Zeus types regenerated from schema.gql (features, searchDocuments, SearchResult)
- SDK selector validation re-enabled
- Reverted System API hack — proper Zeus types now handle features
2026-02-25 21:03:07 +00:00
Cursor Agent 1cc7aca53f fix(search): TS catch types, OpenSearch 2.11.1 compatibility
- Fix 'e' is of type 'unknown' in catch blocks
- Downgrade OpenSearch to 2.11.1 (no admin password required)
- docker-compose.yaml updated
2026-02-25 20:19:52 +00:00
Cursor Agent e5111622a6 refactor(search): фабричный SearchRegistry + header actions pattern
Backend:
- SearchRegistryService: универсальный фабричный движок поиска
  - registerIndex(): регистрация индексов с маппингами
  - index(): индексация любых данных
  - search(): generic поиск по любому индексу
- DocumentSearchService: регистрирует индекс 'documents' через registry
- Разделение: registry (универсальный) vs document search (специфичный)

Desktop:
- SearchHeaderAction: компонент для header actions system
- Убран прямой SearchButton из MainHeader
- UserDocumentsPage: registerAction через useHeaderActions
- ListOfDocumentsPage (совет): registerAction через useHeaderActions
- Действие появляется ТОЛЬКО на страницах документов
- Скрыто если features.search=false
2026-02-25 20:10:44 +00:00
Cursor Agent 99b98cb5c9 docs: update TASKS.md with search system progress 2026-02-25 19:59:38 +00:00
Cursor Agent 20447cfb46 feat(search): SDK query + Desktop search UI components
SDK:
- searchDocuments query with input/output types
- features selector in SystemInfo (search flag)

Desktop:
- DocumentSearchDialog: full-text search modal with debounced input
- SearchButton: conditional button (shown only if features.search=true)
- Header integration: SearchButton next to NotificationCenter
- UserDocumentsPage: SearchButton on documents registry page
- Graceful: hidden when search feature disabled
2026-02-25 19:55:50 +00:00
Cursor Agent 723a1813c9 feat(search): OpenSearch integration in controller
- SystemFeatures: new features field in SystemInfo with search flag
- OpenSearchService: index management, document indexing, full-text search
- SearchResolver: GraphQL searchDocuments query with auth
- Auto-indexing: documents indexed on generation via GeneratorService
- Graceful degradation: works without OpenSearch (features.search=false)
- SearchModule + SearchInfrastructureModule registered in app.module
2026-02-25 19:44:59 +00:00
Cursor Agent 04d52a0631 feat(search): add OpenSearch to docker-compose and reboot
- OpenSearch 2.18.0 single-node, security disabled for dev
- Volume opensearch_data for persistence
- Reboot script clears opensearch on restart
- Controller .env-example: OPENSEARCH_NODE, OPENSEARCH_ENABLED
2026-02-25 19:26:13 +00:00
Cursor Agent 097d18cd56 docs: add TASKS.md with progress tracking 2026-02-25 19:22:58 +00:00
Cursor Agent 5043995d14 docs: добавлены AGENTS.md для 6 компонентов (factory, boot, parser, sdk, cooptypes, notifications)
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 19:02:40 +00:00
Cursor Agent 804cfc8b72 docs(desktop): добавлен AGENTS.md — справочный документ по архитектуре фронтенда
Подробный документ на русском языке, описывающий:
- Feature Sliced Design (FSD) архитектуру и правило зависимостей слоёв
- Систему расширений (extensions): архитектура, регистрация, загрузка
- Все 8 расширений с описанием ролей и страниц
- Desktop Store, Session Store, System Store
- Процессы инициализации и их последовательность
- Навигационные гварды и ролевой доступ
- SSR vs SPA: известная проблема с Pinia-сериализацией
- Widget-режим, DecisionFactory, RequireAgreements
- Паттерны кода и правила для AI-агентов

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:56:03 +00:00
Cursor Agent a0dc6f3333 docs(controller): добавлен AGENTS.md — архитектура, расширения, паттерны
Подробная документация для AI-агентов:
- Обзор чистой архитектуры (domain → infrastructure → application)
- Дерево директорий с описанием каждого уровня
- Описание всех 10 расширений (chairman, capital, chatcoop и др.)
- Блокчейн-адаптеры и смарт-контракты EOSIO
- Система аутентификации JWT + RolesGuard
- Платёжный шлюз (yookassa, sberpoll, qrpay)
- Генерация документов через @coopenomics/factory
- TypeORM-сущности (23+ таблиц)
- Конфигурация и переменные окружения

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:48:03 +00:00
Cursor Agent a53957271e feat(setup): профессиональный установщик с 3 режимами
- Разработка: генерация .env, сборка библиотек и контрактов
- Тестовая сеть: фронтенд подключается к testnet API
- Продакшен: генерация конфигурации для mainnet
- Красивый CLI с inquirer, ora spinners, цветной вывод
2026-02-25 18:39:18 +00:00
Cursor Agent abae483dd3 docs: обновлены README и описания всех компонентов на русском языке
- Корневой README обновлён с актуальной структурой и командами
- 13 компонентных README с единым стилем: описание, фичи, скрипты, архитектура, тесты
- Обновлены description в package.json всех компонентов
- Новые README для cleos и setup (ранее отсутствовали)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:31:36 +00:00
Cursor Agent 4418e09211 docs: создание профессиональных README.md для 6 компонентов монорепозитория
- cooptypes: типы и интерфейсы экосистемы с подробной архитектурой
- sdk: TypeScript SDK с быстрым стартом и описанием классов
- notifications: библиотека уведомлений с таблицей 21 workflow
- contracts: смарт-контракты EOSIO с описанием чистой архитектуры
- migrator: утилита миграций с жизненным циклом
- docs: документация MkDocs Material с инструкцией по синхронизации

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:30:06 +00:00
Cursor Agent ca79457c19 docs: add professional Russian-language README files for 7 components
- boot: CLI blockchain initialization utility
- cleos: EOSIO cleos wrapper with wallet container
- controller: NestJS GraphQL API backend
- desktop: Vue 3 + Quasar cooperative workspace
- factory: legal document generator library
- parser: blockchain indexer via SHiP
- setup: interactive setup wizard

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:25:53 +00:00
Cursor Agent d2cd947b9c fix(tests): add sleep in high precision test to prevent duplicate tx
Add sleep(200) between refreshSegment calls in loops
and sleep(500) between commitToResult calls

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 18:13:12 +00:00
Cursor Agent ea28285639 docs: add test running instructions to AGENTS.md
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:58:22 +00:00
Cursor Agent 6174ddea9a fix(tests): add sleep before investor refreshSegment to prevent duplicate transaction
The refreshSegment calls for investors duplicate the ones from the
'вносим результаты' loop earlier in the test suite. Without a delay,
the TAPOS block reference may be identical, causing EOSIO to reject
the transaction as a duplicate. This follows the existing pattern
used elsewhere in the test file (e.g. line 720).

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:57:22 +00:00
Cursor Agent ce56adc3c8 feat(tests): boot capital 58/60, integration script
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:44:23 +00:00
Cursor Agent 59334bb13a feat(tests): add integration test runner script
- scripts/run-integration-tests.sh with configurable host/ports
- SDK tests: login, extensions, branches, systemInfo
- Notifications: 7 smoke tests for exports and workflows
- Root test pipeline: test:unit -> test:component -> test:integration

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:27:18 +00:00
Cursor Agent 21cd1f5217 feat(tests): add SDK, notifications tests; fix test scripts
- SDK: 4 integration tests (login, extensions, branches, systemInfo)
- Notifications: 7 smoke tests (exports, workflows, config helpers)
- Fix vitest watch mode in sdk package.json
- Add vitest to notifications devDependencies
- Update root test pipeline: unit -> component -> integration

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:18:56 +00:00
Cursor Agent 515df53c96 fix(tests): fix 5 failing factory tests — 85/85 passing
1. test/index.test.ts: Add block_num: 1 to ParticipantApplication tests.
   Root cause: getCurrentBlock() returns 0 with SKIP_BLOCK_FETCH=TRUE,
   and the source code check 'if (data.block_num)' treats 0 as falsy,
   skipping the signature DB lookup during document regeneration.

2. test/blagorost.test.ts: Add udata records in beforeAll.
   Root cause: udata.test.ts runs before blagorost.test.ts and its
   beforeEach clears the entire udatas collection, removing records
   inserted by the global preLoading() setup.

3. test/udata.test.ts: Insert versioned records directly into MongoDB
   with incrementing block_num values (10, 20, 30, 40).
   Root cause: getCurrentBlock() returns 0 for all saves, making all
   versions have identical block_num. Since getOne sorts by block_num
   desc, ties are resolved non-deterministically by MongoDB.

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 17:02:19 +00:00
Cursor Agent cbf7f48c44 feat(tests): factory 80/85 passing — add authorize, signatures, udata setup
- Add authorize action mocks with document data for decisions
- Add signatures collection setup for participant applications
- Add Udata records for capital/blagorost/generator agreements
- Add decision with accepted status for meet 301 tests
- Enable fileParallelism: false to prevent test data races
- Remaining 5: 3 signature lookup issues, 1 blagorost udata, 1 udata versioning

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 16:29:54 +00:00
Cursor Agent ed37aeb615 fix(tests): fix DB-backed action mocks, factory 63/85 passing
- Fix action query format (account/name vs action.account)
- Remaining 22 tests need Udata service or full signatures setup
- These are integration-level tests requiring controller running

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 15:40:26 +00:00
Cursor Agent ea89558696 feat(tests): add DB-backed mocks for factory tests — 59/85 passing
- Add draft table/translation mocks that read from MongoDB
- Add cooperative table mock for registrator/soviet data
- Add fallback DB actions mock
- Fix translation lookup: map registry_id -> internal id for draft_id
- Support SOURCE=local for local template resolution
- Remaining 26 tests need capital/blagorost flow setup (Udata, votes)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 15:35:30 +00:00
Cursor Agent c0bad75a37 docs: update TEST_PLAN.md with detailed status and strategy
Factory tests need mocks for draft templates and cooperative data.
Boot tests need full reboot before running.
Detailed plan for all components.

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 15:01:49 +00:00
Cursor Agent bdc227f110 feat(tests): add unified pnpm run test pipeline
- Root: pnpm run test (test:unit + test:integration)
- test:unit: cooptypes (4 smoke tests) + parser (3 config tests)
- test:integration: boot capital.test (60 blockchain tests)
- boot: add vitest config and test scripts
- cooptypes/parser: fix vitest watch mode -> vitest run
- controller: test script placeholder (tests via boot)
- Update TEST_PLAN.md

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:47:21 +00:00
Cursor Agent a9c3a1b6de fix(tests): cleanup cooptypes, parser, controller tests
- cooptypes: replace empty test with smoke test of exports (4 tests)
- parser: replace commented-out test with config smoke test (3 tests)
- controller: remove all outdated pre-NestJS integration and unit tests
- factory: make mongoUri configurable via env vars
- Add TEST_PLAN.md for tracking test implementation

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:38:59 +00:00
Cursor Agent f0e7b0b63a docs: finalize AGENTS.md with complete Cloud setup instructions
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:21:29 +00:00
Cursor Agent a709e2f1e3 fix(security): update vulnerable deps in desktop, boot, factory
- desktop: axios, dompurify, express
- boot: axios, mongoose
- factory: axios, mongodb

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:08:47 +00:00
Cursor Agent 3a34bd74db fix(security): downgrade express to 4.21.2 (v5 has breaking changes with path-to-regexp)
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:07:02 +00:00
Cursor Agent b755cb715a fix(security): update vulnerable dependencies in controller
CRITICAL fixes:
- mongoose 8.5.2 -> 9.2.2
- jspdf 2.5.1 -> 4.2.0
- jsonwebtoken 8.5.1 -> 9.0.3

HIGH fixes:
- express 4.17.1 -> 5.2.1
- axios 1.6.2 -> 1.13.5
- nodemailer 6.3.1 -> 8.0.1
- validator 13.0.0 -> 13.15.26
- ws 8.18.0 -> 8.19.0
- helmet 4.1.0 -> 8.1.0
- compression 1.7.4 -> 1.8.1
- moment 2.24.0 -> 2.30.1
- morgan 1.9.1 -> 1.10.1
- nodemon 2.0.0 -> 3.1.14
- supertest 6.0.1 -> 7.2.2
- cross-spawn 7.0.6 (added)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:02:57 +00:00
Alex Ant 42753fae0a update 2026-02-25 18:25:42 +05:00
Alex Ant d8b8fa2d6e update 2026-02-25 18:14:18 +05:00
Cursor Agent a75277e0b8 docs: update AGENTS.md with docker networking and reboot instructions
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 13:06:05 +00:00
Alex Ant f95b83674b update 2026-02-25 17:38:55 +05:00
Cursor Agent bb13e780af fix: correct SHiP port mapping 8070:8070 (was 8070:8080)
state-history-endpoint in config.ini listens on container port 8070,
not 8080. Fixed mapping so parser can connect via ws://127.0.0.1:8070

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:33:26 +00:00
Cursor Agent 6e80d5187c docs: document SSR hydration issue in desktop dev mode
Root cause: Pinia serializes Vue component objects from extension
routes into __INITIAL_STATE__. After hydration, components are
plain objects without render/setup functions. Production SSR build
works fine. Dev workaround: use SPA mode (quasar dev without --mode ssr).

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:23:15 +00:00
Cursor Agent db9b704d1d feat: add dev services to docker-compose, fix SHiP port, CHAIN_ID, postgres version
- docker-compose.yaml: add coopback, cooparser, desktop services for dev mode
- docker-compose.yaml: add SHiP port 8070, pin postgres:16
- components/desktop/.env-example: fix CHAIN_ID to match local blockchain
- components/parser/.env-example: fix SHIP port to 8070
- AGENTS.md: comprehensive dev environment instructions
- Remove docker-compose.override.yaml (changes merged into main file)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:23:15 +00:00
Cursor Agent 65a2b5361c chore: add AGENTS.md and docker-compose.override for dev environment setup
- AGENTS.md: development instructions for Cursor Cloud agents
- docker-compose.override.yaml: pin postgres:16 (postgres:latest v18+ breaks with current volume config)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:22:23 +00:00
Alex Ant 7bca73cd7b chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 59383014be chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 60a5296f5f notification center repair 2026-02-25 10:22:23 +00:00
Alex Ant eb4fe333a7 chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 8d06d6d817 chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant b6afb536b6 expired decision & walletPage & 30 days for expiration & formatDateFromNow with local time 2026-02-25 10:22:23 +00:00
Alex Ant 790047fa8a chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant baf55b6b6d chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 92d85244f1 update docker and packages 2026-02-25 15:20:11 +05:00
Alex Ant e0e8fef265 audio-stream 2026-02-23 20:21:46 +05:00
Alex Ant deb0f8344b chore(release): publish 2026-02-22 19:48:58 +05:00
Alex Ant ed135950eb openReplay ingest make throw proxy 2026-02-22 19:48:09 +05:00
Alex Ant ed7c547a06 chore(release): publish 2026-02-22 19:35:00 +05:00
Alex Ant 1a9f927c4c service-worker update fix & voting console.error log 2026-02-22 19:33:41 +05:00
Alex Ant 350af86b65 chore(release): publish 2026-02-22 14:17:08 +05:00
Alex Ant e136b73192 chore(release): publish 2026-02-22 13:08:00 +05:00
Alex Ant b6bf2fec21 notification center repair 2026-02-22 13:07:02 +05:00
Alex Ant 56daadbbca chore(release): publish 2026-02-21 21:06:27 +05:00
Alex Ant 7de26ec9a7 chore(release): publish 2026-02-21 20:19:13 +05:00
Alex Ant 7dbd3036df expired decision & walletPage & 30 days for expiration & formatDateFromNow with local time 2026-02-21 20:17:34 +05:00
Alex Ant 687085f9c7 chore(release): publish 2026-02-21 12:43:35 +05:00
Alex Ant 96cfc7e184 chore(release): publish 2026-02-21 12:42:06 +05:00
Alex Ant 0b26433b1a update 2026-02-21 12:40:54 +05:00
Alex Ant 4f06bb40f8 chore(release): publish 2026-02-21 12:34:00 +05:00
Alex Ant 46d571b65a update 2026-02-21 12:32:34 +05:00
Alex Ant 7da1bfd315 chore(release): publish 2026-02-21 12:15:08 +05:00
Alex Ant b30f47b5aa chore(release): publish 2026-02-21 12:13:59 +05:00
Alex Ant ccc6c7206c publish packages fix 2026-02-21 12:11:38 +05:00
Alex Ant a549f4cae0 chore(release): publish 2026-02-21 11:38:11 +05:00
Alex Ant 07c451ce22 fix npm publish-packages 2026-02-21 11:36:49 +05:00
Alex Ant 62da624369 chore(release): publish 2026-02-21 11:31:18 +05:00
Alex Ant a1a9b03180 chore(release): publish 2026-02-20 16:55:19 +05:00
Alex Ant e4c1d37c35 VideoPlayer and wallet fix 2026-02-20 16:54:06 +05:00
Alex Ant 42cacac256 chore(release): publish 2026-02-20 15:28:41 +05:00
Alex Ant 2816970327 update 2026-02-20 15:27:33 +05:00
Alex Ant 5744fcdcb2 chore(release): publish 2026-02-20 15:21:56 +05:00
Alex Ant 3c412b394d capital -> blagorost program type migration 2026-02-20 15:20:53 +05:00
Alex Ant 4e64cea373 update 2026-02-20 13:17:30 +05:00
Alex Ant db579db87f update 2026-02-20 13:01:59 +05:00
Alex Ant f612a918d9 update 2026-02-20 13:00:39 +05:00
Alex Ant 1a78e06a7a chore(release): publish 2026-02-20 12:49:32 +05:00
Alex Ant 374a98ffc3 update 2026-02-20 12:29:49 +05:00
Alex Ant d723e2e273 chore(release): publish 2026-02-20 11:39:36 +05:00
Alex Ant 41030171b5 update 2026-02-20 11:38:29 +05:00
Alex Ant aa31706bcc chore(release): publish 2026-02-20 11:26:19 +05:00
Alex Ant 4e3810493d update 2026-02-20 11:25:24 +05:00
Alex Ant eeb2db92ed chore(release): publish 2026-02-20 11:09:44 +05:00
Alex Ant 5a2930029b update 2026-02-20 11:09:15 +05:00
Alex Ant 2e9a73923b chore(release): publish 2026-02-20 10:19:35 +05:00
Alex Ant 97e67f8a88 fix workflow for package deployment 2026-02-20 10:18:47 +05:00
Alex Ant 074849af74 chore(release): publish 2026-02-20 09:30:23 +05:00
Alex Ant 6939783518 update 2026-02-20 09:30:01 +05:00
Alex Ant 260e48fa48 chore(release): publish 2026-02-20 09:26:36 +05:00
Alex Ant 52c4ef5df5 fix builder 2026-02-20 09:26:10 +05:00
Alex Ant 46b634e0fa chore(release): publish 2026-02-20 00:12:44 +05:00
Alex Ant a556c29a17 fix docker base image 2026-02-20 00:12:02 +05:00
Alex Ant bbc4f508b5 chore(release): publish 2026-02-19 23:10:26 +05:00
Alex Ant fe0243549a update .env-example 2026-02-19 23:07:12 +05:00
Alex Ant d9196ba650 add stub usernames 2026-02-19 22:53:22 +05:00
Alex Ant a0ea1fa392 pre-release 2026-02-19 22:01:50 +05:00
Alex Ant 195bcb4495 WTF 2026-02-19 16:41:15 +05:00
Alex Ant 929627a191 candidates page and contract calculation fix 2026-02-17 09:37:01 +05:00
Alex Ant 83a313b4b7 tests and fixes 2026-02-15 11:18:32 +05:00
Alex Ant f734bfb50f documents fixs & transcription page 2026-02-12 23:27:21 +05:00
Alex Ant 9efc22d195 chatcoop secretary transcriptor 2026-02-12 16:58:24 +05:00
Alex Ant bd3bef5fb0 chatcoop default room settings change 2026-02-12 11:29:32 +05:00
Alex Ant ddbe49e847 result submission 2026-02-12 10:43:48 +05:00
Alex Ant ed9bddec0b investors are invest to blagorost program directly, not to generation contract 2026-02-10 11:38:32 +05:00
Alex Ant 73a07eeb54 intellectual investments 2026-02-10 10:40:04 +05:00
Alex Ant 6e27aed804 registration process fixs 2026-02-09 23:44:34 +05:00
Alex Ant 5f955781a6 download document package button 2026-02-07 12:41:06 +05:00
Alex Ant c124f7310e some fixes 2026-02-07 12:09:43 +05:00
Alex Ant 73ffcae549 github sync subsystem on blagorost module 2026-02-06 21:15:13 +05:00
Alex Ant 80e55ba0be make requirements as documents and editor readonly mode 2026-02-06 13:46:35 +05:00
Alex Ant eecf955096 import contributors with some peculiarities 2026-02-06 11:16:29 +05:00
Alex Ant 6426db9a5d wallet sync and contributors import 2026-02-05 22:15:42 +05:00
Alex Ant bf4753cd09 gamification fix 2026-02-05 12:50:50 +05:00
Alex Ant a592eaf383 replace editor 2026-02-05 11:18:17 +05:00
Alex Ant 75d654cec3 feat: STORY-001 Исправить логику создания участников
Изменена логика создания Contributor с "при первом вкладе" на "при подтверждении доступа к проекту".
Теперь Contributor создается автоматически при одобрении appendix (confirmClearance).

Acceptance Criteria:
- Участник отображается в виджете сразу после подписания соглашения об участии
- Не требуется делать вклад для отображения в списке участников
- Логика создания Contributor изменена с "при первом вкладе" на "при подтверждении доступа"

Technical Changes:
- Добавлена логика создания Contributor в ClearanceManagementInteractor.handleConfirmClearance()
- Contributor создается со статусом APPROVED при подтверждении доступа
- Добавлены необходимые импорты и зависимости
- Обновлен статус спринта

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-04 11:42:55 +05:00
Alex Ant a18ed37600 blagorost test restore 2026-02-04 11:07:53 +05:00
Alex Ant 6747a8dfbc refactoring possible complete 2026-02-01 13:29:29 +05:00
Alex Ant 583dfb8126 before capital contract refactoring 2026-01-31 11:36:00 +05:00
Alex Ant bda31dd086 access violation bugfix with wharfkit rollback 2026-01-29 23:19:22 +05:00
Alex Ant 283af35f3b looking for access violation bug 2026-01-29 15:51:35 +05:00
Alex Ant 82564e2580 contract access violation bug fix | documents 2026-01-29 01:56:40 +05:00
Alex Ant fc6a9026e6 blagorost documents preparing 2026-01-27 23:00:27 +05:00
Alex Ant e1538bb5ab contract optional blagorost agreement on registration 2026-01-26 10:54:53 +05:00
Alex Ant d47db969ea refactoring vars on blagorost documents 2026-01-25 22:51:35 +05:00
Alex Ant f01de37a59 before make new document models 2026-01-25 13:40:56 +05:00
Alex Ant cee429a0a0 add udata model to a factory 2026-01-25 11:48:49 +05:00
Alex Ant 321ee89676 added all blagorost documents 2026-01-25 10:30:57 +05:00
Alex Ant aa60484315 make invest statement 2026-01-23 13:00:24 +05:00
Alex Ant b807df3ee8 document names refactoring & context component 2026-01-22 18:13:45 +05:00
Alex Ant c11e922573 registration with programs and agreements 2026-01-22 13:41:02 +05:00
Alex Ant 7135a3bbeb new documents 2026-01-21 21:49:35 +05:00
Alex Ant 142b87dd67 before make git 2026-01-20 21:23:02 +05:00
Alex Ant dff4579f28 project & component initialization & InitProjectStatement & InitProjectDecision 2026-01-19 17:39:20 +05:00
Alex Ant 2b3b662a9a generationAgreement Document & project initialization process 2026-01-19 14:24:05 +05:00
Alex Ant 98bc5195b1 blagorost decision tracking factory 2026-01-18 17:31:10 +05:00
Alex Ant a02912ccbb document updates 2026-01-17 11:47:48 +05:00
Alex Ant 2f1d29dfd2 Merge branch 'project-authorization-on-create' into dev 2026-01-16 22:02:48 +05:00
Alex Ant 11efacc4cb 1002 document generation agreement 2026-01-16 22:00:43 +05:00
Alex Ant 8133d0da3d register contributor with UHD 2026-01-16 12:34:47 +05:00
Alex Ant 093d63faae add zeus 2026-01-16 12:34:18 +05:00
Alex Ant 07f5e04488 authorization 2026-01-16 12:32:34 +05:00
Alex Ant acfc97fc7c permissions and boot fixs 2026-01-15 12:15:14 +05:00
Alex Ant 6e30554143 fix button 2026-01-13 18:25:54 +05:00
Alex Ant fa1c788c41 issue and project permissions & selectors 2026-01-13 18:01:59 +05:00
Alex Ant 3a688942c3 finalize_project and convert unused project funds to global program fund 2026-01-09 18:17:34 +05:00
Alex Ant 6797522618 blagorost masterplace 2026-01-07 17:11:47 +05:00
Alex Ant 4730a53313 requirements project -> component -> issue 2026-01-06 00:26:11 +05:00
Alex Ant 5a538792f0 project logs 2026-01-05 23:46:42 +05:00
Alex Ant 5f20289767 blagorost human project log 2026-01-04 23:43:54 +05:00
Alex Ant 4c7ed97a76 blagorost convert segment dialog 2026-01-04 21:43:23 +05:00
Alex Ant a9c2228e3c repair blagorost tests 2026-01-02 19:19:55 +05:00
Alex Ant ec44fcb7cb refactoring: user and certificates 2026-01-02 18:03:17 +05:00
Alex Ant 1f9c8d261b refactoring: wallet & user-certificate 2026-01-02 17:15:43 +05:00
Alex Ant 23ab3bd416 refactoring: system 2026-01-02 16:42:15 +05:00
Alex Ant 79482775d2 refactoring: settings 2026-01-02 10:40:27 +05:00
Alex Ant 2dcb3678b0 refactoring: payment-method 2026-01-02 10:28:48 +05:00
Alex Ant 8d7c206012 refactoring: notification 2026-01-02 10:03:37 +05:00
Alex Ant c21c264426 refactoring: meet interactor 2026-01-01 20:42:29 +05:00
Alex Ant 77a39c18f1 refactoring: make adapters for extensions 2026-01-01 20:11:25 +05:00
Alex Ant 65a00b0a8e refactoring: ledger 2026-01-01 19:11:56 +05:00
Alex Ant 0c2d5679f1 extensions reinit 2026-01-01 19:05:12 +05:00
Alex Ant 75d0e0de43 gateway refactoring 2026-01-01 18:49:59 +05:00
Alex Ant 19ff864eaa gateway refactoring 2026-01-01 14:30:37 +05:00
Alex Ant 6bbee7cb10 refactoring -> clean architecture 2026-01-01 12:57:37 +05:00
Alex Ant e268923cf6 down logger lever for powerup overdrawn balance 2025-12-31 13:14:30 +05:00
Alex Ant 4fb3d7182c chore(release): publish 2025-12-31 00:08:59 +05:00
Alex Ant 6e7a6ec032 chore(release): publish 2025-12-31 00:08:00 +05:00
Alex Ant 0e92c92f87 bug: повторное общее собрание не завершало онбординг кооператива 2025-12-30 21:37:37 +05:00
Alex Ant a6f1eb39ec chore(release): publish 2025-12-30 20:49:54 +05:00
Alex Ant 21364f6239 chore(release): publish 2025-12-30 18:24:48 +05:00
Alex Ant a0527094a9 fix a little 2025-12-30 18:24:17 +05:00
Alex Ant eda890c294 chore(release): publish 2025-12-30 17:59:11 +05:00
Alex Ant 1ed8db87cc openReplay http watcher 2025-12-30 17:57:57 +05:00
Alex Ant 136892478f chore(release): publish 2025-12-30 14:41:51 +05:00
Alex Ant aee90d31a0 chore(release): publish 2025-12-30 14:33:54 +05:00
Alex Ant 6fe632fda3 add repairdec action in soviet contract 2025-12-30 14:33:00 +05:00
Alex Ant fa975a2f5b auto cancel decisions full disable and postgres booter 2025-12-30 13:45:40 +05:00
Alex Ant bd5ab14d2f chore(release): publish 2025-12-28 20:44:51 +05:00
Alex Ant 32890bfda9 chore(release): publish 2025-12-28 15:10:11 +05:00
Alex Ant a3d4113b97 sentry backend integration 2025-12-28 15:09:13 +05:00
Alex Ant 8acfa722f8 chore(release): publish 2025-12-28 14:26:21 +05:00
Alex Ant 234beba06a sentry env 2025-12-28 14:24:55 +05:00
Alex Ant 90c27a0f73 chore(release): publish 2025-12-28 12:47:59 +05:00
Alex Ant 2577a5d2c2 remove logs 2025-12-28 12:47:25 +05:00
Alex Ant 84bf10b65b chore(release): publish 2025-12-28 12:15:59 +05:00
Alex Ant 5d70f5fd97 env refactoring rollback 2025-12-28 12:15:29 +05:00
Alex Ant f42cc59d53 chore(release): publish 2025-12-28 11:33:03 +05:00
Alex Ant d12b222d52 fix tracker assistant (disable it) 2025-12-28 11:32:22 +05:00
Alex Ant b663c7f9d4 env config refactoring 2025-12-28 11:24:26 +05:00
Alex Ant 30abfa7e23 chore(release): publish 2025-12-28 10:58:32 +05:00
Alex Ant 0be32c34c5 update 2025-12-28 10:58:09 +05:00
Alex Ant 8d4863b894 chore(release): publish 2025-12-28 10:43:19 +05:00
Alex Ant ad1ae8fb54 update openScreen assistant 2025-12-28 10:42:46 +05:00
Alex Ant b31e41bfd9 chore(release): publish 2025-12-27 20:49:02 +05:00
Alex Ant 999d5a937d SSR fix 2025-12-27 20:48:15 +05:00
Alex Ant 45ea543a4b chore(release): publish 2025-12-27 20:23:58 +05:00
Alex Ant 9d72a5e02d openScreen 2025-12-27 20:21:56 +05:00
Alex Ant 8973c45b4e chore(release): publish 2025-12-27 20:04:57 +05:00
Alex Ant b3e5a5dc62 complete refactoring for a clean architecture 2025-12-27 20:03:53 +05:00
Alex Ant d8bddadd42 chore(release): publish 2025-12-27 11:42:16 +05:00
Alex Ant aa552d1c80 refactoring #4 2025-12-27 11:41:23 +05:00
Alex Ant 6daac5f442 chore(release): publish 2025-12-26 23:53:27 +05:00
Alex Ant a8c453fbda refactoring #2 2025-12-26 23:52:43 +05:00
Alex Ant 3e15da5ad7 chore(release): publish 2025-12-26 19:46:46 +05:00
Alex Ant 8e776e7d42 chore(release): publish 2025-12-26 18:48:28 +05:00
Alex Ant de3d5654b4 mono monitor mongo -> psql migration & ipn model migration 2025-12-26 18:47:31 +05:00
Alex Ant b0e19ffa30 chore(release): publish 2025-12-26 17:22:25 +05:00
Alex Ant bacf451cf5 chore(release): publish 2025-12-26 16:58:44 +05:00
Alex Ant 2a5600b6af login bugfix 2025-12-26 16:58:13 +05:00
Alex Ant bfbc78d9b7 chore(release): publish 2025-12-26 13:41:36 +05:00
Alex Ant d509281bdc delete some migrations 2025-12-26 13:40:55 +05:00
Alex Ant 866fdfb4f6 chore(release): publish 2025-12-26 13:27:55 +05:00
Alex Ant 6c14d0f4c5 fix generator port 2025-12-26 13:27:23 +05:00
Alex Ant b31a8e3bf8 chore(release): publish 2025-12-26 12:46:11 +05:00
Alex Ant 7f3261e453 migrate tokens from mongo to postgres and clean arch 2025-12-26 12:45:09 +05:00
Alex Ant 861c3617dd payment providers refactoring 2025-12-26 10:51:57 +05:00
Alex Ant 1f4b1f5421 chore(release): publish 2025-12-26 10:04:34 +05:00
Alex Ant f851460a32 fix package 2025-12-26 10:03:09 +05:00
Alex Ant 992ac6d9eb chore(release): publish 2025-12-25 23:46:03 +05:00
Alex Ant 2e9ac57930 big service refactoring 2025-12-25 23:44:07 +05:00
Alex Ant ce8d090f27 chore(release): publish 2025-12-25 14:34:36 +05:00
Alex Ant 4ec8b59fd6 chore(release): publish 2025-12-25 14:32:42 +05:00
Alex Ant 6b086af8b4 upgrade chatwoot sdk 2025-12-25 14:30:57 +05:00
Alex Ant 52f95665ed chore(release): publish 2025-12-25 10:13:13 +05:00
Alex Ant d1ce5ab105 fix build chatwoot 2025-12-25 10:12:39 +05:00
Alex Ant 0fd10649f4 chore(release): publish 2025-12-25 00:24:37 +05:00
Alex Ant 3c65900768 support widget 2025-12-25 00:23:31 +05:00
Alex Ant 0b962b4fee board members refactoring 2025-12-24 17:06:18 +05:00
Alex Ant a58633fc5f chore(release): publish 2025-12-24 13:24:14 +05:00
Alex Ant 24c2ed3ae3 chore(release): publish 2025-12-23 23:22:46 +05:00
Alex Ant 84a77d98cf refactoring sendPOST and sendGET from desktop 2025-12-23 23:19:55 +05:00
Alex Ant 20e50e02e9 delete @coopenomics/controller package from desktop 2025-12-23 19:46:51 +05:00
Alex Ant c8bb354972 chore(release): publish 2025-12-22 18:47:11 +05:00
Alex Ant 3f9b73d211 chore(release): publish 2025-12-22 18:46:36 +05:00
Alex Ant 40bbffed17 bug fix 2025-12-22 18:46:11 +05:00
Alex Ant b16ec4e809 fix blagorost contract 2025-12-22 18:39:56 +05:00
Alex Ant 9629c895d5 chore(release): publish 2025-12-17 23:29:39 +05:00
Alex Ant f4420fcc03 doc update 2025-12-17 23:28:34 +05:00
Alex Ant 17f8c3e134 chore(release): publish 2025-12-17 22:20:38 +05:00
Alex Ant 02512a522c chore(release): publish 2025-12-17 21:47:10 +05:00
Alex Ant 4974f99842 blagorost off 2025-12-17 21:46:08 +05:00
Alex Ant a3d0c32be3 chore(release): publish 2025-12-17 21:33:01 +05:00
Alex Ant 31523160fc chore(release): publish 2025-12-17 21:25:53 +05:00
Alex Ant 183d5cdeec migration and little fix 2025-12-17 21:25:02 +05:00
Alex Ant 52fed810f2 chore(release): publish 2025-12-17 20:53:43 +05:00
Alex Ant f58d63da8d blagorost documents agenda proposals 2025-12-17 20:51:34 +05:00
Alex Ant 6793e6fbdc blagorost documents 2025-12-17 18:21:24 +05:00
Alex Ant 4f88e9b97d blagorost imgs, profile and other fixs 2025-12-17 09:35:32 +05:00
Alex Ant 5004cec9b3 chore(release): publish 2025-12-14 23:42:01 +05:00
Alex Ant 76c81e4209 chore(release): publish 2025-12-14 23:40:47 +05:00
Alex Ant 323043dc01 документация по общему собранию и т.п. 2025-12-14 23:39:41 +05:00
Alex Ant 37902ce70a chore(release): publish 2025-12-13 15:14:11 +05:00
Alex Ant c69e795d49 chore(release): publish 2025-12-13 15:12:28 +05:00
Alex Ant c3b82af95a 1c doc 2025-12-13 15:11:58 +05:00
Alex Ant 24510d4828 chore(release): publish 2025-12-13 13:45:35 +05:00
Alex Ant 11a04a99f7 1ccoop availability 2025-12-13 13:44:11 +05:00
Alex Ant 6e96eeaa05 chore(release): publish 2025-12-13 13:22:25 +05:00
Alex Ant 2c16d4af5a fix 2025-12-13 13:21:46 +05:00
Alex Ant d1f9cf43de chore(release): publish 2025-12-13 12:44:06 +05:00
Alex Ant 26fb11cb30 chore(release): publish 2025-12-13 12:21:45 +05:00
Alex Ant a7a3eb8658 weasyprint -> 67 2025-12-13 12:21:17 +05:00
Alex Ant fbedcd1f5f chore(release): publish 2025-12-13 11:28:37 +05:00
Alex Ant 6033af0f23 fix 2025-12-13 11:28:10 +05:00
Alex Ant 4809786b16 chore(release): publish 2025-12-13 11:19:11 +05:00
Alex Ant a37f40ff6e weasyprint #2 2025-12-13 11:18:25 +05:00
Alex Ant 44f5cc9de0 chore(release): publish 2025-12-13 10:51:12 +05:00
Alex Ant e059d0cbd9 Dockerfile 2025-12-13 10:50:25 +05:00
Alex Ant c51040b0de chore(release): publish 2025-12-13 10:42:39 +05:00
Alex Ant eba2abc108 downgrade weasyprint and upgrade base image 2025-12-13 10:41:46 +05:00
Alex Ant ed7626009a chore(release): publish 2025-12-13 10:11:33 +05:00
Alex Ant 442413ec6b up weasyprint version 2025-12-13 10:09:25 +05:00
Alex Ant 90986df641 onboarding 2025-12-13 09:31:42 +05:00
Alex Ant 8b505dbce3 cooperative onboarding process & chat with union 2025-12-11 20:47:16 +05:00
Alex Ant 2b26e9643b chore(release): publish 2025-12-10 15:31:43 +05:00
Alex Ant eb16f01839 docs and ledger refactoring 2025-12-10 13:50:59 +05:00
Alex Ant 872cf33d1c update 2025-12-02 18:39:54 +05:00
Alex Ant afbce19028 chore(release): publish 2025-12-02 18:13:18 +05:00
Alex Ant e27228e735 chore(release): publish 2025-12-02 18:09:56 +05:00
Alex Ant 3125232114 change CNAME in docs and fix publish CI 2025-12-02 18:09:07 +05:00
Alex Ant e552840cc4 chore(release): publish 2025-12-02 14:39:34 +05:00
Alex Ant 569142845a chore(release): publish 2025-12-02 14:37:38 +05:00
Alex Ant e62d837858 1c-integration and deploy bug fix 2025-12-02 14:35:58 +05:00
Alex Ant 27badfb88a chore(release): publish 2025-11-29 00:50:18 +05:00
Alex Ant e4d0781eb8 chore(release): publish 2025-11-29 00:44:02 +05:00
Alex Ant 93c8373335 disable setInterval on server side 2025-11-29 00:42:51 +05:00
Alex Ant 42bad6b4ea chore(release): publish 2025-11-28 13:08:14 +05:00
Alex Ant 83d4bd88cf chore(release): publish 2025-11-28 13:06:11 +05:00
Alex Ant 4141267104 disable unused routes 2025-11-28 13:05:04 +05:00
Alex Ant d5778f165b remove notifications on update 2025-11-28 12:16:54 +05:00
Alex Ant 7128e26a27 chore(release): publish 2025-11-28 12:15:17 +05:00
Alex Ant 78247e9969 bug fixes and logging improve 2025-11-28 12:14:13 +05:00
Alex Ant 66d36e034e chore(release): publish 2025-11-27 19:52:17 +05:00
Alex Ant 4100ee842c update 2025-11-27 19:49:59 +05:00
Alex Ant d4857c8e6e some fixes 2025-11-27 14:26:34 +05:00
Alex Ant 0f52781489 cmdK z-top & latest tag on docker production container 2025-11-26 17:27:08 +05:00
Alex Ant 332f19cd22 chore(release): publish 2025-11-26 13:13:59 +05:00
Alex Ant 53a1c103d6 chore(release): publish 2025-11-26 12:55:34 +05:00
Alex Ant addf7a3e6b fix 2025-11-26 12:54:19 +05:00
Alex Ant c76078ea06 chore(release): publish 2025-11-26 12:51:24 +05:00
Alex Ant 9299288e8a bug fix: chat loading and console.logs for debug router problem 2025-11-26 12:49:42 +05:00
Alex Ant dc34f24ee2 chore(release): publish 2025-11-26 11:23:40 +05:00
Alex Ant fe83bf1483 fix latest tag on testnet 2025-11-26 11:22:41 +05:00
Alex Ant c184f9281d chore(release): publish 2025-11-26 00:14:11 +05:00
Alex Ant 3127a69b67 chore(release): publish 2025-11-26 00:10:29 +05:00
Alex Ant d7326c4f82 chore(release): publish 2025-11-25 16:12:22 +05:00
Alex Ant f8478284b6 backward capability 2025-11-25 16:11:26 +05:00
Alex Ant 1c910de4b1 chore(release): publish 2025-11-25 10:56:15 +05:00
Alex Ant 2c3cb955ef fix 2025-11-25 10:55:36 +05:00
Alex Ant 1d7746d839 chore(release): publish 2025-11-25 10:10:36 +05:00
Alex Ant e16d2ba238 chore(release): publish 2025-11-24 22:26:05 +05:00
Alex Ant 60f8b448ec coopgram -> chatcoop & mobile client instruction 2025-11-24 22:24:33 +05:00
Alex Ant c0d61f29a8 empty settings fix & matrix room creation fix 2025-11-24 20:10:57 +05:00
Alex Ant c47b99f9ee chore(release): publish 2025-11-24 19:02:09 +05:00
Alex Ant 653429a6d6 docs generation fix & docker compose env missing params fix 2025-11-24 19:01:23 +05:00
Alex Ant c2250e1817 chore(release): publish 2025-11-24 17:35:56 +05:00
Alex Ant 92ca48d65f make common group room & fix deploy workflow 2025-11-24 17:33:17 +05:00
Alex Ant 2f53d9a79d chatcoop prerelease 2025-11-24 11:44:59 +05:00
Alex Ant d025bfe2da Merge branch 'element' into dev 2025-11-22 15:28:41 +05:00
Alex Ant fbc2460ac4 typeorm fix expiration and add complete & fail date 2025-11-22 15:23:49 +05:00
Alex Ant 76c2800275 extensions migration system, extensions logs, powerup log 2025-11-22 10:59:08 +05:00
Alex Ant 536bf984ee powerup page 2025-11-21 19:33:09 +05:00
Alex Ant 23f8af43ee chore(release): publish 2025-11-20 20:26:39 +05:00
Alex Ant eba54bf2e7 fix amount precision in document 2025-11-20 20:25:29 +05:00
Alex Ant 50270e6541 rub to axon convertation 2025-11-20 20:18:36 +05:00
Alex Ant 5f7d9e80ee chore(release): publish 2025-11-20 12:56:57 +05:00
Alex Ant a71ef35d5c delegate fees fund and convert_to_axon action 2025-11-20 12:55:59 +05:00
Alex Ant 2cf5291831 chore(release): publish 2025-11-19 19:40:44 +05:00
Alex Ant 75e3e9dd7b fix convertation system 2025-11-19 19:40:08 +05:00
Alex Ant 7a75365d2f chore(release): publish 2025-11-19 15:38:27 +05:00
Alex Ant 7a23d59aa1 injection AXON to any coop 2025-11-19 15:37:20 +05:00
Alex Ant 2583423600 chore(release): publish 2025-11-19 12:28:35 +05:00
Alex Ant 1d748a0424 generate and process AXON - GOVERN convertation 2025-11-19 12:26:05 +05:00
Alex Ant 86172790d1 chore(release): publish 2025-11-18 11:51:59 +05:00
Alex Ant 92c9103059 currentUser -> session stores refactoring 2025-11-18 11:50:07 +05:00
Alex Ant 16393dbc9d chore(release): publish 2025-11-17 22:10:07 +05:00
Alex Ant e841b0ef03 fix login bug 2025-11-17 22:09:06 +05:00
Alex Ant 27c7ae9d76 chore(release): publish 2025-11-17 14:04:59 +05:00
Alex Ant dc3a40675d fix health check 2025-11-17 14:04:07 +05:00
Alex Ant 31e65ac2e3 chore(release): publish 2025-11-17 13:32:08 +05:00
Alex Ant c2444d1b09 health-check route 2025-11-17 13:31:37 +05:00
Alex Ant 2faadae4cf chore(release): publish 2025-11-17 12:29:25 +05:00
Alex Ant d24e51c355 some fixes 2025-11-17 12:28:37 +05:00
Alex Ant 8ed8af1b9c chore(release): publish 2025-11-16 14:10:06 +05:00
Alex Ant 5f6b416f26 installer fix, parser init fix, service worker fix 2025-11-16 14:06:43 +05:00
Alex Ant 3671c05573 chore(release): publish 2025-11-15 18:53:03 +05:00
Alex Ant db6eed9292 pre-release 2025-11-15 18:51:37 +05:00
Alex Ant 4c903ba0bb chore(release): publish 2025-11-14 12:26:30 +05:00
Alex Ant 9eee216065 fix parser initializator 2025-11-14 12:25:58 +05:00
Alex Ant 9e1dd02d70 chore(release): publish 2025-11-14 11:58:32 +05:00
Alex Ant b986bf9bcc update 2025-11-14 11:56:51 +05:00
Alex Ant a3e0e62efc chore(release): publish 2025-11-14 11:25:42 +05:00
Alex Ant 8646c5acf6 boolean factory fix 2025-11-14 11:24:49 +05:00
Alex Ant dab9465755 chore(release): publish 2025-11-13 20:32:34 +05:00
Alex Ant 3a076cc4ea fix parser 2025-11-13 20:31:38 +05:00
Alex Ant 1b6d1ea0ef chore(release): publish 2025-11-13 20:19:25 +05:00
Alex Ant c490284c65 initializator for parser 2025-11-13 20:17:19 +05:00
Alex Ant c3ca329138 installation step 2025-11-13 18:33:43 +05:00
Alex Ant d0f73c32bb chore(release): publish 2025-11-13 11:18:18 +05:00
Alex Ant 79e0da7cf2 fix user notifications 2025-11-13 11:16:22 +05:00
Alex Ant b261f505aa chore(release): publish 2025-11-12 18:06:40 +05:00
Alex Ant 65c4c95cbe connection page and regcoop payer 2025-11-12 18:05:34 +05:00
Alex Ant 5b3c9c9055 chore(release): publish 2025-11-11 19:12:42 +05:00
Alex Ant d32d4f845c coop connect page 2025-11-11 19:11:25 +05:00
Alex Ant 637950d37f chore(release): publish 2025-11-10 21:01:05 +05:00
Alex Ant 0e1ede58e5 some installation process fixs and regcoop modify instead of emplace when exists 2025-11-10 20:59:36 +05:00
Alex Ant e914a11768 chore(release): publish 2025-11-10 12:47:47 +05:00
Alex Ant 73d66dab35 command+k menu 2025-11-10 12:46:45 +05:00
Alex Ant 634766a08e chore(release): publish 2025-11-10 09:47:19 +05:00
Alex Ant 5fbc450e96 ConnectionStepper 2025-11-10 09:45:11 +05:00
Alex Ant 5acd7ae2c4 chore(release): publish 2025-11-09 20:48:49 +05:00
Alex Ant 09578ddc5d provider integration 2025-11-09 20:47:16 +05:00
Alex Ant 8270801711 chore(release): publish 2025-11-09 16:26:20 +05:00
Alex Ant de91c3cfc8 sdk public constructor 2025-11-09 16:22:43 +05:00
Alex Ant 06888a2061 chore(release): publish 2025-11-08 20:58:10 +05:00
Alex Ant cde2d33d03 chore(release): publish 2025-11-08 20:56:36 +05:00
Alex Ant 9cf89f95d1 fix 2025-11-08 20:55:28 +05:00
Alex Ant 20486e15ea chore(release): publish 2025-11-08 14:18:32 +05:00
Alex Ant 420c403da1 chore(release): publish 2025-11-08 14:14:44 +05:00
Alex Ant cb8fdf3461 startup notification 2025-11-08 14:14:04 +05:00
Alex Ant 0937d08770 chore(release): publish 2025-11-08 13:59:05 +05:00
Alex Ant 9b1fd8e1aa chore(release): publish 2025-11-08 13:58:14 +05:00
Alex Ant d87861142c chore(release): publish 2025-11-08 12:47:15 +05:00
Alex Ant ff5fc1d0f3 update publish scripts 2025-11-08 12:46:45 +05:00
Alex Ant d5a671cbb8 update config 2025-11-08 12:42:51 +05:00
Alex Ant d0508cbd96 chore(release): publish 2025-11-08 11:58:39 +05:00
Alex Ant 12098cf8ac server provisioned notification workflow 2025-11-08 11:48:35 +05:00
Alex Ant 2557c35bea add dev mode to smart-contracts, make notifications external accessable, make boot works 2025-11-07 20:20:17 +05:00
Alex Ant 11f23bbd77 chore(release): publish 2025-11-07 12:25:13 +05:00
Alex Ant 2b2ec729ac external notification resolver 2025-11-07 12:24:18 +05:00
Alex Ant 9292acec8c chore(release): publish 2025-11-06 21:49:46 +05:00
Alex Ant 3095ed5375 installation process 2025-11-06 20:06:40 +05:00
Alex Ant a333e303c1 improve installation process, make privacy policy public and acceptable 2025-11-05 22:13:10 +05:00
Alex Ant bd8512feab clear install 2025-11-03 19:16:57 +05:00
Alex Ant c235fc4a7a fix save wif 2025-11-03 15:55:55 +05:00
Alex Ant 9c283b9aee invite page & set soviet refactoring 2025-11-03 14:16:37 +05:00
Alex Ant 4e824d739b all notifications going throw novu now 2025-11-03 11:09:34 +05:00
Alex Ant fcbbc792b0 make installation starts 2025-11-02 21:58:44 +05:00
Alex Ant ba7cf8e79e Merge branch 'installation' into refactoring 2025-11-02 09:48:41 +05:00
Alex Ant 4ab49d8e43 delete application, domain and infra module aggregators 2025-11-02 09:45:19 +05:00
Alex Ant 7f864db80d wtf 2025-11-01 21:39:23 +05:00
Alex Ant 772a163693 parser fix 2025-11-01 18:04:11 +05:00
Alex Ant c8e106f272 update 2025-11-01 17:53:16 +05:00
Alex Ant ade4f0a1d4 ssr bug fix 2025-11-01 17:14:23 +05:00
Alex Ant 4fcf933dd1 fix: no SSR for some modules 2025-11-01 14:03:45 +05:00
Alex Ant 1fb60771ca fix: synchronize: true in typeorm.module.ts 2025-10-31 23:32:31 +05:00
Alex Ant 2cd391e106 hide capital app for now 2025-10-31 19:52:24 +05:00
Alex Ant 196f3bcba1 level & energy system 2025-10-27 21:36:02 +05:00
Alex Ant bc73c5ca86 contract gamification 2025-10-27 16:19:56 +05:00
Alex Ant f4badc15d1 редактирование участника, шаблон "о себе" 2025-10-27 12:50:51 +05:00
Alex Ant 76f731d0cd poll requests for update content 2025-10-26 18:27:08 +05:00
Alex Ant 5e22858ab4 some bugs and permissions 2025-10-26 15:45:15 +05:00
Alex Ant bc1c54c43e notifications on novu improve (sanity), make a coop settings for chairman and select default workspace / page 2025-10-25 20:19:10 +05:00
Alex Ant 95ac0d36f8 a lot of changes 2025-10-22 19:53:40 +05:00
Alex Ant 2bee3eb179 unique participants count and workflow notification 2025-10-21 17:13:20 +05:00
Alex Ant 52165f2a0b microWallet & workspaceMenu rework 2025-10-20 21:12:53 +05:00
Alex Ant 9f2da226f4 extensionsStore refactoring and make it with multi-desktops 2025-10-20 17:26:24 +05:00
Alex Ant 46b999317d result submission 2025-10-20 15:35:32 +05:00
Alex Ant 80bce3c9c2 refactoring, optimization and improvement 2025-10-17 19:33:07 +05:00
Alex Ant 90b25b4bd8 fab | visual refactoring 2025-10-16 20:34:56 +05:00
Alex Ant f562f1eafd Merge branch 'dev' into capital 2025-10-16 10:27:23 +05:00
Alex Ant ff520622ff chore(release): publish 2025-10-16 10:25:26 +05:00
Alex Ant 1a6c6a4f31 fix parser 2025-10-16 01:32:23 +05:00
Alex Ant 4a9e0388f4 fix parser start block 2025-10-16 01:30:55 +05:00
Alex Ant e47b2a971e chore(release): publish 2025-10-15 23:12:28 +05:00
Alex Ant 96777ec095 delete mongo transactions 2025-10-15 23:00:23 +05:00
Alex Ant 52019deef5 ComponentPage 2025-10-15 22:51:30 +05:00
Alex Ant c26a534ff6 secondary route menu 2025-10-15 20:36:24 +05:00
Alex Ant e5be5c0b05 syncers refactoring and frontend 2025-10-15 13:49:20 +05:00
Alex Ant 5b07f52641 hard update 2025-10-14 22:06:23 +05:00
Alex Ant e0109cb530 pushResult подготовка и отправка документов с ожиданием решения совета 2025-10-12 19:41:53 +05:00
Alex Ant 1496dd1598 approvals with auth start 2025-10-07 19:41:54 +05:00
Alex Ant efcaeb2f00 set plan and other | going to voting 2025-10-07 11:31:56 +05:00
Alex Ant 1252946fe5 permissions and booter 2025-10-05 10:55:53 +05:00
Alex Ant 2913d5f636 some widgets and filters 2025-10-02 17:35:06 +05:00
Alex Ant c50beb3d43 some updates 2025-09-29 13:34:52 +05:00
Alex Ant 46b559001a some refactoring 2025-09-28 16:38:19 +05:00
Alex Ant 55a2e2e060 agreement repo and mutation 2025-09-28 15:48:56 +05:00
Alex Ant 12364a052f desktop agremeent features 2025-09-28 13:18:38 +05:00
Alex Ant 45ac971035 entity log 2025-09-28 11:19:50 +05:00
Alex Ant 26b818c9a6 update 2025-09-27 17:58:04 +05:00
Alex Ant 007836f52e contributor registration #1 2025-09-25 20:16:58 +05:00
Alex Ant ed0982770b mutations for all document generations 2025-09-25 12:47:17 +05:00
Alex Ant dcc72e90b7 factory capital documents basic templates 2025-09-24 21:52:34 +05:00
Alex Ant b428660944 project voting skeleton 2025-09-23 12:56:22 +05:00
Alex Ant 0008dcc0ee time tracker #3 2025-09-22 22:56:45 +05:00
Alex Ant e3fbf3981d time tracker personal time limit 2025-09-22 17:04:09 +05:00
Alex Ant c5e9244a08 selector fix 2025-09-21 23:55:11 +05:00
Alex Ant e1fd823c13 time tracker 2025-09-21 23:53:21 +05:00
Alex Ant 346f0d636c time tracker sdk queries 2025-09-21 20:42:28 +05:00
Alex Ant 315a259e5b lang refactoring 2025-09-21 16:09:19 +05:00
Alex Ant 3157a03c74 time tracking service prototype 2025-09-21 14:50:18 +05:00
Alex Ant 045807a748 edit project and create commit simple button on tasks 2025-09-20 20:52:53 +05:00
Alex Ant ea0e12ac80 update 2025-09-20 17:33:07 +05:00
Alex Ant 692060af6c update 2025-09-19 20:42:16 +05:00
Alex Ant d8b23a4b5c issues create 2025-09-19 10:41:29 +05:00
Alex Ant 074bf55d6c stories complete 2025-09-18 16:28:51 +05:00
Alex Ant 84e505b817 before make pain 2025-09-17 21:23:52 +05:00
Alex Ant a12e6ff603 taskPage 2025-09-17 16:40:18 +05:00
Alex Ant b01d662a1c components 2025-09-16 23:28:34 +05:00
Alex Ant e631dad499 getState, fix some bugs 2025-09-16 17:28:01 +05:00
Alex Ant 7ab0bfdd6b optional right drawer 2025-09-15 22:52:17 +05:00
Alex Ant 35a4db43fb add all entities and make extension store better 2025-09-13 01:02:21 +05:00
Alex Ant 094028006b make new queries and selectors 2025-09-11 22:21:53 +05:00
Alex Ant c12f42fcac some selectors, queries, features and entities 2025-09-11 19:10:37 +05:00
Alex Ant 3e498b098b refactoring #2 2025-09-11 13:03:21 +05:00
Alex Ant bbf1a1a544 refactoring 2025-09-11 12:15:05 +05:00
Alex Ant 42d57f30bf update names 2025-09-10 19:28:13 +05:00
Alex Ant 19967a9ad0 update 2025-09-10 15:23:33 +05:00
Alex Ant 9d1a8f12d8 main domain model 2025-09-10 14:22:14 +05:00
Alex Ant 57bebebe09 domain entities synced with blockchain 2025-09-08 13:47:40 +05:00
Alex Ant d19303cd62 typeorm repositories 2025-09-07 21:06:54 +05:00
Alex Ant 7c96e46a37 domain entities and infrastructure mappers 2025-09-07 20:44:28 +05:00
Alex Ant f75e68f8ce before refactoring 2025-09-06 16:42:29 +05:00
Alex Ant 9b922b00c1 coopgram base plugin 2025-09-06 01:17:31 +05:00
Alex Ant c8d9ee2457 migration fixs 2025-09-05 23:22:49 +05:00
Alex Ant a29f23086a update migrations 2025-09-05 23:07:27 +05:00
Alex Ant 4a76365df4 migrator fix 2025-09-05 22:56:08 +05:00
Alex Ant 972264844a migrator commands and subcommands 2025-09-05 22:46:39 +05:00
Alex Ant e23e6f3c1a some domain modules for capital 2025-09-05 15:03:48 +05:00
Alex Ant 03c10382d1 save deltas and actions and forks to main repositories 2025-09-04 14:21:20 +05:00
Alex Ant 3abe24aa18 refactoring, double bus refactoring, delete notificator module, desktop fix 2025-09-03 22:30:05 +05:00
Alex Ant 2a4eabcda5 update 2025-09-02 11:36:59 +05:00
Alex Ant 280d98c9c4 update docs 2025-09-02 11:35:46 +05:00
Alex Ant 227c5c80c8 chore(release): publish 2025-09-01 20:20:41 +05:00
Alex Ant edd00a6ab5 add capital to whitelist 2025-09-01 19:29:11 +05:00
Alex Ant d57bb17fc7 fix package.json 2025-09-01 19:00:32 +05:00
Alex Ant 098da5c868 small refactoring 2025-09-01 16:41:26 +05:00
Alex Ant dfc17bcc26 complete common tests 2025-09-01 14:18:54 +05:00
Alex Ant 7a5eab104e common capital tests 2025-09-01 11:37:54 +05:00
Alex Ant e0bf815377 cut some in docs 2025-08-27 22:48:37 +05:00
Alex Ant 2cbc6f2890 update 2025-08-27 22:46:38 +05:00
Alex Ant 5859b2c08b some fixs 2025-08-27 22:11:04 +05:00
Alex Ant 44eaa9dc8b economic fix 2025-08-27 22:03:06 +05:00
Alex Ant 49cd7a41dd documentation updates and some capital bug fix 2025-08-27 21:25:27 +05:00
Alex Ant 53de5ece21 test repair 2025-08-27 16:03:00 +05:00
Alex Ant 90dffa5fcc workflow 2025-08-27 00:19:13 +05:00
Alex Ant ac6cd0ca15 update 2025-08-27 00:05:24 +05:00
Alex Ant 0e970128ab update 2025-08-27 00:03:00 +05:00
Alex Ant 9fc373384d update 2025-08-26 23:23:28 +05:00
Alex Ant a76bd2ea55 fix header 2025-08-26 23:15:59 +05:00
Alex Ant 3c610b74d2 update package.json 2025-08-26 20:07:39 +05:00
Alex Ant 0623005ff4 effects, documents and processes 2025-08-26 19:58:20 +05:00
Alex Ant 8b032eba9a some docs 2025-08-25 20:11:42 +05:00
Alex Ant e3229b85fd before update 2025-08-24 11:40:24 +05:00
Alex Ant 1178431b8f update docx 2025-08-22 23:44:35 +05:00
Alex Ant 9d92c4a2b1 update docs 2025-08-22 17:54:10 +05:00
Alex Ant 0bda8707ce before delete anchors 2025-08-22 12:35:16 +05:00
Alex Ant 9cb7d3a0fd docs 2025-08-21 17:01:13 +05:00
Alex Ant f1204f948a update docx 2025-08-20 21:54:54 +05:00
Alex Ant 455b8dafb0 start make doc 2025-08-20 21:53:24 +05:00
Alex Ant fe5a32d95b some tests 2025-08-19 20:19:06 +05:00
Alex Ant b34f6f1690 direct coordinator rewards 2025-08-12 15:18:06 +05:00
Alex Ant a89bc35c6f before coordinators distribution change 2025-08-12 11:51:20 +05:00
Alex Ant e551b6c202 restore losted actions in fund contract 2025-08-08 20:27:09 +05:00
Alex Ant c6ed1afc51 test prepare 2025-08-08 20:24:20 +05:00
Alex Ant 4bedebb74d capital -> cooptypes 2025-08-08 19:38:25 +05:00
Alex Ant 741fc7eb05 готово к отладке 2025-08-08 16:51:46 +05:00
Alex Ant 000da1197c voting by vodyanov system 2025-08-03 16:45:11 +05:00
Alex Ant dd8be29d54 core / startproject 2025-08-01 17:51:15 +05:00
Alex Ant 099c2c7ff4 capital прием инвестиций и расчеты 2025-07-31 21:14:21 +05:00
Alex Ant 874232a152 Merge branch 'dev' into capital 2025-07-30 13:18:14 +05:00
Alex Ant d21f046e1f ignore DS_store 2025-07-30 13:17:58 +05:00
Alex Ant ef0e25878e Merge branch 'dev' into capital 2025-07-30 13:17:35 +05:00
Alex Ant cd37619af1 ledger - переход на debet и credit транзакции 2025-07-30 13:15:58 +05:00
Alex Ant 209ee18943 capital refactoring 2025-07-30 10:15:21 +05:00
Alex Ant aff7733a5b refactoring capital contract 2025-07-29 13:20:42 +05:00
Alex Ant 4bb65921e4 Merge branch 'dev' into capital 2025-07-29 10:02:03 +05:00
Alex Ant 26216a6a8c списание и отмена списания в истории 2025-07-29 10:01:28 +05:00
Alex Ant 60c3f11d7e fix 2025-07-29 00:40:24 +05:00
Alex Ant fcab08bfab история движений средств по счетам 2025-07-29 00:17:33 +05:00
Alex Ant e69ba466e1 chore(release): publish 2025-07-28 21:08:02 +05:00
Alex Ant 675e85ee09 fix precision 2025-07-28 20:26:31 +05:00
Alex Ant 36ec13b2c4 Merge branch 'ledger' into dev 2025-07-28 18:14:06 +05:00
Alex Ant 0b9c36e3ce Merge branch 'dev' into capital 2025-07-28 17:31:25 +05:00
Alex Ant 07cf895447 migration msg change 2025-07-28 17:30:10 +05:00
Alex Ant 028f21f0cf complete ledger contract 2025-07-28 17:14:06 +05:00
Alex Ant 77724e363d замена контракта fund на ledger 2025-07-27 13:13:58 +05:00
Alex Ant 15863d8a5a appendix еще не работает 2025-07-27 10:02:35 +05:00
Alex Ant b836af58f0 Merge branch 'dev' of github.com:coopenomics/mono into capital 2025-07-26 19:02:15 +05:00
Alex Ant ef5d880f14 chore(release): publish 2025-07-23 21:09:51 +05:00
Alex Ant 15fd03b4c0 устранена проверка времени подписи на голосовании 2025-07-23 21:09:07 +05:00
Alex Ant cac06f3e17 chore(release): publish 2025-07-23 15:13:43 +05:00
Alex Ant 6d81c62cdf setWif for cooperative 2025-07-23 15:12:30 +05:00
Alex Ant 60e319589d chore(release): publish 2025-07-20 21:35:08 +05:00
Alex Ant 7723a5785d Merge branch 'testnet' 2025-07-20 21:34:49 +05:00
Alex Ant dd982fe6e2 Merge branch 'dev' into testnet 2025-07-20 21:30:12 +05:00
Alex Ant c02dfeaa97 bug fix на составном префиксе кооператива при голосовании на общем собрании 2025-07-20 21:29:00 +05:00
Alex Ant a3d513afb0 update gitignore 2025-07-20 12:08:36 +05:00
Alex Ant 40fa99a1f9 смарт-контракт адаптирован к ТЗ, рыба контроллера 2025-07-20 12:06:46 +05:00
Alex Ant f26c996095 chore(release): publish 2025-07-15 17:17:20 +05:00
Alex Ant 012c4dfb9f fix 2025-07-15 17:15:14 +05:00
Alex Ant 9337ca2616 chore(release): publish 2025-07-15 16:59:47 +05:00
Alex Ant 5dcec4bcfd Merge branch 'testnet' 2025-07-15 16:57:48 +05:00
Alex Ant ed2e5e4d09 fix build image 2025-07-15 10:37:42 +05:00
Alex Ant 2249f52c32 fix build 2025-07-15 10:19:45 +05:00
Alex Ant f4ef5d2b53 notifications release 2025-07-15 09:59:21 +05:00
Alex Ant a92c5d6ce0 pwa configuration fix and novu subscribers fix 2025-07-14 13:38:34 +05:00
Alex Ant b61ceaead6 widget mode 2025-07-10 19:47:11 +05:00
Alex Ant 0672cfc8f3 delete notificator2 2025-07-10 17:29:43 +05:00
Alex Ant b5859a43cc Merge branch 'dev' into testnet 2025-07-10 16:49:33 +05:00
Alex Ant de1cd7c737 pwa push notifications 2 2025-07-10 16:45:07 +05:00
Alex Ant f588312ad3 pwa push notifications 2025-07-10 16:44:42 +05:00
Alex Ant 643ef1fa45 chore(release): publish 2025-07-08 14:17:04 +05:00
Alex Ant ec38a4ed33 reset and invite emails fix 2025-07-08 14:15:07 +05:00
Alex Ant defbd80ce0 fix contacts 2025-07-08 13:08:42 +05:00
Alex Ant f11b91e7fa auto reload on update 2025-07-08 12:56:05 +05:00
Alex Ant 58bb4d5a7f fix contacts bug 2025-07-08 12:43:01 +05:00
Alex Ant d92af60d9b лист подключенных кооперативов и документация 2025-07-08 10:14:04 +05:00
Alex Ant bca1e043bd chore(release): publish 2025-07-07 10:26:39 +05:00
Alex Ant 7719175e85 update 2025-07-07 10:13:31 +05:00
Alex Ant 046146c4d8 delete sdk docs from repo 2025-07-07 09:45:43 +05:00
Alex Ant 0e2f66b35a add gitignore 2025-07-07 09:45:06 +05:00
Alex Ant 56b9ff1dc9 docs, registration fix and connection button fix 2025-07-07 09:44:06 +05:00
Alex Ant dab8e78cd5 chore(release): publish 2025-07-06 23:15:03 +05:00
Alex Ant 67dd27b9a1 pwa rollback 2025-07-06 23:11:29 +05:00
Alex Ant 4605985a47 fix 2025-07-06 22:45:39 +05:00
Alex Ant 02ac27342a fix exlude error 2025-07-06 22:37:06 +05:00
Alex Ant cbc59a4a1c chore(release): publish 2025-07-06 21:48:06 +05:00
Alex Ant 02758d96c7 PWA service worker update 2025-07-06 21:44:36 +05:00
Alex Ant 12752134fd стол председателя 2025-07-06 17:32:43 +05:00
Alex Ant 504e70441b move exit button 2025-07-06 14:34:58 +05:00
Alex Ant 85cd16e793 navigation guard fix & desktop store left drawer mobile improve 2025-07-06 13:22:15 +05:00
Alex Ant aa2277f05e visual fixs 2025-07-06 11:46:21 +05:00
Alex Ant 27eb96722a chore(release): publish 2025-07-05 18:14:55 +05:00
Alex Ant 0215096c9e изменение принципа отображения кнопок действия 2025-07-05 18:13:31 +05:00
Alex Ant 90d8c51402 фикс гоночного бага при входе, демонстрация входящих оповещений всплывками 2025-07-05 16:45:51 +05:00
Alex Ant ba0bbfefe7 fix boot index 2025-07-05 13:02:25 +05:00
Alex Ant dd65673c1e pwa+ssr configuration 2025-07-05 12:54:30 +05:00
Alex Ant 2f3f8de682 fix 2025-07-05 10:18:32 +05:00
Alex Ant 3ce6fcad3a fix 2025-07-05 09:43:43 +05:00
Alex Ant 4302e7426e update quasar config 2025-07-04 23:09:25 +05:00
Alex Ant 4e246c35c8 try it 2025-07-04 22:44:43 +05:00
Alex Ant 0cbcae9624 update 2025-07-04 21:35:32 +05:00
Alex Ant 313aed606f update 2025-07-04 21:24:47 +05:00
Alex Ant aade735c79 Merge branch 'dev' into testnet 2025-07-04 21:17:50 +05:00
Alex Ant 1e5786a142 looking for bug 2025-07-04 21:14:45 +05:00
Alex Ant 3e5e5eeafa Merge branch 'dev' into testnet 2025-07-04 21:07:56 +05:00
Alex Ant 8a48ee8e14 logging 2025-07-04 21:04:56 +05:00
Alex Ant 6a89981c77 try add env to PWA 2025-07-04 19:59:55 +05:00
Alex Ant 03c014fdda rollback 2025-07-04 19:32:30 +05:00
Alex Ant ae683cd3c9 rollback env 2025-07-04 19:03:26 +05:00
Alex Ant a0dcae0882 pwa env inject 2025-07-04 18:34:43 +05:00
Alex Ant 6e839b210c update 2025-07-04 17:06:08 +05:00
Alex Ant c7f4f62e93 update package.json 2025-07-04 16:50:59 +05:00
Alex Ant 214aba8f01 pwa 2025-07-04 16:07:38 +05:00
Alex Ant 153c9d944f Merge branch 'notifications' into dev 2025-07-04 13:48:05 +05:00
Alex Ant 99a9d15871 pwa implementation 2025-07-04 13:47:42 +05:00
Alex Ant f6fb905ef8 Генерация, синхронизация и выдача данных для центра уведомлений 2025-07-04 13:12:33 +05:00
Alex Ant a5456452f6 http auth 2025-07-03 18:48:50 +05:00
Alex Ant 48cd9af739 Merge commit 'b9ae66e557f82e1265ae8d71c7fd6ec38509b5f1' into notifications 2025-07-03 18:09:23 +05:00
Alex Ant 01fac428d5 chore(release): publish 2025-07-03 18:05:11 +05:00
Alex Ant 3acce100f6 Merge branch 'testnet' 2025-07-03 18:04:50 +05:00
Alex Ant ba2dfe6598 Merge branch 'dev' into testnet 2025-07-03 18:04:27 +05:00
Alex Ant 46600c38a8 fast bug fix 2025-07-03 18:04:17 +05:00
Alex Ant b9ae66e557 чуть не забыл 2025-07-03 18:03:31 +05:00
Alex Ant bed9010918 some steps 2025-07-03 17:58:45 +05:00
Alex Ant d13ccbde71 bug: meet service improve 2025-07-02 13:27:31 +05:00
Alex Ant 2e2aac0413 chore(release): publish 2025-07-02 12:16:36 +05:00
Alex Ant 5f18cff344 fix: meet сервис на отправку уведомления о повторном собрании 2025-07-02 11:58:51 +05:00
Alex Ant 830300ea81 chore(release): publish 2025-07-01 21:03:01 +05:00
Alex Ant 9da2fcfbca migration 2025-07-01 20:31:21 +05:00
Alex Ant ece68df495 добавлены анимации для входа и выхода меню, улучшена логика рендеринга. Обновлены стили и добавлен ключ для перерендера при смене маршрутов. Исправлены мелкие недочеты в других компонентах. 2025-07-01 16:46:33 +05:00
Alex Ant 6a43ddbee9 chore(release): publish 2025-07-01 16:04:01 +05:00
Alex Ant ee58596ed0 Упрощен компонент ShadowHtml: удалена проверка на клиентскую среду, добавлены логи для отладки, обновлено состояние миграции с изменением тестового файла. 2025-07-01 15:45:10 +05:00
Alex Ant 5bce6e77ae Исправлено название типа платежа "Возврат средств" на "Возврат взноса" в PaymentDomainEntity. 2025-07-01 13:36:13 +05:00
Alex Ant 5dec45c0c8 Рефакторинг пользователя в сессии на фронте, правки визуального отображения всех страниц, управление исходящими платежами в шлюзе, возврат паевого взноса в кошельке. 2025-07-01 13:34:46 +05:00
Alex Ant b7ba6500e4 Новая архитектура для входящих и исходящих платежей 2025-06-26 20:20:58 +05:00
Alex Ant 2a263d749b returnByMoneyDecision Document 2025-06-25 20:34:12 +05:00
Alex Ant 3a21af48d1 Добавляем шаблон заявления на возврат паевого взноса с ЦК 2025-06-24 16:18:34 +05:00
Alex Ant cb3bfe99ce chore(release): publish 2025-06-24 09:07:26 +05:00
Alex Ant 328d51622f fix navigation guard bug 2025-06-24 08:50:05 +05:00
Alex Ant 38ead74697 поле ввода для поиска пайщика и извлечения его username 2025-06-23 20:56:07 +05:00
Alex Ant ffd4d72850 search by private account 2025-06-23 13:30:34 +05:00
Alex Ant d23d90a794 Корректное удаление кооперативного участка. Общее собранее уполномоченных. Правки title шаблонов. 2025-06-19 12:09:10 +05:00
Alex Ant c74036ad26 chore(release): publish 2025-06-19 10:00:51 +05:00
Alex Ant a146a9f424 Правка сообщений с ошибками на фронте, расширение шаблонов общих собраний на повторяемость. 2025-06-19 09:41:41 +05:00
Alex Ant ddc0c82cee chore(release): publish 2025-06-14 21:27:59 +05:00
Alex Ant bab09ac0d7 FATALITY BUG FIX 2025-06-14 21:26:41 +05:00
Alex Ant 8a6eb912c5 Revert "откатываем функционал"
This reverts commit e5642cd752.
2025-06-14 20:28:02 +05:00
Alex Ant b5fbd40ea3 update workflow #2 2025-06-14 17:56:14 +05:00
Alex Ant 46b1c085cd workflow fix 2025-06-14 16:09:22 +05:00
Alex Ant e5642cd752 откатываем функционал 2025-06-14 13:40:01 +05:00
Alex Ant be933e9085 fix hash router 2025-06-14 11:07:35 +05:00
Alex Ant 26769675c7 rollback 2025-06-14 09:07:57 +05:00
Alex Ant 672d6d67aa chore(release): publish 2025-06-13 20:45:38 +05:00
Alex Ant b3c6967ed9 update 2025-06-13 20:18:34 +05:00
Alex Ant 9a528b17e8 update 2025-06-13 20:15:39 +05:00
Alex Ant 4532932749 back to hash routing 2025-06-13 19:48:36 +05:00
Alex Ant 639a8e2f35 update 2025-06-13 19:20:02 +05:00
Alex Ant 56c2ab38c6 looking for bug 2025-06-13 17:08:27 +05:00
Alex Ant 9769cc0ed4 enable test mode 2025-06-13 16:15:50 +05:00
Alex Ant 96d66b2205 Обновлено использование индекса cooperatives в shared_registrator.hpp, добавлено новое действие delmeet в meet для удаления собраний. Исправлены проверки на наличие активных пайщиков и добавлено форматирование ссылок в компонентах голосования и повестки. Обновлены формы создания и перезапуска собраний для поддержки новых требований. 2025-06-13 13:26:32 +05:00
Alex Ant fea73109ca Добавлено использование функции parseLinks для форматирования ссылок в контексте и решениях в компонентах повестки и голосования. Упрощен код за счет удаления дублирующей функции parseLinks из компонентов. 2025-06-12 20:26:43 +05:00
Alex Ant e39c15c16f Добавлено форматирование ссылок в повестке собрания. Реализована функция для преобразования текста с ссылками в HTML-формат. Исправлено отображение контекста повестки. 2025-06-12 20:22:33 +05:00
Alex Ant 2a13f60bbd Добавлено новое поле 'level' в интерфейсы и DTO для обработки собраний, обновлены адаптеры для поддержки этого поля. Пофиксена проблема с Float на проценте кворума. 2025-06-12 20:14:55 +05:00
Alex Ant d8e9738d71 fix count 2025-06-12 19:17:45 +05:00
Alex Ant f38b78f821 подсчитываем количество активных пайщиков в кооперативах 2025-06-12 15:47:27 +05:00
Alex Ant 9c21630295 meet fix 2025-06-12 15:45:12 +05:00
Alex Ant ea502727ba добавлено действие migrate в meet и созданы миграции на добавление новых шаблонов 2025-06-12 14:56:42 +05:00
Alex Ant 72f4999162 release 2025-06-12 11:13:51 +05:00
Alex Ant adf1dbe635 Добавлены новые зависимости moment-timezone и @novu/js, обновлены версии существующих пакетов. Внесены изменения в модули и сервисы для поддержки новых функциональностей, включая адаптеры для расширений и обработку уведомлений. Реализована логика работы с временными зонами и обновлены интерфейсы для работы с аккаунтами и собраниями. 2025-06-11 21:39:13 +05:00
Alex Ant af2cdb1ca1 Добавлены новые поля для ответов голосования в схемы и DTO, а также обновлены интерфейсы и типы для поддержки голосования на собраниях. Реализована логика обработки ответов и обновлены компоненты для работы с новыми данными. Добавлена поддержка часового пояса в форматирование дат. 2025-06-10 14:14:44 +05:00
Alex Ant e499c0e990 Обновлены компоненты рабочего стола: добавлены маршруты по умолчанию для рабочих столов "participant" и "soviet", реализована логика выбора рабочего стола на основе прав пользователя и сохраненного выбора. Также добавлены новые методы для навигации и сохранения состояния рабочего стола. Исправлены ошибки в компонентах и улучшена верстка. 2025-06-09 17:54:33 +05:00
Alex Ant 206ab6793f almost works 2025-06-09 15:27:03 +05:00
Alex Ant e207c90fd4 add exampleData 2025-06-03 13:12:35 +05:00
Alex Ant c9864008a3 update 2025-06-03 10:54:14 +05:00
Alex Ant 5c89b27a87 update 2025-06-02 23:15:51 +05:00
Alex Ant 3c482ba017 update cooptypes and factory 2025-06-02 23:00:32 +05:00
Alex Ant 2d684411d4 Добавлена поддержка часового пояса в компоненты создания и перезапуска собраний. Обновлены метки для отображения времени с учетом часового пояса. Добавлены зависимости moment-timezone и обновлены конфигурации окружения. 2025-05-30 22:54:22 +05:00
Alex Ant 408ee36a8b Поправлена верстка. Обновлены компоненты для отображения новых данных о вопросах и результатах голосования. Изменен SDK для корректной мульти-подписи документа. 2025-05-30 21:42:07 +05:00
Alex Ant d9efbad80d Добавлены поля для председателя и секретаря в интерфейсы и DTO собраний, а также обновлены соответствующие селекторы и агрегаты документов. Введены сертификаты для председателя и секретаря, обновлены зависимости и исправлены ссылки на информацию о пользователях в документах. 2025-05-23 17:48:52 +05:00
Alex Ant 92e5c9f71a Добавлены новые типы сертификатов пользователей (EntrepreneurCertificate, IndividualCertificate, OrganizationCertificate, RepresentedByCertificate) и обновлены соответствующие DTO и интерфейсы. Обновлены зависимости в модулях и исправлены ссылки на информацию о подписантах в документах. 2025-05-23 14:14:56 +05:00
Alex Ant baef88be50 expandableDocument for meet 2025-05-23 10:49:03 +05:00
Alex Ant 7eff6ac504 meet aggregation DTO 2025-05-22 17:51:29 +05:00
Alex Ant a3ea4d4e18 chore(release): publish 2025-05-14 16:16:51 +05:00
Alex Ant f12047e36f повторная миграция соглашений 2025-05-14 15:41:34 +05:00
Alex Ant c77f37cc43 chore(release): publish 2025-05-14 12:45:52 +05:00
Alex Ant 8bc463b6d6 Обновлен changelog для версии v2025.5.14. 2025-05-14 12:22:46 +05:00
Alex Ant 953429f0da Добавлена поддержка смены темы (светлая/темная) с сохранением в localStorage, обновлены соответствующие компоненты и стили. Исправлено отображение данных участников в таблицах. 2025-05-14 12:12:00 +05:00
Alex Ant 8598050931 Добавлено поле аккаунта в тип Payment и обновлены соответствующие сущности и DTO, а также интерфейс для отображения ФИО плательщика. 2025-05-13 18:15:21 +05:00
Alex Ant 81566674a8 chore(release): publish 2025-05-13 14:56:31 +05:00
Alex Ant aba9f88352 factory bugs fix 2025-05-13 10:19:44 +05:00
Alex Ant 0093c6ddf7 fix bug on dinamic import 2025-05-13 09:10:04 +05:00
Alex Ant cd5a0c2fbc процесс голосования через подпись номера решения 2025-05-12 10:41:37 +05:00
Alex Ant b1c803d2f1 almost done 2025-05-09 17:31:41 +05:00
Alex Ant 3883b68669 SDK компилируется 2025-05-07 08:23:59 +05:00
Alex Ant 5111bb12cf Merge branch 'dev' into document2 2025-05-05 18:56:04 +05:00
Alex Ant cc2518a346 chore(release): publish 2025-05-05 18:55:34 +05:00
Alex Ant 6e98f4c953 update version 2 2025-05-05 18:55:01 +05:00
Alex Ant 8990b25970 update version 2025-05-05 18:53:25 +05:00
Alex Ant c0cdde8685 Merge branch 'testnet' 2025-05-05 18:50:23 +05:00
Alex Ant 7a7e20e4c2 откатываем новую таблицу кооперативов 2025-05-05 18:49:08 +05:00
Alex Ant 743ccd27b6 контракты скомпилировались 2025-05-05 18:46:44 +05:00
Alex Ant 155d9110c5 before contract migrate 2025-05-05 17:25:56 +05:00
Alex Ant 651e0b3445 chore(release): publish 2025-05-05 12:53:42 +05:00
Alex Ant 82f438cbe4 coop migration 2025-05-05 12:28:54 +05:00
Alex Ant c3c30b4e20 chore(release): publish 2025-05-05 11:17:29 +05:00
Alex Ant 8b4889ce83 Функционал миграций по версиям с интеграцией блокчейна и двух баз данных 2025-05-04 14:17:27 +05:00
Alex Ant 7ccaa0390d chore(release): publish 2025-05-04 12:42:52 +05:00
Alex Ant 5f0ed73d0d fix workflow #2 2025-05-04 12:42:23 +05:00
Alex Ant 54518b63d0 update publish workflow 2025-05-04 12:39:52 +05:00
Alex Ant 1c38f2e5fd update version 2025-05-04 12:37:53 +05:00
Alex Ant 7ba12707e1 chore(release): publish 2025-05-04 12:32:16 +05:00
Alex Ant 01992b26f4 fix workflows 2025-05-04 12:26:07 +05:00
Alex Ant 8670ba08d5 chore(release): publish 2025-05-04 12:08:16 +05:00
Alex Ant 67698757cf publish workflow 2025-05-04 12:07:22 +05:00
Alex Ant 833ef3f5d3 Обновление структуры signature_info: добавлен идентификатор подписи. Исправлено условие фильтрации истекших решений в ChairmanPlugin. 2025-05-04 11:40:00 +05:00
Alex Ant b104f14899 create document2 format 2025-05-04 11:02:25 +05:00
Alex Ant 60c86b1db8 chore(release): publish 2025-05-03 16:19:40 +05:00
Alex Ant b6d2965962 Дефолтные конфиги для расширений, фиксы визуалов, микросервис отмены вопроса с повестки 2025-05-03 15:56:08 +05:00
Alex Ant 1548552af3 chore(release): publish 2025-05-02 10:40:41 +05:00
Alex Ant 002cf8025b update 2025-05-01 22:51:07 +05:00
Alex Ant dfd7e4b62c update 2025-05-01 22:46:58 +05:00
Alex Ant e54380cd81 update 2025-05-01 22:29:40 +05:00
Alex Ant 09b3532793 update 2025-05-01 22:28:01 +05:00
Alex Ant b36e288958 update 2025-05-01 22:23:56 +05:00
Alex Ant bcd3c2739a update 2025-05-01 22:21:00 +05:00
Alex Ant 695e653461 update 2025-05-01 22:17:07 +05:00
Alex Ant b068ff810c update 2025-05-01 22:13:38 +05:00
Alex Ant 1a57f7a047 update lock-file 2025-05-01 21:57:50 +05:00
Alex Ant 90f955e48e update 2025-05-01 21:54:59 +05:00
Alex Ant 14fbce5a6b docs #2 2025-05-01 21:52:49 +05:00
Alex Ant 5cff407379 docs 2025-05-01 21:50:39 +05:00
Alex Ant 79b7ff21f5 Удалены отладочные логи из функций входа и выхода пользователя, а также обновлен способ проверки переменных окружения в функции работы с IndexedDB. 2025-05-01 18:23:56 +05:00
Alex Ant 65996a40e9 update 2025-05-01 16:46:36 +05:00
Alex Ant cf3566255a Добавлены отладочные логи в функции входа и выхода пользователя, а также обновлен способ загрузки переменных окружения в конфигурации Quasar для режима разработки. 2025-05-01 16:00:45 +05:00
Alex Ant 582c6fa36d Удалены отладочные логи из функции получения переменных окружения и обновлен middleware для инъекции переменных окружения в браузер с использованием ssrMiddleware. 2025-05-01 14:33:41 +05:00
Alex Ant aefba8b4df update 2025-05-01 13:42:00 +05:00
Alex Ant e43674fd29 Изменен способ определения переменных окружения в конфигурации Quasar: теперь они устанавливаются только для клиентской стороны, если режим не SSR. 2025-05-01 13:27:05 +05:00
Alex Ant 3856826e4d update 2025-05-01 11:34:33 +05:00
Alex Ant 00ed0b7691 update 2025-05-01 11:22:00 +05:00
Alex Ant aa456f6cff update 2025-05-01 11:12:30 +05:00
Alex Ant 3a9dfa643e update 2025-05-01 11:01:19 +05:00
Alex Ant 28580114da update 2025-05-01 10:31:01 +05:00
Alex Ant 8d47f118dd update 2025-05-01 10:29:53 +05:00
Alex Ant d7b45157ff update 2025-05-01 10:27:41 +05:00
Alex Ant db8a48284f update 2025-05-01 10:24:33 +05:00
Alex Ant b00b8d6ca3 update 2025-05-01 09:59:02 +05:00
Alex Ant 22b3d8f691 update 2025-05-01 09:51:43 +05:00
Alex Ant 432c1cd476 update 2025-05-01 09:41:37 +05:00
Alex Ant ed8e4a059a Обновление Dockerfile: замена команды установки зависимостей на принудительную, улучшение структуры образа. Изменение конфигурации GitHub Actions для отключения кеширования и упрощения сборки образов. 2025-05-01 09:36:57 +05:00
Alex Ant 571c1e31ac Оптимизация Dockerfile: добавление этапа сборки для зависимостей, упрощение копирования файлов и улучшение структуры образа. Обновление конфигурации GitHub Actions для сборки и публикации образов с использованием Docker Buildx. 2025-04-30 21:49:16 +05:00
Alex Ant b294d0eea0 Добавление шагов для отладки в GitHub Actions: вывод последних коммитов, текущей ветки и содержимого компонентов. Обновление комментариев и улучшение сборки образов без кеша. 2025-04-30 21:32:51 +05:00
Alex Ant 0b50fbc55d update 2025-04-30 21:29:50 +05:00
Alex Ant f5d5534290 update 2025-04-30 20:54:13 +05:00
Alex Ant 777eb7e398 Добавление middleware для инъекции переменных окружения в window.__ENV__ и рефакторинг функции получения переменных окружения с учетом различных сред (SSR, SPA). Теперь переменные окружения экспортируются через единую функцию getEnv(). 2025-04-30 20:53:02 +05:00
Alex Ant 9b1be55a9e Обновление конфигурации проекта: замена использования переменных окружения на новый модуль env, упрощение Dockerfile, улучшение сборки контейнеров и исправление мелких ошибок в компонентах. 2025-04-30 20:30:42 +05:00
Alex Ant ef4a7d1fa1 update 2025-04-30 18:37:23 +05:00
Alex Ant e6a7380aa5 update 2025-04-30 18:00:07 +05:00
Alex Ant 2f41591339 update 2025-04-30 17:47:08 +05:00
Alex Ant 8916ef0207 update 2025-04-30 17:45:28 +05:00
Alex Ant c3288f765e update 2025-04-30 17:35:42 +05:00
Alex Ant 11d631bee3 update 2025-04-30 17:33:58 +05:00
Alex Ant e3be78edb0 update 2025-04-30 17:29:37 +05:00
Alex Ant 59f01a8426 update 2025-04-30 17:17:55 +05:00
Alex Ant 76b7c8ef3b update 2025-04-30 17:05:31 +05:00
Alex Ant fbaae9ce76 update 2025-04-30 16:53:55 +05:00
Alex Ant e70b58a3df print env 2025-04-30 16:47:47 +05:00
Alex Ant ec551c9de8 VITE_ prefix 2025-04-30 16:39:33 +05:00
Alex Ant 7a8cb5835a правки SSR проблема рендиринга 2025-04-30 15:55:07 +05:00
Alex Ant 3b8ccf173b modify quasar conf 2025-04-30 12:49:12 +05:00
Alex Ant 172ce29665 update 2025-04-30 11:32:47 +05:00
Alex Ant e7d8a8d2c0 хук на деплой 2025-04-30 10:29:59 +05:00
Alex Ant cc7b43f4ba Добавление сборки и публикации нового Docker-образа для приложения @coopenomics/desktop в workflow. Обновление конфигурации сборки контейнеров. 2025-04-29 23:23:13 +05:00
Alex Ant 71425977f5 Обновление Dockerfile для упрощения сборки с использованием lerna. Добавление новых прокси-файлов для модулей @dicebear/core, @dicebear/collection и email-regex. Обновление зависимостей в package.json и pnpm-lock.yaml. Изменение конфигурации Quasar для оптимизации работы с новыми модулями. 2025-04-29 23:21:10 +05:00
Alex Ant 4549727d0b update workflow 2025-04-29 21:49:48 +05:00
Alex Ant e030d97059 workflow initialization 2025-04-29 21:40:51 +05:00
Alex Ant 13c1ca4f79 Merge branch 'main' into dev 2025-04-29 16:32:03 +05:00
Alex Ant 637d7c0db1 chore(release): publish 2025-04-29 16:27:09 +05:00
Alex Ant 7513a4b6d6 Merge branch 'testnet' 2025-04-29 16:21:40 +05:00
Alex Ant 76100ef07d update script 2025-04-29 16:21:22 +05:00
Alex Ant ab139209db update templates 2025-04-29 16:17:58 +05:00
Alex Ant c6a31baaab Добавление нового поля 'type' в сущность Payment и соответствующие изменения в DTO, схемах и селекторах. Обновление логики обновления статусов платежей с учетом нового поля. Исправление параметров в методах для получения заказов. 2025-04-29 13:36:04 +05:00
Alex Ant 7c9d4629b6 Добавление нового поля 'coop' в интерфейс Model и обновление схемы валидации для SelectBranchStatement. Обновление логики комбинирования данных в классе Factory для включения данных о кооперативе. Комментарии в стилях инпутов переведены в закомментированный вид. 2025-04-29 12:25:08 +05:00
Alex Ant 986858c20d Обновление скриптов публикации и логики миграции. Изменены команды для публикации на продакшн, добавлен новый скрипт publish-prod.sh. Обновлена логика удаления кандидатов в функции миграции и исправлены ссылки на индексы кандидатов в confirmreg.cpp. 2025-04-29 11:05:03 +05:00
Alex Ant a0551095dc lerna update 2025-04-29 10:16:02 +05:00
Alex Ant d0804c886a disable meet for temp 2025-04-29 10:11:14 +05:00
Alex Ant 1befca7e28 Добавление новых скриптов для развертывания и обновление интерфейсов для обработки голосования на общем собрании. Обновлены типы данных в интерфейсах и DTO, добавлены новые методы для генерации документов и улучшения функционала. Удалены устаревшие интерфейсы и компоненты, улучшена структура кода для повышения эффективности. 2025-04-28 23:55:55 +05:00
Alex Ant 3d4482157a Удаление устаревших интерфейсов и таблиц, связанных с кошельками. Обновление логики обработки платежей и интерфейсов для работы с новыми типами данных. Улучшение структуры компонентов и интерфейсов для более эффективного взаимодействия с API. Обновление стилей и компонентов пользовательского интерфейса для улучшения визуального восприятия. 2025-04-26 17:14:22 +05:00
Alex Ant 3cd71f1cd4 Обновление интерфейсов и DTO для работы с документами и собраниями. Добавлены новые поля и типы данных, улучшена обработка документов в сервисах. Удалены устаревшие селекторы и обновлены существующие для соответствия новым требованиям. Исправлены типы данных в селекторах и добавлены новые экспорты. 2025-04-25 23:44:32 +05:00
Alex Ant eebf443c1d Сейчас будем переводить сервис документов на агрегатор. Обновление интерфейсов и DTO для обработки документов. Изменены типы полей в интерфейсах DocumentPackageDomainInterface, DocumentRepository и DocumentDomainService для поддержки значений null. Добавлены проверки на наличие данных в DocumentDomainEntity и улучшена обработка документов в DocumentDomainService. Обновлены методы для работы с документами в API и хранилище. 2025-04-23 19:03:18 +05:00
Alex Ant 7aaf0b9375 Первичная мобильная верстка страниц стола совета 2025-04-23 12:39:23 +05:00
Alex Ant 5ff9675e60 Обновление конфигурации и добавление тестового режима для собраний. Изменен порт для state-history-endpoint, добавлен флаг TEST_MODE в контракте meet, обновлены проверки дат открытия и закрытия собраний. Обновлены интерфейсы и DTO для обработки новых типов данных и улучшения функционала. 2025-04-22 23:23:09 +05:00
Alex Ant 646390edff Добавление функционала для генерации бюллетеней голосования на общем собрании. Обновлены интерфейсы и DTO для обработки новых типов документов, добавлены методы для генерации бюллетеней и голосования. Исправлены типы данных для идентификаторов вопросов повестки. 2025-04-21 20:29:12 +05:00
Alex Ant b1958c5442 Добавление новых методов для подписания решений председателем и секретарем на общем собрании. Удален метод закрытия собрания, обновлены интерфейсы и DTO для обработки новых типов документов и действий. 2025-04-21 16:44:27 +05:00
Alex Ant 01b9590e81 Добавление новых типов и методов для работы с собраниями в модуле общих собраний. Реализованы методы получения данных о собраниях, обновлены интерфейсы и DTO для обработки информации о голосованиях и документах собраний. 2025-04-20 16:41:20 +05:00
Alex Ant 985a288bf3 Добавление новых входных типов и методов для получения данных о собраниях в модуле общих собраний. Реализованы методы getMeet и getMeets, а также обновлены интерфейсы и DTO для обработки данных. 2025-04-20 12:56:42 +05:00
Alex Ant 01dcfb88e7 Обновление документации и DTO для модуля общих собраний. Добавлены описания для полей в DTO, улучшена структура и добавлены новые типы данных для обработки информации о собраниях и голосованиях. 2025-04-20 11:59:29 +05:00
Alex Ant 76415a8dec Добавление модуля общих собраний и расширение DTO для документов, включая новые интерфейсы и классы для обработки документов собраний и решений. 2025-04-20 11:39:27 +05:00
Alex Ant 586b7c14f8 meet module & extends all documents DTOs 2025-04-18 15:27:35 +05:00
Alex Ant 0ee4bf2b25 update release script 2025-04-16 22:03:36 +05:00
Alex Ant 5673e8abe8 update releases script 2025-04-16 22:02:08 +05:00
Alex Ant c446e32a8a generate changelog releases 2025-04-16 21:42:37 +05:00
Alex Ant eee9392fb4 make Templates correct 2025-04-16 20:08:50 +05:00
Alex Ant 82a3961608 move templates #1 2025-04-16 20:08:24 +05:00
Alex Ant f8689fc409 wtf?! 2025-04-16 20:06:49 +05:00
Alex Ant 7de93e1780 add meet contract, cooptypes and empty documents templates 2025-04-16 19:58:49 +05:00
Alex Ant 19e742b7d1 desktop bug fixs 2025-04-15 15:57:55 +05:00
Alex Ant e3f0250fd2 remove Env.ts file from desktop and move params to .env 2025-04-15 12:27:29 +05:00
Alex Ant e4b39b89f3 fix system contract action name 2025-04-15 10:27:53 +05:00
Alex Ant d2888b49e8 fix contracts build 2025-04-15 10:14:07 +05:00
Alex Ant b97ed49a60 coopStore release 2025-04-11 18:29:07 +05:00
Alex Ant 65936dc85b refactoring desktop 2025-04-10 11:40:14 +05:00
Alex Ant b1d02ee302 claim -> result, result -> assignment 2025-04-06 12:56:55 +05:00
Alex Ant cca47abb9c before make refactoring 2025-04-06 11:25:29 +05:00
Alex Ant c810ba62b4 shared actions 2025-04-05 11:36:06 +05:00
Alex Ant dcce7ffacc loan contract 2025-04-03 18:52:53 +05:00
Alex Ant e9fb137146 contracts refactoring 2025-04-03 09:36:48 +05:00
Alex Ant 7100c9c300 appStore & medusa component & capital contract (start rework) 2025-03-28 18:42:28 +05:00
Alex Ant e9b5496387 refactoring participant widget to page 2025-03-27 19:14:02 +05:00
Alex Ant 87ecde390d chore(release): publish 2025-03-27 18:13:33 +05:00
Alex Ant 8b33c66e22 update mutation simplification 2025-03-27 17:30:31 +05:00
Alex Ant b3bbb6624e make participants editable 2025-03-27 14:32:23 +05:00
Alex Ant 2ddac45612 change decision dto and logout on some auth problems 2025-03-26 22:18:12 +05:00
Alex Ant ddd7d47516 Merge pull request #201 from coopenomics/kamilocean/sprint-4
Kamilocean/sprint 4
2025-03-26 20:09:37 +03:00
Alex Ant a770072809 README.md for cleos wallet 2025-03-26 18:27:05 +05:00
Alex Ant 5b38ce0b37 make cleos separated component 2025-03-26 18:19:52 +05:00
Alex Ant 804708076a make cleos wallet accessable 2025-03-26 13:08:04 +05:00
Alex Ant a0ad71a524 make unnesassary result 2025-03-26 11:42:05 +05:00
Alex Ant 9d03694ea7 some updates on contracts and tests 2025-03-22 13:58:51 +05:00
Alex Ant 7a619e0e9e update 2025-03-21 17:58:33 +05:00
Alex Ant 6bd5eb7930 Merge branch 'dev' into TSK-900 2025-03-19 10:58:53 +05:00
Alex Ant 744d1859d5 Создание кошелька при регистрации участника 2025-03-19 10:58:20 +05:00
Alex Ant 8033e3fc8e update 2025-03-18 21:04:12 +05:00
Alex Ant abb0b629f3 TSK-900 capital contract - fund by membership contributions and distribution it 2025-03-18 11:50:07 +05:00
kamilocean 34a61f49f6 Merge branch 'dev' into kamilocean/sprint-4 2025-03-17 16:07:50 +03:00
kamilocean 08c31171a0 WIP: fix EditableActions instead of ActionButton 2025-03-17 16:07:28 +03:00
Ilia Smirnov 40addbf595 Merged readme fixes 2025-03-17 12:52:02 +03:00
Ilia Smirnov abd6da0a37 Merge branch 'dev' of github.com:coopenomics/mono into TSK-878 2025-03-17 12:49:40 +03:00
Alex Ant 794d1c5019 Merge pull request #217 from coopenomics/dacom-dark-sun/sprint-4
Спринт-4 | dacom-dark-sun
2025-03-17 09:57:34 +03:00
Alex Ant b8d048bf3c README.md 2025-03-17 11:55:18 +05:00
Ilia Smirnov bb7b650ebc docs(install): updated installation instructions 2025-03-17 09:44:10 +03:00
kamilocean 5253de9279 WIP: add EditableOrganizationCard 2025-03-16 21:04:40 +03:00
Alex Ant 094945e350 make it works 2025-03-14 13:20:41 +05:00
kamilocean eb6b58e6db Merge branch 'dev' into kamilocean/sprint-4 2025-03-13 23:54:25 +03:00
kamilocean 7bcb6e30a7 fix: add Zeus type 2025-03-13 23:54:02 +03:00
Alex Ant 7ed4024555 run container on boot 2025-03-13 22:58:46 +05:00
Alex Ant 6fa45c5958 bug fix 2025-03-13 22:39:41 +05:00
Alex Ant a883f140d4 clear database on reboot 2025-03-13 21:04:21 +05:00
Alex Ant 1bec52efcb bug fix #2 2025-03-13 20:50:42 +05:00
Alex Ant 674e58ec3f fix bug 2025-03-13 20:46:53 +05:00
Alex Ant 373d705387 update nodemon for notificator 2025-03-13 20:33:56 +05:00
Alex Ant 46fb693020 add clear method 2025-03-13 20:10:47 +05:00
Alex Ant f1fe7d1545 make boot better 2025-03-13 19:57:35 +05:00
kamilocean 44ab386835 WIP: EditableAccountCard 2025-03-13 00:30:00 +03:00
Alex Ant 51d736eba4 chore(release): publish 2025-03-12 11:33:00 +05:00
Alex Ant dcc4f6bd03 fix packages 2025-03-12 10:31:41 +05:00
Alex Ant eef2482423 README.md and some setup fixs 2025-03-12 10:24:48 +05:00
Alex Ant f46a1fb9b5 ascii text fix 2025-03-12 09:51:52 +05:00
Alex Ant bae45af414 contracts path change 2025-03-12 09:33:47 +05:00
Alex Ant aff3835f50 fix data paths 2025-03-12 09:26:43 +05:00
Alex Ant 5f249f6032 add lossed lib dir 2025-03-12 09:03:45 +05:00
Alex Ant a185d7e9e8 fix build 2025-03-11 23:34:27 +05:00
Alex Ant 3449caf0e2 fix build 2025-03-11 23:15:03 +05:00
Alex Ant cc458cc916 fix some files 2025-03-11 22:59:52 +05:00
Alex Ant 2b6b56ace9 move contracts inside and make common setup script 2025-03-11 22:57:08 +05:00
Alex Ant 4d72c920c6 capital cooptypes 2025-03-11 19:57:49 +05:00
Alex Ant b0186a1baa add Env-testnet.ts 2025-03-06 13:53:31 +05:00
Alex Ant bc3836b316 docker-compose for infra 2025-03-06 11:34:04 +05:00
Alex Ant 0a444efe55 sosediAgreement and docker-compose-infra.yaml 2025-03-06 11:22:43 +05:00
Alex Ant fba3988834 env controller 2025-03-05 21:55:40 +05:00
Alex Ant b99e1abb03 update package 2025-03-05 21:54:10 +05:00
Alex Ant ab44237bb9 update cooptypes 2025-03-05 17:33:48 +05:00
Alex Ant 6d98dc073f add capital contract 2025-03-04 20:23:36 +05:00
Alex Ant 9709ef4333 investment templates 2025-02-25 20:35:03 +05:00
Alex Ant cb60f6f735 update test structure 2025-02-22 15:43:00 +05:00
Alex Ant 5d0ab2e2db fix 2025-02-13 10:14:32 +05:00
Alex Ant 4e3b0b52c8 branch bug fix 2025-02-13 10:08:46 +05:00
Alex Ant 973590281c chore(release): publish 2025-02-10 14:58:11 +05:00
Alex Ant 4f7defb85c update 2025-02-10 14:56:44 +05:00
Alex Ant 59f4d8dc26 delete onewallet structure 2025-02-10 14:44:03 +05:00
Alex Ant be736e105a wallet fast fix 2025-02-10 13:39:31 +05:00
Alex Ant 3be56ba462 chore(release): publish 2025-02-07 19:35:06 +05:00
Alex Ant f7adf8457b Dockerfile fix 2025-02-07 19:25:01 +05:00
Alex Ant 80b8856f15 fix 2025-02-07 18:48:44 +05:00
Alex Ant 0860f47d88 capital sdk mutations 2025-02-07 18:39:39 +05:00
Alex Ant 382b75fccf cooplace 2025-01-30 19:25:35 +05:00
Alex Ant d3dacbd32d add production cooperative to the list 2025-01-28 23:08:02 +05:00
Alex Ant 6c51e4b596 fix selectBranch bug and add IP for entrepreneur user 2025-01-28 16:27:22 +05:00
Alex Ant d0bd4428be chore(release): publish 2025-01-27 11:08:26 +05:00
Alex Ant 43ffcb7362 update 2025-01-27 10:54:50 +05:00
Alex Ant ca5dfca290 update table style on documents 2025-01-27 10:29:08 +05:00
Alex Ant 8a495eff37 add new templates for documents and fix QR-pay interface 2025-01-27 10:07:52 +05:00
Alex Ant bee6af12fb get COOPBACK const from system.info everywhere 2025-01-23 18:52:33 +05:00
Alex Ant 4a3dfc3eb7 update 2025-01-22 17:49:33 +05:00
Alex Ant 4e87a4adc4 fix init process 2025-01-22 16:08:24 +05:00
Alex Ant d9bb9d5884 update system info 2025-01-22 12:23:34 +05:00
Alex Ant 562be0cb89 update 2025-01-21 17:51:03 +05:00
Alex Ant 2fdc86af97 chore(release): publish 2025-01-19 18:52:27 +05:00
Alex Ant 220d1225b2 update 2025-01-19 17:04:34 +05:00
Alex Ant e047a6e449 remove username input on getAccounts & remove deleteAccount mutation on SDK 2025-01-19 16:34:54 +05:00
Alex Ant 97b21a9d69 chore(release): publish 2025-01-18 16:15:43 +05:00
Alex Ant 9be9570dea document and blockchain classes on SDK, move sign documents on signup to use SDK 2025-01-18 16:11:32 +05:00
Alex Ant cbfe37dbba chore(release): publish 2025-01-17 13:52:58 +05:00
Alex Ant bf176a2c05 fix connection button only for voskhod coops 2025-01-17 13:50:36 +05:00
Alex Ant 173c51eb26 connection button for coops on desktop 2025-01-17 13:46:47 +05:00
Alex Ant 71dbb090fb memberButton for access to coop settings 2025-01-17 13:02:40 +05:00
Alex Ant 832133e6cc Canvas SDK class 2025-01-17 12:57:46 +05:00
Alex Ant 2730a92b01 update 2025-01-16 19:54:20 +05:00
Alex Ant 40fb5c80eb update 2025-01-16 19:45:35 +05:00
Alex Ant 6c65095961 add account class 2025-01-16 17:46:08 +05:00
Alex Ant d02f7ae7bc rollback 2025-01-16 12:40:17 +05:00
Alex Ant 9f0b4b24ba update SDK 2025-01-16 12:34:04 +05:00
Alex Ant 237357c1d5 canvas class and update types on dto 2025-01-16 12:30:57 +05:00
Alex Ant aefb1caf57 update 2025-01-16 12:17:00 +05:00
Alex Ant a3328e7ef0 move to sdk login 2025-01-16 12:09:04 +05:00
Alex Ant db24588273 fix paymentMethods orders 2025-01-15 13:47:58 +05:00
Alex Ant 2cabeeb6bd temp fix order id bug 2025-01-15 10:52:02 +05:00
Alex Ant 88352202b0 update 2025-01-14 21:50:45 +05:00
Alex Ant e02d8a949b update 2025-01-14 20:40:26 +05:00
Alex Ant e7e5457cf2 uppdate 2025-01-14 20:39:39 +05:00
Alex Ant 29b98b0f7d add login method to SDK 2025-01-14 20:32:49 +05:00
Alex Ant ae7378f474 update 2025-01-14 18:07:31 +05:00
Alex Ant b05c17bee4 chore(release): publish 2025-01-14 17:50:44 +05:00
Alex Ant a2d3dac226 chore(release): publish 2025-01-14 17:41:40 +05:00
Alex Ant a8ec41e255 fix 2025-01-14 13:48:53 +05:00
Alex Ant 1d16062961 renaming and sdk 2025-01-14 13:45:45 +05:00
Alex Ant 2a614e8656 move to sdk #1 2025-01-14 12:32:58 +05:00
Alex Ant baaaaf01bf sdk-client docs 2025-01-10 10:35:54 +05:00
Alex Ant 20f1fc51b1 hard payment migration 2025-01-01 21:07:59 +05:00
Alex Ant d749ebf981 add a lot of queries and mutations 2025-01-01 12:06:41 +05:00
Alex Ant 04364c503c chore(release): publish 2024-12-24 23:43:08 +05:00
Alex Ant ae2a040fe4 free decision 2024-12-24 22:45:50 +05:00
Alex Ant 0debf23b5e move to a documented ql-client 2024-12-23 13:45:49 +05:00
Alex Ant 55e7849b43 visual fixs 2024-12-21 18:37:31 +05:00
Alex Ant 34d9a73677 Merge branch 'dev' into testnet 2024-12-20 21:04:35 +05:00
Alex Ant 0b9ccf5bc0 update 2024-12-20 21:04:07 +05:00
Alex Ant 48e72bb9df update 2024-12-20 21:03:36 +05:00
Alex Ant 35aed44bcb update card and add selectors for generate free decision and selectBranch documents 2024-12-20 21:02:19 +05:00
Alex Ant e4cf693c0d Merge branch 'testnet' 2024-12-20 16:47:35 +05:00
Alex Ant f0755f5f90 update 2024-12-20 16:47:29 +05:00
Alex Ant bdfc6af688 selectors update coopjs 2024-12-20 16:26:26 +05:00
Alex Ant 31b4061a43 resolver of free decision 2024-12-20 14:44:20 +05:00
Alex Ant a1b80f0323 drafts migration complete 2024-12-18 13:36:52 +05:00
Alex Ant 1678686ce7 freeDecision and projectFreeDecision document templates 2024-12-18 13:29:10 +05:00
Alex Ant c25d05196d generate selectBranch document throw graphql 2024-12-16 20:16:44 +05:00
Alex Ant bdddc7ad03 selectBranch for added members 2024-12-14 21:09:13 +05:00
Alex Ant 1d1ad4f9fa branches 2024-12-11 21:01:22 +05:00
Alex Ant 099fcc7981 update! 2024-12-05 19:00:58 +05:00
Alex Ant 2ef44e45f6 from 2024-12-03 20:19:36 +05:00
Alex Ant ebe62d0498 migrate 2024-12-03 19:11:46 +05:00
Alex Ant 6472b20ea4 branches auto enable 2024-12-03 14:00:11 +05:00
Alex Ant 69cdb2d754 enable / disable branches 2024-12-02 16:11:05 +05:00
Alex Ant f7d8000b48 coopjs mutations and queries for branches 2024-11-29 21:10:54 +05:00
Alex Ant a44afbe1b9 make branches happen #1 2024-11-19 22:19:26 +05:00
Alex Ant 662da1b8a9 update 2024-11-15 16:24:39 +05:00
Alex Ant 2a3d743204 router refactoring 2024-11-14 20:30:39 +05:00
Alex Ant 7241ee4c7a pre-release 2024-11-14 20:08:56 +05:00
Alex Ant 5c084d6e8b add reflect-metadata 2024-11-10 20:03:16 +05:00
Alex Ant 1060c7fdfc package fix 2024-11-10 19:53:15 +05:00
Alex Ant b5d6c0e0b3 fix bug on parser with return function 2024-11-10 17:42:59 +05:00
Alex Ant d69644350e integration coopjs & terminal 2024-11-10 17:33:29 +05:00
Alex Ant 8634622a6a coopjs 0.01 2024-11-09 21:24:29 +05:00
Alex Ant cc73554350 graphql & apollo server & guards & documentator prototype 2024-11-07 21:06:17 +05:00
Alex Ant 8adf93d592 graphql implementation 2024-11-07 19:42:17 +05:00
Alex Ant 6bd60ddf96 extensions #3 2024-11-07 12:43:33 +05:00
Alex Ant ea5bcbe4dd extensions #2 2024-11-07 10:38:36 +05:00
Alex Ant 541b49069d nestjs & postgres migration #1 2024-11-06 10:54:38 +05:00
Alex Ant ba868866ec postgres initialization 2024-11-04 12:48:58 +05:00
Alex Ant 606ae48f13 update 2024-11-04 11:54:33 +05:00
Alex Ant 09c5445f83 nestjs but fix 2024-11-04 11:46:26 +05:00
Alex Ant 4a0a18a7b0 nestjs integration 2024-11-03 20:18:40 +05:00
Alex Ant ed9d1ab738 test 2024-11-03 14:25:08 +05:00
Alex Ant a61e8084dd fix 2024-11-03 14:13:39 +05:00
Alex Ant e169380178 make settings visible only for logged in 2024-11-03 14:09:15 +05:00
Alex Ant b07d4ef6c3 fix cooperative settings for everone 2024-11-03 13:49:39 +05:00
Alex Ant 4c713c7652 make common header layout and better settings menu 2024-11-03 13:22:24 +05:00
Alex Ant 90e8c6b3c4 chore(release): publish 2024-10-30 19:49:43 +05:00
Alex Ant 07bf05cb2c normal plugin model with some payment plugins 2024-10-30 19:42:48 +05:00
Alex Ant 49d29902a8 update 2024-10-29 23:42:49 +05:00
Alex Ant 32a90c2a9e controller-next initialization 2024-10-29 23:42:14 +05:00
Alex Ant 16ed2c1517 support 2024-10-29 11:50:49 +05:00
Alex Ant 8bb0f1c799 chore(release): publish 2024-10-28 21:49:21 +05:00
Alex Ant f14fe2dd64 update cardcoop 2024-10-28 20:45:08 +05:00
Alex Ant 884f0ee0de changeAuthorizedHome 2024-10-28 19:47:55 +05:00
Alex Ant 744270f48c update 2024-10-28 19:42:04 +05:00
Alex Ant c30849be3c cardcoop on a main screen 2024-10-28 19:06:15 +05:00
Alex Ant f295ac8b4d separated settings page 2024-10-28 17:56:40 +05:00
Alex Ant 9469a8f43d update 2024-10-28 14:18:07 +05:00
Alex Ant 786799ca40 update 2024-10-28 14:16:53 +05:00
Alex Ant aa6238cf5a fix 2024-10-28 13:38:54 +05:00
Alex Ant 1ed31ee2e2 update 2024-10-28 13:26:42 +05:00
Alex Ant 7d8c12977c generateOptions for Documents 2024-10-28 13:15:07 +05:00
Alex Ant 81ee1d63a6 Merge branch 'main' into dev 2024-10-28 11:59:00 +05:00
Alex Ant cf1c1b3198 chore(release): publish 2024-10-28 11:55:43 +05:00
Alex Ant bc7b1e8472 update 2024-10-28 11:55:02 +05:00
Alex Ant 7319c41cc1 chore(release): publish 2024-10-28 11:51:09 +05:00
Alex Ant 2f09fc32c0 finish bugfix 2024-10-28 11:50:41 +05:00
Alex Ant f85b3f54a1 chore(release): publish 2024-10-28 10:49:06 +05:00
Alex Ant 1a68669d6d update Dockerfile 2024-10-28 10:48:04 +05:00
Alex Ant 6345c2d414 chore(release): publish 2024-10-28 10:16:08 +05:00
Alex Ant 87930cad75 update 2024-10-28 10:14:41 +05:00
Alex Ant 3ab2232121 Dockerfile 2024-10-28 10:06:40 +05:00
Alex Ant 9719e25421 move to weasyprint 2024-10-28 10:03:29 +05:00
Alex Ant 8e1f998c39 try to fix it ! 2024-10-27 20:34:08 +05:00
Alex Ant deb1c224f8 update 2024-10-27 19:33:06 +05:00
Alex Ant b2fa79898a arial embded font 2024-10-27 17:56:49 +05:00
Alex Ant da2759a3c6 update 2024-10-27 15:29:12 +05:00
Alex Ant f6bc696426 add common Arial font 2024-10-27 15:09:17 +05:00
Alex Ant da653e9b32 looking for bug #2 2024-10-27 12:46:47 +05:00
Alex Ant 227e153bde looking for bug 2024-10-27 12:03:18 +05:00
Alex Ant 12f906b9b0 update 2024-10-26 21:09:25 +05:00
Alex Ant c8236d4d10 update 2024-10-26 21:01:26 +05:00
Alex Ant e2006da50d TSK-289 Устранить избыточное сохранение документа при регенерации (1) 2024-10-26 20:20:05 +05:00
Alex Ant 433a7d3641 chore(release): publish 2024-10-26 17:45:45 +05:00
Alex Ant 6b41c74b43 regenerate document button, some bugs and fixs 2024-10-26 17:38:12 +05:00
Alex Ant cc3e7ff7c6 fix bug auto powerup on every restart 2024-10-26 13:42:11 +05:00
Alex Ant a35a6bf642 separate init payment factory 2024-10-26 13:10:52 +05:00
Alex Ant 9ecd523646 fix double subscriber bug 2024-10-26 12:52:24 +05:00
Alex Ant e7f896c38e update notification template 2024-10-26 10:49:28 +05:00
Alex Ant 624ec743b1 fix bug loading button 2024-10-25 19:26:41 +05:00
Alex Ant 553c5745ef fix update soviet bug 2024-10-25 16:46:26 +05:00
Alex Ant 3f2779ca3d fix bug voskhod everywhere 2024-10-25 16:42:18 +05:00
Alex Ant e51b962627 databazor bug fix 2024-10-25 16:20:58 +05:00
Alex Ant f709e135b6 Merge branch 'testnet' 2024-10-25 11:10:33 +05:00
Alex Ant 50b48aa247 update 2024-10-24 11:44:05 +05:00
Alex Ant 508f2abc82 update 2024-10-24 11:37:39 +05:00
Alex Ant 42ec56e578 fix 2024-10-24 11:26:07 +05:00
Alex Ant 92673dac6e chore(release): publish 2024-10-23 21:21:41 +05:00
Alex Ant daf57aba35 fix 2024-10-23 21:21:00 +05:00
Alex Ant dee629c429 chore(release): publish 2024-10-23 21:16:50 +05:00
Alex Ant 67f9092350 fix lib 2024-10-23 21:15:52 +05:00
Alex Ant 88691d47a5 chore(release): publish 2024-10-23 21:02:47 +05:00
Alex Ant 0fdd9bc803 update 2024-10-23 21:02:25 +05:00
Alex Ant 8452c607b4 chore(release): publish 2024-10-23 20:57:27 +05:00
Alex Ant 4fc57c2d57 update 2024-10-23 20:56:58 +05:00
Alex Ant 9f0b707a30 chore(release): publish 2024-10-23 20:51:30 +05:00
Alex Ant 511bc351d5 publish 2024-10-23 20:49:38 +05:00
Alex Ant 56ee3e962c chore(release): publish 2024-10-23 20:42:13 +05:00
Alex Ant 2781e8519d fix 2024-10-23 20:40:34 +05:00
Alex Ant 46575348fe chore(release): publish 2024-10-23 20:24:23 +05:00
Alex Ant bae2ca646b publish again 2024-10-23 20:24:00 +05:00
Alex Ant 0686c6ab00 chore(release): publish 2024-10-23 20:20:51 +05:00
Alex Ant db04b66ce5 update 2024-10-23 20:20:25 +05:00
Alex Ant c43c8a29af chore(release): publish 2024-10-23 20:17:40 +05:00
Alex Ant aaea18abe1 publish 2024-10-23 20:16:35 +05:00
Alex Ant 0b6076cb61 chore(release): publish 2024-10-23 20:10:05 +05:00
Alex Ant 3b6d02f02c fix 2024-10-23 20:08:45 +05:00
Alex Ant 625a0586a3 public 2024-10-23 20:06:22 +05:00
Alex Ant 10d63ca586 powerup is private plugin 2024-10-23 20:06:01 +05:00
Alex Ant d2697bf9de make library from terminal 2024-10-23 20:04:34 +05:00
Alex Ant fa8c8843e2 make powerup private plugin 2024-10-19 22:07:20 +05:00
Alex Ant 7b59ea4b96 chore(release): publish 2024-10-19 22:03:06 +05:00
Alex Ant 058215934e Merge branch 'testnet' 2024-10-19 22:02:08 +05:00
Alex Ant 1d724d841f plugin factory and powerup plugin 2024-10-19 21:58:34 +05:00
Alex Ant 32db2100de implementation plugins worker system 2024-10-18 11:27:39 +05:00
Alex Ant 095c77b65b chore(release): publish 2024-10-17 16:20:02 +05:00
Alex Ant c1c07bff8f plugin factory and powerup plugin sketch 2024-10-17 16:18:39 +05:00
Alex Ant 1206bd0e98 fix voskhod everywhere bug 2024-10-17 13:22:35 +05:00
Alex Ant 67bcd1b307 update text on install 2024-10-17 10:52:06 +05:00
Alex Ant 31ef70e433 health check 2024-10-16 13:56:37 +05:00
Alex Ant afb12961d2 fix launch errors 2024-10-16 13:38:08 +05:00
Alex Ant 3dbd40e334 bug fix soviet update 2024-10-16 11:10:18 +05:00
Alex Ant bc5d6e38c8 console.error(e 2024-10-15 23:37:27 +05:00
Alex Ant 5a39143051 openReplay connection 2024-10-14 21:24:45 +05:00
Alex Ant 18e9571877 fix css bug 2024-10-13 16:52:55 +05:00
Alex Ant a828f4c831 chore(release): publish 2024-10-13 16:26:37 +05:00
Alex Ant 54ac12f250 update migrations 2024-10-13 16:24:26 +05:00
Alex Ant 9a6a72f605 feat: запрос соглашений
fix: интерфейс
2024-10-13 16:13:52 +05:00
Alex Ant 03949489e8 docs: добавлен файл конвенции коммитов 2024-10-13 13:19:16 +05:00
Alex Ant 8ebd735e7e update 2024-10-13 13:08:59 +05:00
Alex Ant 39246da73f update amount in agreement and fix bug in terminal 2024-10-13 12:00:39 +05:00
Alex Ant bf33f60368 cooperative connection 2024-10-13 11:50:22 +05:00
Alex Ant 492a613769 fix bugs 2024-10-12 00:59:58 +05:00
Alex Ant 2c02c3434e update 2024-10-12 00:51:37 +05:00
Alex Ant a186ddd9d2 chore(release): publish 2024-10-12 00:46:06 +05:00
Alex Ant 4b0c7a5e4e update 2024-10-12 00:45:35 +05:00
Alex Ant 54cfbe3ab6 fix 2024-10-12 00:39:07 +05:00
Alex Ant 02015cf0b8 update migrator package.json 2024-10-12 00:37:39 +05:00
Alex Ant 416e4d3598 interface rework & documents style change 2024-10-12 00:31:52 +05:00
Alex Ant e0d9c4b0ea make migrator private 2024-10-10 20:55:46 +05:00
Alex Ant 2659e3bc09 migrator release 2024-10-10 20:54:27 +05:00
Alex Ant d296167b6a agreementChecker fix, payment description fix, loader on add user 2024-10-10 20:41:18 +05:00
Alex Ant 89b0aa7fec chore(release): publish 2024-10-10 18:38:19 +05:00
Alex Ant 787aadf444 fix bugs 2024-10-10 18:09:22 +05:00
Alex Ant d67161a29b slice secret 2024-10-10 15:02:12 +05:00
Alex Ant 7f4777ead4 правка текста вступительного взноса 2024-10-10 14:41:26 +05:00
Alex Ant 1e2147b143 fix aggregation 2 2024-10-10 12:09:10 +05:00
Alex Ant 9f0a2fbf63 chore(release): publish 2024-10-10 11:28:11 +05:00
Alex Ant 7804851bc9 update agregate pipeline 2024-10-10 11:15:47 +05:00
Alex Ant adc50dd89e chore(release): publish 2024-10-09 22:20:55 +05:00
Alex Ant 5378cf5ddc template fix 2024-10-09 22:14:11 +05:00
Alex Ant a733bb605f fix model 2024-10-09 14:17:19 +05:00
Alex Ant aaa1b5a264 test update 2024-10-09 14:01:21 +05:00
Alex Ant 5855cdee65 install process 2024-10-09 13:46:08 +05:00
Alex Ant c0d584e644 TSK-225 vaultModel and wif store for cooperative operations 2024-10-04 12:01:44 +05:00
Alex Ant 955f0f473e fix bug 2024-09-30 23:10:59 +05:00
Alex Ant 9ceb888124 update 2024-09-30 22:13:59 +05:00
Alex Ant e902933693 update 2024-09-30 21:43:41 +05:00
Alex Ant e07b47fb10 update 2024-09-30 21:37:54 +05:00
Alex Ant 5bf08d9701 update 2024-09-30 21:33:02 +05:00
Alex Ant 597fa1e6fa update package 2024-09-30 21:21:45 +05:00
Alex Ant dda3ec0ba0 chore(release): publish 2024-09-30 21:09:57 +05:00
Alex Ant 991f08c749 dompurify and coopback <br> fix 2024-09-30 21:08:26 +05:00
Alex Ant 923865cd7c bug fix #3 2024-09-30 20:00:25 +05:00
Alex Ant 606aee8ccd chore(release): publish 2024-09-30 17:24:04 +05:00
Alex Ant 4a1cb27d0a big bug #2 2024-09-30 17:21:38 +05:00
Alex Ant 26cd7f3a12 chore(release): publish 2024-09-30 17:09:39 +05:00
Alex Ant f4045dbc56 chore(release): publish 2024-09-30 17:07:54 +05:00
Alex Ant 33c2b24550 fix bug of parallel generation 2024-09-30 17:06:17 +05:00
Alex Ant cd86f3c8ea redis auth 2024-09-30 15:15:57 +05:00
Alex Ant 58026ebc2d fix redis auth 2024-09-30 15:07:33 +05:00
Alex Ant 9183275dad redis authentification 2024-09-30 15:00:38 +05:00
Alex Ant 5e3a625d95 chore(release): publish 2024-09-30 14:52:28 +05:00
Alex Ant 3297f9d2d2 update package.json 2024-09-30 14:51:18 +05:00
Alex Ant 29b8306a28 chore(release): publish 2024-09-29 19:17:36 +05:00
Alex Ant d84746bac6 add compression package 2024-09-29 19:14:12 +05:00
Alex Ant 4eacf2ff0b chore(release): publish 2024-09-29 19:01:29 +05:00
Alex Ant a68e0d6f61 update 2024-09-29 18:57:09 +05:00
Alex Ant ddf44304c7 order_id -> order_num, stable initial_order 2024-09-29 15:28:16 +05:00
Alex Ant 789d98f58e chore(release): publish 2024-09-29 09:39:57 +05:00
Alex Ant 205574620d Merge branch 'testnet' 2024-09-29 09:39:19 +05:00
Alex Ant d9bae12b31 chore(release): publish 2024-09-29 09:36:26 +05:00
Alex Ant c951190794 update 2024-09-29 09:34:32 +05:00
Alex Ant d4e1d69c3c update 2024-09-29 09:29:53 +05:00
Alex Ant f93a32c07b update 2024-09-29 09:25:10 +05:00
Alex Ant 8e7c152c0d update 2024-09-29 09:23:20 +05:00
Alex Ant 6a64404f20 update 2024-09-29 09:19:43 +05:00
Alex Ant 1a09589add from-packages 2024-09-29 09:18:03 +05:00
Alex Ant d0f9072ab5 fixed packet version 2024-09-29 09:15:58 +05:00
Alex Ant 011ed9f2d8 update 2024-09-29 09:11:57 +05:00
Alex Ant 12332f83d9 Merge branch 'dev' into testnet 2024-09-29 09:05:09 +05:00
Alex Ant 8f75829948 update 2024-09-29 09:04:53 +05:00
Alex Ant a7ffedb3b2 update 2024-09-29 09:03:14 +05:00
Alex Ant 378d3b9747 update 2024-09-29 09:02:06 +05:00
Alex Ant 5ba05409c4 chore(release): publish
- coopback@1.7.38-alpha.3
 - coopdoc-generator-ts@1.0.21-alpha.3
 - cooptypes@1.0.26-alpha.3
 - coop-notificator@0.0.29-alpha.3
 - terminal@0.1.51-alpha.3
2024-09-29 08:58:35 +05:00
Alex Ant d86beef58f document updates 2024-09-29 08:57:37 +05:00
Alex Ant 7315d67c9f chore(release): publish
- coopback@1.7.38-alpha.2
 - coopdoc-generator-ts@1.0.21-alpha.2
 - cooptypes@1.0.26-alpha.2
 - coop-notificator@0.0.29-alpha.2
 - terminal@0.1.51-alpha.2
2024-09-28 19:43:01 +05:00
Alex Ant 59d79b8c7a update order statuses, list order statuses, add passport data, update registry documents 2024-09-28 19:41:12 +05:00
Alex Ant 3df97653a2 TSK-181 StatusPage and setStatus for orders [pre] 2024-09-26 21:30:08 +05:00
Alex Ant 948d55e418 download qr and show order data 2024-09-23 21:18:10 +05:00
Alex Ant 1d2c7281b1 sberbankPollingProvider prototype 2024-09-23 12:46:12 +05:00
Alex Ant 35759b943c chore(release): publish
- coopback@1.7.38-alpha.1
 - coopdoc-generator-ts@1.0.21-alpha.1
 - cooptypes@1.0.26-alpha.1
 - coop-notificator@0.0.29-alpha.1
 - terminal@0.1.51-alpha.1
2024-09-20 12:13:47 +05:00
Alex Ant 29da33e200 update 2024-09-20 12:12:06 +05:00
Alex Ant 1d758f8126 gitignore 2024-09-20 10:09:54 +05:00
Alex Ant b663dac2ab delete logs 2024-09-20 10:09:32 +05:00
Alex Ant 485a3971e9 update 2024-09-20 10:09:13 +05:00
Alex Ant 36b5d64ca6 chore(release): publish
- coopback@1.7.38-alpha.0
 - coopdoc-generator-ts@1.0.21-alpha.0
 - cooptypes@1.0.26-alpha.0
 - coop-notificator@0.0.29-alpha.0
 - terminal@0.1.51-alpha.0
2024-09-16 12:14:29 +05:00
Alex Ant 8462a709bc hz 2024-09-16 12:13:44 +05:00
Alex Ant 20cabe8362 update 2024-09-16 12:00:54 +05:00
Alex Ant 6967864361 chore(release): publish
- coopback@1.7.37-alpha.3
 - coopdoc-generator-ts@1.0.11-alpha.2
 - terminal@0.1.41-alpha.2
2024-09-16 11:58:50 +05:00
Alex Ant a6ee3840ae update 2024-09-16 11:57:43 +05:00
Alex Ant 01de6139df update 2024-09-16 11:46:48 +05:00
Alex Ant 6ba0924f6c chore(release): publish
- coopback@1.7.37-alpha.2
 - coopdoc-generator-ts@1.0.11-alpha.2
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.28-alpha.1
 - terminal@0.1.41-alpha.1
2024-09-16 11:44:22 +05:00
Alex Ant 00c75d1fd8 update 2024-09-16 11:43:37 +05:00
Alex Ant 93f28fa9d5 update 2024-09-16 11:38:25 +05:00
Alex Ant 0aed10eb8b chore(release): publish
- coopback@1.7.37-alpha.1
 - coopdoc-generator-ts@1.0.11-alpha.1
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.28-alpha.0
 - terminal@0.1.41-alpha.0
2024-09-16 11:37:13 +05:00
Alex Ant d84d687eee update 2024-09-16 11:36:50 +05:00
Alex Ant d046f56832 update 2024-09-16 11:34:07 +05:00
Alex Ant 0af1fbf313 update 2024-09-16 11:24:50 +05:00
Alex Ant e9aaf4309e chore(release): publish
- cooparser-ts@1.0.20-alpha.0
 - coopback@1.7.37-alpha.0
 - coopdoc-generator-ts@1.0.11-alpha.0
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.27
 - terminal@0.1.40
2024-09-16 11:19:45 +05:00
Alex Ant 80fa1085d2 FUCK 2024-09-16 11:13:22 +05:00
Alex Ant 88195291fd update 2024-09-16 11:09:40 +05:00
Alex Ant 39ad600d00 update 2024-09-16 10:58:59 +05:00
Alex Ant c63a3aba6b update 2024-09-16 10:41:15 +05:00
Alex Ant 782ecbe307 dfdf 2024-09-16 10:36:54 +05:00
Alex Ant f75bbfa048 fix 2024-09-16 10:15:16 +05:00
Alex Ant 3a772cc983 herna 2024-09-16 10:00:29 +05:00
Alex Ant 807113523e documents update 2024-09-16 09:52:00 +05:00
Alex Ant 7df2af8611 update 2024-09-15 17:28:33 +05:00
Alex Ant 10c1697b00 update 2024-09-15 17:23:58 +05:00
Alex Ant 742e5d38ab chore(release): publish
- coopback@1.7.35-alpha.5
 - coopdoc-generator-ts@1.0.79-alpha.5
 - cooptypes@1.0.22-alpha.5
 - coop-notificator@0.0.26-alpha.5
 - terminal@0.1.39-alpha.5
2024-09-15 17:20:44 +05:00
Alex Ant 1d3e7f8af0 update 2024-09-15 17:19:13 +05:00
Alex Ant c99bacab64 before update 2024-09-15 10:40:18 +05:00
Alex Ant 95d7555ebc ssr & covars -> vars 2024-09-15 09:38:02 +05:00
Alex Ant 5854e0a23c chore(release): publish
- coopback@1.7.35-alpha.4
 - coopdoc-generator-ts@1.0.79-alpha.4
 - cooptypes@1.0.22-alpha.4
 - coop-notificator@0.0.26-alpha.4
 - terminal@0.1.39-alpha.4
2024-09-14 17:02:55 +05:00
Alex Ant 81f7f32767 fix 2024-09-14 17:02:33 +05:00
Alex Ant c0374a7da1 chore(release): publish
- coopback@1.7.35-alpha.3
 - coopdoc-generator-ts@1.0.79-alpha.3
 - cooptypes@1.0.22-alpha.3
 - coop-notificator@0.0.26-alpha.3
 - terminal@0.1.39-alpha.3
2024-09-14 16:44:32 +05:00
Alex Ant ed65e655b9 update 2024-09-14 16:43:06 +05:00
Alex Ant d250e516b5 update document models 2024-09-14 12:43:06 +05:00
Alex Ant e4f404b2c7 documents update 2024-09-13 20:41:00 +05:00
Alex Ant 86571331f8 update registry 2024-09-12 15:05:11 +05:00
Alex Ant 163d420af9 request agreements 2024-09-11 16:12:05 +05:00
Alex Ant 4925ea4af0 chore(release): publish
- cooparser-ts@1.0.19-alpha.1
 - coopback@1.7.35-alpha.2
 - coopdoc-generator-ts@1.0.79-alpha.2
 - cooptypes@1.0.22-alpha.2
 - coop-notificator@0.0.26-alpha.2
 - terminal@0.1.39-alpha.2
2024-09-11 10:04:46 +05:00
Alex Ant 57b05ce348 registration agreements 2024-09-11 10:01:09 +05:00
Alex Ant 427b53fcbd chore(release): publish
- coopback@1.7.35-alpha.1
 - coopdoc-generator-ts@1.0.79-alpha.1
 - cooptypes@1.0.22-alpha.1
 - coop-notificator@0.0.26-alpha.1
 - terminal@0.1.39-alpha.1
2024-09-10 10:06:12 +05:00
Alex Ant 2ac7a31be6 промежуточный результат добавления кооперативных соглашений 2024-09-10 10:05:17 +05:00
Alex Ant 4baed6409f chore(release): publish
- cooparser-ts@1.0.19-alpha.0
 - coopback@1.7.35-alpha.0
 - coopdoc-generator-ts@1.0.79-alpha.0
 - cooptypes@1.0.22-alpha.0
 - coop-notificator@0.0.26-alpha.0
 - terminal@0.1.39-alpha.0
2024-09-06 16:39:34 +05:00
Alex Ant 2ea0ff0965 big update 2024-09-06 16:29:18 +05:00
Alex Ant 88b9a7edce small letters 2024-09-05 19:27:23 +05:00
Alex Ant 538066b960 ufff template or Template ??? always should be small letters 2024-09-05 19:25:37 +05:00
Alex Ant b10dd1d75a update generator with new type of document 2024-09-04 20:57:59 +05:00
Alex Ant f743580b55 fix signature collection name 2024-09-03 20:33:30 +05:00
Alex Ant 0688467302 update 2024-09-03 20:11:17 +05:00
Alex Ant 29e972dbf2 fix is_paid 2024-08-27 18:47:39 +05:00
Alex Ant bd99697c5b chore(release): publish
- coopback@1.7.34
 - coopdoc-generator-ts@1.0.78
 - cooptypes@1.0.21
 - coop-notificator@0.0.25
 - terminal@0.1.38
2024-08-25 11:39:35 +05:00
Alex Ant 1d5c431ff6 update types 2024-08-25 11:38:40 +05:00
Alex Ant 3736c510ff chore(release): publish
- cooparser-ts@1.0.18
 - coopback@1.7.33
 - coopdoc-generator-ts@1.0.77
 - terminal@0.1.37
2024-08-25 11:27:44 +05:00
Alex Ant 26c12c3a85 update 2024-08-25 11:25:28 +05:00
Alex Ant 10bb08e5e6 chore(release): publish
- coopback@1.7.32
 - coopdoc-generator-ts@1.0.76
 - terminal@0.1.36
2024-08-25 11:19:13 +05:00
Alex Ant b50d5b6121 publish 2024-08-25 11:07:57 +05:00
Alex Ant 38b28752e1 chore(release): publish
- coopback@1.7.31
 - coopdoc-generator-ts@1.0.75
 - cooptypes@1.0.20
 - coop-notificator@0.0.24
 - terminal@0.1.35
2024-08-25 11:02:44 +05:00
Alex Ant 87c51009de ProgramProvision | ЦПП Цифровой Кошелёк 2024-08-25 10:59:57 +05:00
6784 changed files with 794163 additions and 109561 deletions
Vendored
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
{
"agentCanUpdateSnapshot": true
}
+8
View File
@@ -0,0 +1,8 @@
---
description:
globs:
alwaysApply: true
---
ВАЖНО!!! Мы работаем в МОНО-репозитории pnpm, любая установка пакетов производится ЧЕРЕЗ ФИЛЬТР компонента в корне: pnpm add glob --filter notificator2.
ВАЖНО!!! НИКОГДА НЕ ЗАПУСКАЙ ПРИЛОЖЕНИЕ ИЛИ ЕГО СБОРКУ!!! Просто отчитайся что всё сделал.
+151
View File
@@ -0,0 +1,151 @@
---
description:
globs: components/controller/**
alwaysApply: false
---
# NestJS Controller и Чистая архитектура
## Основные принципы
1. Чистая архитектура - главный архитектурный подход. Пиши код с комментариями.
2. Типы IName, IChecksum256, ITimePointSec - это просто строки.
3. Домен должен быть полностью изолирован от инфраструктурных деталей.
4. Направление зависимостей - внутрь (к ядру, домену).
5. Домен, инфраструктура и приложение связаны через App.module, НЕ НУЖНО импортировать их друг в друга, а достаточно просто импортировать домен на уровне приложения чтобы использовать все экспорты домена.
## Структура проекта
- `domain/` - доменный слой (бизнес-логика, независимая от инфраструктуры)
- `infrastructure/` - инфраструктурный слой (адаптеры к внешним системам)
- `modules/` - слой приложения (DTO, резолверы, сервисы)
## Слои и их взаимодействие
1. **Домен**:
- Содержит чистые доменные интерфейсы и реализованные на них сущности
- НЕ должен зависеть от `cooptypes` (инфраструктурный контракт)
- Использует собственные доменные типы (например, Date вместо строковых timestamp)
- Интерфейсы портов описывают взаимодействие с внешними системами
2. **Инфраструктура**:
- Содержит адаптеры к внешним системам (блокчейн, БД и т.д.)
- Осуществляет преобразование между доменными и инфраструктурными типами
- Общая логика преобразования выносится в утилитарные классы (например, `DomainToBlockchainUtils`)
3. **Модули (приложение)**:
- DTO имплементируют доменные интерфейсы напрямую
- Резолвер вызывает сервис, который принимает DTO
- Сервис передает объекты DTO в интерактор домена
## Поток данных
1. Резолвер принимает входные данные и передает их в сервис
2. Сервис вызывает интерактор домена, передавая ему объекты DTO (имплементирующие доменный интерфейс)
3. Интерактор выполняет бизнес-логику и взаимодействует с портами домена
4. Адаптеры (инфраструктурный слой) преобразуют доменные объекты в формат внешних систем (JSON для хранения мета-данных, ISO для дат и т.д.)
5. Адаптеры возвращают результаты в домен, домен возвращает данные в сервис, который возвращает их в резолвер
## Правила преобразования данных
1. Преобразование DTO → доменный объект: не требуется, если DTO имплементирует доменный интерфейс
2. Преобразование доменный объект → инфраструктурный тип: происходит в адаптерах
3. Преобразование документов, дат и других сложных объектов: используются утилитарные классы уровня инфраструктуры
## Работа с типами данных
1. **Даты**:
- В доменных интерфейсах используется тип `Date`
- В DTO используется тип `Date` с декораторами `@IsDate()` и `@Type(() => Date)`
- В инфраструктурном слое происходит преобразование `Date` → `string` (ISO формат)
```typescript
// Доменный интерфейс
interface MeetDomainInterface {
open_at: Date;
}
// DTO
@Field(() => Date)
@IsDate()
@Type(() => Date)
open_at!: Date;
// Адаптер
const blockchainData = {
open_at: domainToBlockchainUtils.convertDateToBlockchainFormat(data.open_at)
};
```
2. **Документы**:
- В доменных интерфейсах используется собственный тип `SignedDocumentDomainInterface<T>`
- В DTO используется соответствующий DTO-класс (например, `SignedDigitalDocumentInputDTO`)
- В инфраструктурном слое происходит преобразование между форматами (meta-поля в JSON и т.д.)
## Типовые ошибки
1. **Нарушение изоляции домена**: домен должен быть изолирован от инфраструктуры. Не используйте импорты из `cooptypes` в доменных интерфейсах.
```typescript
// Неправильно
import { MeetContract } from 'cooptypes';
export type VoteDomainInterface = MeetContract.Actions.Vote.IInput;
// Правильно
export interface VoteDomainInterface {
coopname: string;
hash: string;
// ... доменные поля
}
```
2. **Преобразование в неправильном слое**: преобразование доменных объектов в инфраструктурные типы должно происходить в адаптерах, а не в доменном слое или сервисе.
```typescript
// Неправильно (в доменном интеракторе)
async vote(data: VoteDomainInterface) {
const blockchainData = { ...data, meta: JSON.stringify(data.meta) };
// ...
}
// Правильно (в адаптере)
async vote(data: VoteDomainInterface) {
const blockchainData = this.convertToBlockchainFormat(data);
// ...
}
```
3. **Дублирование логики преобразования**: общая логика преобразования должна быть вынесена в утилитарные классы.
```typescript
// Неправильно (дублирование в разных адаптерах)
// В MeetAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// В BranchAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// Правильно (общий утилитарный класс)
// DomainToBlockchainUtils
convertSignedDocumentToBlockchainFormat(doc) {
return { ...doc, meta: JSON.stringify(doc.meta) };
}
```
## Примеры
```typescript
// Доменный интерфейс - чистый, без зависимостей от инфраструктуры
export interface VoteOnAnnualGeneralMeetInputDomainInterface {
coopname: string;
hash: string;
member: string;
ballot: VoteItemInputDomainInterface[];
}
// DTO имплементирует доменный интерфейс напрямую
@InputType('VoteOnAnnualGeneralMeetInput')
export class VoteOnAnnualGeneralMeetInputDTO implements VoteOnAnnualGeneralMeetInputDomainInterface {
@Field(() => String)
@IsString()
coopname!: string;
// ... остальные поля
}
// Адаптер преобразует доменный объект в инфраструктурный тип
async vote(data: VoteOnAnnualGeneralMeetInputDomainInterface): Promise<TransactionResult> {
// Преобразуем доменный объект в инфраструктурный тип
const blockchainData: MeetContract.Actions.Vote.IInput = {
coopname: data.coopname,
hash: data.hash,
// ... преобразование других полей
};
// ... отправка данных во внешнюю систему
}
```
@@ -0,0 +1,41 @@
---
description: SDK мутации и запросы для подключения на рабочем столе
globs:
alwaysApply: false
---
Бэкенд реализован здесь components/controller, sdk здесь components/sdk.
Пример создания Query к API через SDK:
``` ts
async function loadBranches(data: IGetBranchesInput): Promise<IBranch[]> {
const { [Queries.Branches.GetBranches.name]: output } = await client.Query(
Queries.Branches.GetBranches.query,
{
variables: {
data
}
}
);
return output;
}
```
Такие запросы обычно хранятся в папке Entities и вызываются из его store.
Пример мутации:
``` ts
async function selectBranch(data: ISelectBranchInput): Promise<boolean>{
const {[Mutations.Branches.SelectBranch.name]: result} = await client.Mutation(Mutations.Branches.SelectBranch.mutation, {variables: {
data
}})
return result
}
```
Мутации обычно храним в папке features.
Как можешь обратить внимание, все мутации и запросы строятся по одному шаблону. Вся необходимая информация уже есть в SDK.
+12
View File
@@ -0,0 +1,12 @@
---
alwaysApply: true
globs: **/desktop/**
---
# DESKTOP
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
- Стиль шаблонов PUG на composite API.
- Всегда создаём index.ts файлы для vue компонент.
- Создавай индексные файлы для vue через export {default as NAME} from './where', а на уровне выше, если это необходимо, делай export * from './ui'
- Всегда удаляем неиспользуемые импорты.
- Не используем emit избыточно. Вместо них используем композабл функции моделей фич, если необходимо. Emit только в крайнем случае.
+62
View File
@@ -0,0 +1,62 @@
---
description:
globs:
alwaysApply: false
---
Файл [main.py](mdc:monocoop/monocoop/monocoop/components/docs/main.py) автоматически генерит ссылки на документацию SDK и GraphQL, которые формируются и публикуются автоматически. При создании документации к методам всегда применяй ссылки на SDK и GraphQL по форме:
{{ get_sdk_doc("Mutations", "Accounts", "RegisterAccount") }} | {{ get_graphql_doc("Mutation.registerAccount") }}
### 🎯 Основные паттерны использования
**1. Стандартная структура для методов API:**
```markdown
## Название действия
{{ get_sdk_doc("Namespace", "Module", "Method") }} | {{ get_graphql_doc("Type.methodName") }}
{{ get_typedoc_input("Namespace.Module.Method") }}
Результат:
{{ get_typedoc_definition("Namespace.Module.Method", "IOutput") }}
```
**2. Описание + пример (для сложных методов):**
```markdown
{{ get_typedoc_desc("Namespace.Module.Method") }}
{{ get_typedoc_input("Namespace.Module.Method") }}
```
**3. Ссылки на типы данных:**
```markdown
У каждого аккаунта есть объект {{ get_graphql_definition("Account") }} в GraphQL-API
```
### 🔧 Макросы и их применение
| Макрос | Назначение | Пример использования |
|--------|------------|---------------------|
| `get_sdk_doc` | Ссылка на SDK документацию | `{{ get_sdk_doc("Mutations", "Payments", "CreateDeposit") }}` |
| `get_graphql_doc` | Ссылка на GraphQL операцию | `{{ get_graphql_doc("Query.getAccount") }}` |
| `get_graphql_definition` | Ссылка на GraphQL тип | `{{ get_graphql_definition("Account") }}` |
| `get_class_doc` | Ссылка на класс/метод SDK | `{{ get_class_doc("Document", "sign") }}` |
| `get_typedoc_input` | **Полный пример** TypeScript вызова | `{{ get_typedoc_input("Mutations.Auth.Login") }}` |
| `get_typedoc_definition` | **Структура интерфейса** | `{{ get_typedoc_definition("Mutations.Auth.Login", "IOutput") }}` |
| `get_typedoc_desc` | Описание + примеры | `{{ get_typedoc_desc("Mutations.Auth.Login") }}` |
| `get_typedoc_value` | Значение константы | `{{ get_typedoc_value("Constants.API_VERSION") }}` |
### 📝 Правила оформления
**ОБЯЗАТЕЛЬНО:**
- Всегда используй `get_typedoc_input` для демонстрации **КАК вызывать** метод
- Всегда используй `get_typedoc_definition` для показа **ЧТО возвращается**
- Комбинируй SDK и GraphQL ссылки через ` | `: `{{ get_sdk_doc(...) }} | {{ get_graphql_doc(...) }}`
**ЖЕЛАТЕЛЬНО:**
- Для сложных методов добавляй `get_typedoc_desc` в начало
- Используй `get_graphql_definition` для ссылок на типы данных в тексте
- Группируй связанные операции в одном разделе
**ФОРМАТ ССЫЛОК:**
- SDK: `"Namespace", "Module", "Method"` (3 аргумента)
- GraphQL: `"Type.methodName"` (точка между типом и методом)
- Определения: просто имя типа `"TypeName"`
+96
View File
@@ -0,0 +1,96 @@
---
description:
globs:
alwaysApply: false
---
# Структура файлов шаблонов документов и алгоритм правки
## Основные директории и файлы
### 1. Определение типов и интерфейсов (cooptypes)
**Путь:** `/cooptypes/src/cooperative/registry/[ID.DocumentName]/index.ts`
В этих файлах определяются:
- Интерфейсы данных (`IAgendaMeet`, etc.)
- Основная модель данных документа (`Model`)
- HTML шаблон документа (`context`)
- Переводы строк (`translations`)
- Пример данных для тестирования (`exampleData`)
**Для изменения шаблона нужно править:**
- Интерфейсы модели данных при изменении структуры
- HTML в переменной `context` для изменения внешнего вида
- Строки переводов в `translations.ru`
### 2. Схемы валидации (factory)
**Путь:** `/factory/src/Schema/[SchemaName].ts`
Содержат:
- JSON схемы для валидации данных
- Определение обязательных полей
**При изменении структуры данных:**
- Обновите соответствующую схему валидации
- Обновите список обязательных полей в `required`
### 3. Фабрики документов (factory)
**Путь:** `/factory/src/Actions/[ID.DocumentName].ts`
Обрабатывают:
- Получение данных из разных источников
- Сборку модели для шаблонизатора
- Валидацию данных по схеме
- Генерацию PDF
**При изменении логики сборки документа:**
- Обновите метод `generateDocument()`
- Добавьте новые источники данных
### 4. Шаблоны (factory)
**Путь:** `/factory/src/Templates/[ID.DocumentName].ts`
Связывают:
- Шаблон из cooptypes
- Схему валидации
- Метаданные документа
**После изменения в cooptypes:**
- Убедитесь, что схема в Templates соответствует новой модели
### 5. Тесты (factory)
**Путь:** `/factory/test/[category].test.ts`
Содержат:
- Тестовые данные для генерации документов
- Вызовы функции `testDocumentGeneration`
**После внесения изменений:**
- Обновите тестовые данные в соответствии с новой структурой
- Запустите тесты командой `pnpm test`
## Алгоритм внесения изменений
1. **Модификация типов**
- Изменить файл в `/cooptypes/src/cooperative/registry/[ID.DocumentName]/index.ts`
- Обновить интерфейсы, шаблон HTML и переводы
2. **Обновление схемы валидации**
- Изменить соответствующую схему в `/factory/src/Schema/`
- Убедиться, что обязательные поля совпадают с интерфейсом
3. **Компиляция библиотеки типов**
- Выполнить `cd /cooptypes && pnpm build`
4. **Обновление тестовых данных**
- Привести тестовые данные в соответствие с новой структурой
5. **Запуск тестов**
- Выполнить `cd /factory && pnpm test [filename].test.ts`
## Особенности шаблонизации
- Используется Nunjucks для шаблонов
- Поддерживаются конструкции: `{% if %}`, `{% for %}`, `{% trans %}`
- Переменные вставляются через `{{ variable }}`
- Переводы через `{% trans 'KEY' %}`
+225
View File
@@ -0,0 +1,225 @@
# Архитектура расширений в 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();
}
}
```
## Управление отображением конфигурации
Zod-схемы используются для автоматического отображения формы настроек в интерфейсе пользователя.
Через интерфейс `DeserializedDescriptionOfExtension` из `components/controller/src/types/shared/extension.types.ts`
можно управлять отображением полей формы:
```typescript
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` - текст до/после значения поля
## Взаимодействие с блокчейном
Для взаимодействия с блокчейном:
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,
},
},
// ...
];
}
```
+168
View File
@@ -0,0 +1,168 @@
---
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,
},
},
// ...
];
}
```
+261
View File
@@ -0,0 +1,261 @@
---
description:
globs: src/**/*.ts
alwaysApply: false
---
# Фабрика Документов Кооперативов
## Общая Архитектура
Фабрика документов — это система генерации PDF документов для кооперативов, построенная на TypeScript с использованием MongoDB для хранения данных. Система состоит из трех основных частей:
1. **registry/** — JSON-шаблоны документов (статические данные)
2. **factory/** — основная фабрика с логикой генерации
3. **cooptypes/** — типы данных и интерфейсы
## Структура Registry
В корневой папке `registry/` находятся JSON файлы с номерными названиями, представляющие шаблоны документов:
### Основные документы:
- `1.walletProgramAgreement.json` — соглашение о кошельке
- `2.regulationElectronicSignature.json` — регламент электронной подписи
- `3.privacyPolicy.json` — политика конфиденциальности
- `4.userAgreement.json` — пользовательское соглашение
- `50.CoopenomicsAgreement.json` — соглашение с партнерами
- `100.participantApplication.json` — заявление участника
- `101.selectBranchStatement.json` — заявление о выборе филиала
### Документы общих собраний (300-304):
- `300.annualGeneralMeetingAgenda.json` — предложение повестки дня
- `301.annualGeneralMeetingSovietDecision.json` — протокол заседания совета
- `302.annualGeneralMeetingNotification.json` — уведомление о собрании
- `303.annualGeneralMeetingVotingBallot.json` — бюллетень для голосования
- `304.annualGeneralMeetingDecision.json` — протокол общего собрания
### Инвестиционные документы (1000+):
- `1000.investAgreement.json` — инвестиционное соглашение
- `1001.investByResultStatement.json` — заявление о зачете по результатам
- `1002.investByResultAct.json` — акт зачета по результатам
- `1005.investByMoneyStatement.json` — заявление о зачете денежных средств
### Структура JSON-шаблона:
```json
{
"context": "<div>...HTML шаблон с переменными...</div>",
"model": {...данные для примера...},
"translation": {...переводы ключей...},
"object_model": {...схема объектной модели...}
}
```
## Фабрика (factory/)
### Основные компоненты:
#### src/index.ts — Главный класс Generator
```typescript
export class Generator implements IGenerator {
// Хранилище фабрик для каждого типа документа
factories: { [K in Numbers]: DocFactory<IGenerate> }
// MongoDB коннектор
public storage: MongoDBConnector
// Основной метод генерации
async generate(data: IGenerate, options?: IGenerationOptions): Promise<IGeneratedDocument>
}
```
#### Архитектура Factory Pattern:
- Базовый класс `DocFactory<T>` в `src/Factory/index.ts`
- Каждый документ имеет свою фабрику в `src/Actions/`
- Фабрики наследуются от `DocFactory` и реализуют метод `generateDocument()`
### Сервисы:
#### Services/Generator/ — PDF генерация
- `PDFService` — конвертирует HTML в PDF через WeasyPrint
- Использует шрифт Arial (base64)
- Добавляет метаданные в PDF
- Вычисляет SHA-256 хеш документа
#### Services/Templator/ — Шаблонизация
- Основан на Nunjucks
- Поддерживает кастомное расширение `{% trans %}` для переводов
- Рендерит HTML из шаблона с подстановкой переменных
#### Services/Validator/ — Валидация
- Использует AJV для JSON Schema валидации
- Поддерживает кастомные форматы (телефон)
- Локализация ошибок на русском языке
#### Services/Databazor/ — База данных
- `MongoDBConnector` — работа с MongoDB
- `DataService` — абстракция над данными
- Коллекции: `deltas`, `actions`, `documents`, и другие
### Модели данных (src/Models/):
#### Основные типы пользователей:
- `Individual` — физические лица (ФИО, паспорт, адрес)
- `Organization` — организации (ИНН, ОГРН, представитель)
- `Entrepreneur` — ИП (ФИО + ИНН/ОГРН)
#### Кооперативные данные:
- `Cooperative` — данные кооператива
- `PaymentMethod` — платежные методы
- `Vars` — переменные кооператива
- `Project` — проекты
### Система Action-ов:
Каждый документ имеет Action класс в `src/Actions/` с методом `generateDocument()`:
1. **Получение шаблона** — из локального Registry или MongoDB
2. **Сбор данных** — пользователь, кооператив, переменные, специфичные данные
3. **Валидация** — проверка по JSON схеме
4. **Рендеринг** — HTML из шаблона + данные
5. **PDF генерация** — HTML → PDF с метаданными
6. **Сохранение** — в MongoDB (если не skip_save)
## Система типов (cooptypes/)
### cooperative/registry/ — Типы документов
Каждый документ имеет папку с интерфейсами:
- `Action` — входные данные для генерации
- `Model` — модель данных для шаблона
- `Template` — структура шаблона
### contracts/ — Блокчейн контракты
- `registrator/` — регистрация кооперативов
- `soviet/` — управление советом
- `meet/` — общие собрания
- `wallet/`, `capital/`, `fund/` — финансовые операции
## База данных MongoDB
### Основные коллекции:
#### deltas — Состояние блокчейна
```javascript
{
block_num: number,
present: boolean,
code: string, // название контракта
scope: string, // область действия
table: string, // имя таблицы
primary_key: string,
value: {...} // данные записи
}
```
#### actions — Действия блокчейна
```javascript
{
block_num: number,
account: string,
name: string, // имя действия
receiver: string,
data: {...} // данные действия
}
```
#### documents — Сгенерированные документы
```javascript
{
hash: string, // SHA-256 хеш
binary: Uint8Array, // PDF данные
html: string, // HTML исходник
meta: { // метаданные
title: string,
created_at: string,
lang: string
}
}
```
### Таблицы данных:
- `coops` — кооперативы
- `meets` — собрания
- `questions` — вопросы собраний
- `decisions` — решения совета
- `individuals` — физические лица
- `organizations` — организации
- `entrepreneurs` — ИП
- `paymentMethods` — платежные методы
- `vars` — переменные кооперативов
## Генерация документов
### Процесс генерации:
1. **Вызов** `generator.generate(action, options)`
2. **Поиск фабрики** по `registry_id`
3. **Загрузка шаблона** (локально или из БД)
4. **Сбор данных** из MongoDB по `coopname`, `username`, `block_num`
5. **Создание модели** — объединение всех данных
6. **Валидация** модели по JSON схеме
7. **Рендеринг HTML** через Nunjucks
8. **Генерация PDF** через WeasyPrint
9. **Добавление метаданных** в PDF
10. **Вычисление хеша** SHA-256
11. **Сохранение** в MongoDB
### Пример использования:
```typescript
const generator = new Generator()
await generator.connect(mongoUri)
const document = await generator.generate({
registry_id: '300',
coopname: 'voskhod',
username: 'ant',
block_num: 0,
meet: {...},
questions: [...]
})
```
## Особенности реализации
### Шаблонизация:
- HTML шаблоны с CSS стилями
- Переменные в формате `{{variable.field}}`
- Условная логика `{% if condition %}`
- Циклы `{% for item in array %}`
- Переводы `{% trans 'KEY', var1, var2 %}`
### Подписи:
- Цифровые подписи вместо физических
- Текст "Подписано электронной подписью"
- Убраны подчеркивания для подписей
### Типы собраний:
- `regular` — очередное
- `extraordinary` — внеочередное
- Условная логика в шаблонах
### Филиалы:
- `coop.is_branched` — проверка на наличие филиалов
- "пайщиков" vs "уполномоченных" в зависимости от типа
### Форматирование дат:
- Формат: "г. Москва, 15 декабря 2024 г."
- Без кавычек вокруг дат
- Запятая после города
## Тестирование
### test/utils/index.ts — Тестовые утилиты:
- `preLoading()` — инициализация тестовых данных
- Создание кооператива, пользователей, платежных методов
- Настройка данных собраний и решений
- Очистка временных файлов
### Тестовые данные:
- Кооператив "ВОСХОД"
- Пользователи: ant, individual, entrepreneur
- Организации: voskhod, branch, exampleorg
- Собрания с вопросами и решениями
Фабрика поддерживает полный цикл создания документов кооператива от заявлений до протоколов собраний с возможностью кастомизации под разные типы кооперативов и требования.
+59
View File
@@ -0,0 +1,59 @@
---
description:
globs: components/sdk/**
alwaysApply: false
---
# Руководство по работе с GraphQL Zeus в SDK
## Основной процесс
1. **Анализ DTO бэкенда**:
- Изучите структуру DTO классов (`@ObjectType`) в бэкенде
- Обратите внимание на имена типов в декораторах `@ObjectType('ИмяТипа')`
- Отметьте поля, связи и вложенные объекты
2. **Создание селекторов**:
- Для каждого DTO создайте соответствующий селектор с именем `raw<ИмяТипа>Selector`
- Селектор - это объект, где ключи соответствуют полям DTO, а значения - `true`
- Для вложенных объектов используйте вложенные селекторы: `field: nestedSelector`
- Для списков используйте один селектор без массива: `items: itemSelector`
3. **Валидация селекторов**:
- Для каждого селектора создайте проверку типа:
```typescript
const _validate: MakeAllFieldsRequired<ValueTypes['ТочноеИмяГрафКьЭлТипа']> = rawSelector
```
- Имя типа должно точно совпадать с именем в декораторе `@ObjectType`
4. **Экспорт селекторов**:
- Создайте финальный селектор с помощью функции `Selector`:
```typescript
export const typeSelector = Selector('ТочноеИмяГрафКьЭлТипа')(rawTypeSelector)
```
- Экспортируйте сырой селектор для переиспользования
- Экспортируйте тип модели: `export type modelType = ModelTypes['ТочноеИмяГрафКьЭлТипа']`
5. **Создание запросов/мутаций**:
- Используйте селекторы в запросах и мутациях:
```typescript
export const query = Selector('Query')({
queryName: [{ data: $('data', 'ТочноеИмяВходногоТипа!') }, exportedSelector]
})
```
- Для параметров используйте оператор `$` с точным именем входного типа
- Создайте интерфейс входных данных:
```typescript
export interface IInput {
data: ModelTypes['ТочноеИмяВходногоТипа']
}
```
## Особенности работы
- **Документы**: всегда сохраняйте структуру `{ hash, signatures, rawDocument }`
- **Сложные DTO**: разбивайте на атомарные селекторы и комбинируйте их
- **Типы в Zeus**: часто отличаются от имен классов в бэкенде, всегда проверяйте в `schema.gql`
- **Массивы**: Zeus автоматически обрабатывает массивы, не используйте `[selector]`
Это руководство поможет правильно структурировать работу с SDK и избежать типичных ошибок при работе с Zeus.
+20
View File
@@ -0,0 +1,20 @@
---
globs: notifications/src/workflows/**/*.ts
---
# Правила валидации воркфлоу
## 🚫 ID воркфлоу
- **Длина ID не должна превышать 32 символа**
- Используйте описательные, но короткие идентификаторы малыми латинскими буквами и тире.
## 🚫 Условия в шаблонах
- **Запрещено использовать JavaScript выражения в шаблонах Novu**
- Нельзя использовать:
- Тернарные операторы: `{{condition ? "text1" : "text2"}}`
- Логические операторы: `{{field && "text"}}`
- Любые другие JS конструкции
## ✅ Рекомендации
- Добавляйте текстовые поля в payload для условной логики
- Вычисляйте значения на стороне сервера перед отправкой уведомления
- Используйте только простые переменные: `{{payload.fieldName}}`
+3
View File
@@ -0,0 +1,3 @@
node_modules
dist
+163
View File
@@ -0,0 +1,163 @@
name: Build Docker Images
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
fetch-depth: 0
- name: Debug info
run: |
echo "Текущая ветка:"
git branch --show-current
echo "Последние коммиты:"
git log -n 3 --oneline
echo "Проверяем файлы в директории components/desktop/src-ssr:"
ls -la components/desktop/src-ssr/ || echo "Директория не найдена!"
echo "Проверяем файлы в middlewares:"
ls -la components/desktop/src-ssr/middlewares/ || echo "Директория middlewares не найдена!"
- name: Set docker tags
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
TAG_NAME=${GITHUB_REF#refs/tags/}
echo "DOCKER_TAG=$TAG_NAME" >> $GITHUB_ENV
# Проверяем, является ли тег продакшн-тегом (не содержит alpha, beta, rc и т.д.)
if [[ ! $TAG_NAME =~ -(alpha|beta|rc|test) ]]; then
echo "IS_PRODUCTION_TAG=true" >> $GITHUB_ENV
echo "Это продакшн тег, будем добавлять latest"
else
echo "IS_PRODUCTION_TAG=false" >> $GITHUB_ENV
echo "Это не продакшн тег, latest не добавляем"
fi
else
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
echo "IS_PRODUCTION_TAG=false" >> $GITHUB_ENV
fi
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Сначала собираем базовый образ с runtime
- name: Build base image
run: |
docker build --target runtime -t dicoop/mono-base:${{ env.DOCKER_TAG }} .
docker push dicoop/mono-base:${{ env.DOCKER_TAG }}
# Если это продакшн тег, добавляем latest
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/mono-base:${{ env.DOCKER_TAG }} dicoop/mono-base:latest
docker push dicoop/mono-base:latest
fi
# Создаем сервисные образы на основе базового
- name: Build desktop image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.desktop
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/desktop\", \"run\", \"start\"]" >> Dockerfile.desktop
docker build -t dicoop/desktop:${{ env.DOCKER_TAG }} -f Dockerfile.desktop .
docker push dicoop/desktop:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/desktop:${{ env.DOCKER_TAG }} dicoop/desktop:latest
docker push dicoop/desktop:latest
fi
- name: Build controller image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.coopback
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/controller\", \"run\", \"start\"]" >> Dockerfile.coopback
docker build -t dicoop/coopback:${{ env.DOCKER_TAG }} -f Dockerfile.coopback .
docker push dicoop/coopback:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/coopback:${{ env.DOCKER_TAG }} dicoop/coopback:latest
docker push dicoop/coopback:latest
fi
- name: Build parser image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.cooparser
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/parser\", \"run\", \"start\"]" >> Dockerfile.cooparser
docker build -t dicoop/cooparser:${{ env.DOCKER_TAG }} -f Dockerfile.cooparser .
docker push dicoop/cooparser:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/cooparser:${{ env.DOCKER_TAG }} dicoop/cooparser:latest
docker push dicoop/cooparser:latest
fi
- name: Build notificator image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.notificator
echo "CMD [\"pnpm\", \"-F\", \"coop-notificator\", \"run\", \"start\"]" >> Dockerfile.notificator
docker build -t dicoop/notificator:${{ env.DOCKER_TAG }} -f Dockerfile.notificator .
docker push dicoop/notificator:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/notificator:${{ env.DOCKER_TAG }} dicoop/notificator:latest
docker push dicoop/notificator:latest
fi
- name: Build notifications image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.notifications
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/notifications\", \"run\", \"sync\"]" >> Dockerfile.notifications
docker build -t dicoop/notifications:${{ env.DOCKER_TAG }} -f Dockerfile.notifications .
docker push dicoop/notifications:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/notifications:${{ env.DOCKER_TAG }} dicoop/notifications:latest
docker push dicoop/notifications:latest
fi
# Отправка хука для деплоя
- name: Trigger deployment webhook
if: ${{ success() }}
run: |
if [[ $GITHUB_REF == refs/tags/*alpha* ]]; then
# Хук для тестнета (alpha теги)
curl -X POST ${{ vars.TESTNET_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
elif [[ $GITHUB_REF == refs/tags/* ]]; then
# Хук для продакшена (остальные теги)
curl -X POST ${{ vars.PRODUCTION_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
fi
# Уведомление в Telegram об успехе
- name: Telegram notify success
if: ${{ success() }}
run: |
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
ADDITIONAL_INFO=" (с тегом latest)"
else
ADDITIONAL_INFO=""
fi
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
-d text="✅ [GITHUB MONO] Успешная сборка контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]$ADDITIONAL_INFO"
# Уведомление в Telegram об ошибке
- name: Telegram notify failure
if: ${{ failure() }}
run: |
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
-d text="❌ [GITHUB MONO] Ошибка при сборке контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]"
@@ -0,0 +1,18 @@
# .github/workflows/trigger-coopenomics.yml
name: Trigger Contracts Docs Deploy
on:
push:
branches: [dev, testnet, main, capital] # или когда нужно триггерить
jobs:
trigger-coopenomics:
runs-on: ubuntu-latest
steps:
- name: Trigger Coopenomics deployment
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.COOPENOMICS_PAT }}
repository: coopenomics/coopenomics # укажи правильный owner/repo
event-type: deploy_from_mono
client-payload: '{"repository": "${{ github.repository }}", "sha": "${{ github.sha }}", "ref": "${{ github.ref }}", "actor": "${{ github.actor }}"}'
+114
View File
@@ -0,0 +1,114 @@
name: Publish Docs
on:
push:
branches:
- main
- testnet
- dev
jobs:
build-and-publish-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install pnpm
run: npm install -g pnpm
- name: Install Python requirements
run: |
python -m venv venv
source venv/bin/activate
pip install mkdocs-material mkdocs-macros-plugin mkdocs-section-index pymdown-extensions
working-directory: ./components/docs
- name: Install Node.js dependencies
run: pnpm install
working-directory: ./components/docs
- name: Patch spectaql-config.yml for CI
run: |
sed -i.bak "0,/url:.*/s|url:.*|url: 'https://testnet.coopenomics.world/backend/v1/graphql'|" spectaql-config.yml
working-directory: ./components/controller
- name: Show patched spectaql-config.yml
run: cat spectaql-config.yml
working-directory: ./components/controller
- name: Build cooptypes
run: pnpm run build
working-directory: ./components/cooptypes
- name: Generate controller docs
run: pnpm run docs
working-directory: ./components/controller
- name: Copy controller docs
run: |
mkdir -p ./components/docs/docs/graphql
cp -r ./components/controller/docs/* ./components/docs/docs/graphql/
- name: Generate sdk docs
run: pnpm run docs
working-directory: ./components/sdk
- name: Copy sdk docs
run: |
mkdir -p ./components/docs/docs/sdk
cp -r ./components/sdk/docs/* ./components/docs/docs/sdk/
- name: Generate cooptypes docs
run: pnpm run docs
working-directory: ./components/cooptypes
- name: Copy cooptypes docs
run: |
mkdir -p ./components/docs/docs/cooptypes
cp -r ./components/cooptypes/docs/* ./components/docs/docs/cooptypes/
- name: Build docs (mkdocs)
run: |
source venv/bin/activate
mkdocs build
working-directory: ./components/docs
- name: Remove specific large file before publishing
run: |
# Удаляем конкретный большой файл sdk/typedoc.json
rm -f ./components/docs/site/sdk/typedoc.json
# Проверяем, что файл удален
if [ -f "./components/docs/site/sdk/typedoc.json" ]; then
echo "ERROR: typedoc.json still exists!"
exit 1
else
echo "SUCCESS: typedoc.json removed successfully"
fi
- name: Publish to GitHub Pages
run: npx gh-pages --nojekyll -d site --repo https://x-access-token:${GITHUB_TOKEN}@github.com/coopenomics/mono.git
working-directory: ./components/docs
env:
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@github.com
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger docs deployment webhook
if: ${{ success() }}
run: |
curl -X POST "${{ vars.DOCS_DEPLOY_WEBHOOK_URL }}" \
-H 'Content-Type: application/json' \
-d '{"ref":"${{ github.ref }}","sha":"${{ github.sha }}","branch":"${{ github.ref_name }}"}'
+37
View File
@@ -0,0 +1,37 @@
name: Publish Packages
on:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
build-and-publish:
if: |
startsWith(github.ref, 'refs/tags/v') &&
!contains(github.ref, '-alpha')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Та же мажорная линия pnpm, что и lockfile (lockfileVersion 6.0 = pnpm 8).
# Иначе `npm i -g pnpm` тянет последний pnpm и переписывает pnpm-lock.yaml → Lerna EUNCOMMIT.
- uses: pnpm/action-setup@v4
with:
version: 8.15.8
- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lerna run build
- run: pnpm lerna publish from-package --yes
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+9
View File
@@ -1,2 +1,11 @@
node_modules/
lerna-debug.log
components/controller/graph.png
blockchain-data/
scripts/changelog-prompt.md
scripts/changelog-release.md
scripts/release-info.md
components/docs/docs/sdk
dist/
.env
.DS_Store
+8
View File
@@ -0,0 +1,8 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 120,
"plugins": [
"prettier-plugin-sort-imports"
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
"vue.volar",
"vue.vscode-typescript-vue-plugin"
]
}
+74
View File
@@ -0,0 +1,74 @@
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.tabCompletion": "onlySnippets",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.run": "onType",
"eslint.validate": ["javascript", "typescript", "vue"],
"i18n-ally.localesPaths": ["src/i18n"],
"typescript.format.enable": true,
"typescript.format.indentSwitchCase": false,
"typescript.validate.enable": true,
"notebook.defaultFormatter": "Vue.volar",
// Оптимизация для монорепозитория
"typescript.preferences.useAliasesForRenames": false,
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.suggest.autoImports": true,
"typescript.suggest.paths": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.workspaceSymbols.scope": "currentProject",
// Настройки для снижения нагрузки
"files.watcherExclude": {
"**/node_modules/**": true,
"**/dist/**": true,
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.cache/**": true,
"**/.quasar/**": true,
"**/*.tsbuildinfo": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.cache": true,
"**/.quasar": true,
"**/*.tsbuildinfo": true
},
"files.exclude": {
"**/.cache": true,
"**/*.tsbuildinfo": true,
"**/node_modules/.cache": true
},
// TypeScript server настройки для монорепозитория
"typescript.tsserver.maxTsServerMemory": 8192,
"typescript.tsserver.watchOptions": {
"excludeDirectories": [
"**/node_modules",
"**/dist",
"**/.cache",
"**/.quasar",
"**/build"
]
},
// Использовать локальный TypeScript из монорепо
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
// КРИТИЧЕСКИ ВАЖНО: включить project references для монорепозитория
"typescript.tsserver.useSyntaxServer": "auto",
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// Оптимизация для больших монорепозиториев
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.surveys.enabled": false,
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}
+87
View File
@@ -0,0 +1,87 @@
# AGENTS.md
## Cursor Cloud specific instructions
### Обзор
Монорепозиторий «Цифровой Кооператив» (monocoop) — платформа управления кооперативами на блокчейне EOSIO. pnpm v9 + Lerna. Node.js v20.
### Сервисы
| Компонент | Контейнер | Порт | Описание |
|-----------|-----------|------|----------|
| controller | coopback | 2998 | NestJS GraphQL API |
| desktop | desktop | 2999 | Vue 3 + Quasar SPA |
| parser | cooparser | 4000 | Индексация блокчейна через SHiP |
| blockchain | node | 8888, 8070 | EOSIO node + State History Plugin |
| MongoDB | mongo | 27017 | Основная БД (replica set) |
| Redis | monoredis | 6379 | Кэш и стримы |
| PG | (см. compose) | 5532→5432 | Реляционная БД |
### Полный перезапуск (одна команда)
```
pnpm run reboot
```
Делает: останавливает контейнеры → чистит blockchain data и volumes → поднимает инфру → ждёт готовности → `pnpm run boot` → запускает parser и controller.
### Первоначальная настройка Cloud-окружения
1. **`/etc/hosts`** — обязательно для boot (запускается на хосте, обращается к MongoDB по docker hostname):
```
echo "127.0.0.1 mongo" | sudo tee -a /etc/hosts
echo "127.0.0.1 monoredis" | sudo tee -a /etc/hosts
```
2. **WeasyPrint** — системная зависимость для генерации PDF:
```
sudo apt-get install -y python3 python3-venv libpango-1.0-0 libcairo2 libffi-dev libjpeg-dev libopenjp2-7-dev libharfbuzz-dev
sudo python3 -m venv /opt/weasyprint-venv && sudo /opt/weasyprint-venv/bin/pip install WeasyPrint==67
sudo ln -sf /opt/weasyprint-venv/bin/weasyprint /usr/local/bin/weasyprint
```
3. **Контракты** (test-режим — позволяет boot с 1 членом совета):
```
cd components/contracts && sudo rm -rf build && bash build-all.sh test
```
4. **Shared-библиотеки** (порядок важен):
```
pnpm --filter cooptypes run build
pnpm --filter @coopenomics/factory run build
pnpm --filter @coopenomics/sdk run build
pnpm --filter @coopenomics/notifications run build
```
5. **`.env` файлы** — скопировать из `.env-example`, адаптировать hostnames:
- Controller/Parser (в Docker): хосты по именам контейнеров из docker-compose (порт БД 5432)
- Boot (на хосте): `127.0.0.1`, PG порт `5532`, mongo через `/etc/hosts`
- Desktop: `127.0.0.1`
- Controller: `BACKEND_URL` (публичный URL API) и `FRONTEND_URL` (публичный URL SPA), см. `components/controller/.env-example`
- **CHAIN_ID**: берётся из `curl http://localhost:8888/v1/chain/get_info` после старта ноды
- Controller требует `VAPID_PUBLIC_KEY` и `VAPID_PRIVATE_KEY`
6. **Запуск**: `pnpm run reboot`, затем `docker compose up -d --force-recreate coopback cooparser` (если .env менялись)
### Запуск тестов
- **Factory** (`components/factory`): нужен только MongoDB. Запуск:
```
NODE_ENV=test SOURCE=local MONGO_URI=$MONGO_URI SKIP_BLOCK_FETCH=TRUE pnpm --filter @coopenomics/factory test
```
`MONGO_URI` по умолчанию: `mongodb://<host>:27017/cooperative-x`.
- **Boot** (`components/boot`): требует полный EOSIO blockchain + MongoDB + PG. Запуск после `pnpm run reboot`:
```
pnpm --filter @coopenomics/boot test
```
- **Duplicate transaction** — в boot-тестах EOSIO отклоняет транзакции с одинаковым хешем (TAPOS block + action data). При повторном вызове `refreshSegment` для того же участника — добавить `await sleep(500)` перед ним. Паттерн уже используется (см. комментарий на строке ~720 capital.test.ts).
### Критические gotchas
- **SHiP порт 8070** — `state-history-endpoint = 0.0.0.0:8070` в config.ini. Парсер: `SHIP=ws://node:8070`.
- **Парсер START_BLOCK**: при `START_BLOCK=1` на чистой БД стартует с HEAD и делает частичную инициализацию. Для полного replay: временно `START_BLOCK=2`, после первого запуска вернуть `1`.
- **SSR desktop в dev** — расширения не рендерятся из-за Pinia SSR-сериализации компонентов. Dev — SPA (`quasar dev`), production build SSR работает.
- **Тестовые учётные данные**: email `ivanov@example.com`, ключ — дефолтный EOSIO dev key (см. `components/boot/.env-example`), пользователь `ant` (председатель).
- **Docker hostnames**: без `network_mode: host` — контейнеры обращаются друг к другу по именам контейнеров из docker-compose.
- **Установка пакетов**: только через фильтр — `pnpm add <pkg> --filter <component>`.
+566
View File
@@ -0,0 +1,566 @@
# v2026.4.2-2
В этом релизе — стабилизация Благороста для вывода в продуктивную работу с результатами интеллектуальной деятельности. Отдельно заложена основа под отчётность в ФНС и ФСС и прототип поиска по документам.
**Благорост и проекты**
- Быстрые действия на странице программы: создать проект, компонент, задачу или требование.
- Требования к компонентам в репозитории: поддержка **Mermaid**, **Draw.io** и **BPMN**.
- Синхронизация проектов, компонентов, требований и задач с Git-репозиторием результатов.
- Встраивание полноразмерного видео (iframe) на страницах проектов.
- Скачивание пакета подписанных документов одной кнопкой.
- Комнаты проектов в кооперативном мессенджере.
**Мессенджер и звонки**
- Автосекретарь: запись синхронных звонков, текстовых и голосовых сообщений в проектных комнатах.
**Отчётность и инфраструктура**
- Прототип фабрики отчётов ФНС/ФСС: выгрузка в XML для дальнейшей отправки.
- Прототип поисковой системы по документам.
- Установщик для развёртывания на своих серверах (разработка или эксплуатация).
- Рефакторинг в сторону чистой архитектуры на бэкенде.
- Ускорение сборки фронтенда за счёт перехода на **Vite 8**.
**Исправления**
- Повторное общее собрание больше не мешало завершить онбординг кооператива.
- Центр уведомлений не блокировал загрузку рабочего стола при отключённом провайдере оповещений.
- Уведомления о собрании совета по свободным вопросам снова доходят до членов совета.
- В интерфейсе восстановлено отображение контактов кооператива.
#releases
---
# v2026.4.2-2
В этом релизе — стабилизация Благороста для вывода в продуктивную работу с результатами интеллектуальной деятельности. Отдельно заложена основа под отчётность в ФНС и ФСС и прототип поиска по документам.
**Благорост и проекты**
- Быстрые действия на странице программы: создать проект, компонент, задачу или требование.
- Требования к компонентам в репозитории: поддержка **Mermaid**, **Draw.io** и **BPMN**.
- Синхронизация проектов, компонентов, требований и задач с Git-репозиторием результатов.
- Встраивание полноразмерного видео (iframe) на страницах проектов.
- Скачивание пакета подписанных документов одной кнопкой.
- Комнаты проектов в кооперативном мессенджере.
**Мессенджер и звонки**
- Автосекретарь: запись синхронных звонков, текстовых и голосовых сообщений в проектных комнатах.
**Отчётность и инфраструктура**
- Прототип фабрики отчётов ФНС/ФСС: выгрузка в XML для дальнейшей отправки.
- Прототип поисковой системы по документам.
- Установщик для развёртывания на своих серверах (разработка или эксплуатация).
- Рефакторинг в сторону чистой архитектуры на бэкенде.
- Ускорение сборки фронтенда за счёт перехода на **Vite 8**.
**Исправления**
- Повторное общее собрание больше не мешало завершить онбординг кооператива.
- Центр уведомлений не блокировал загрузку рабочего стола при отключённом провайдере оповещений.
- Уведомления о собрании совета по свободным вопросам снова доходят до членов совета.
- В интерфейсе восстановлено отображение контактов кооператива.
#releases
---
# v2025.12.28-8
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
---
### ✨ Новые функции
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию для передачи документов и проводок
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование LMS TUTOR для образовательных задач
- [#328](https://github.com/coopenomics/mono/issues/328): Размещение прототипов мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности
- [#324](https://github.com/coopenomics/mono/issues/324): Смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#322](https://github.com/coopenomics/mono/issues/322): Поставка обновлений ПО с нулевым даунтаймом по blue-green стратегии
- [#321](https://github.com/coopenomics/mono/issues/321): Внедрение системы проводок по фондам для контрактов
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#316](https://github.com/coopenomics/mono/issues/316): Переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёртывание GlitchTip для мониторинга ошибок
- [#306](https://github.com/coopenomics/mono/issues/306): Модуль запросов и мутаций для контракта капитализации
### 🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправление подписки на изменение статуса коммитов
- [#309](https://github.com/coopenomics/mono/issues/309): Исправление отображения чужих билетов времени в трекере
### 🔧 Улучшения
- [#331](https://github.com/coopenomics/mono/issues/331): Настройка системы мониторинга сбоев и ошибок на базе GlitchTIP, Loki, Prometheus
- [#327](https://github.com/coopenomics/mono/issues/327): Пользовательская документация по интерфейсам цифрового кооператива
- [#325](https://github.com/coopenomics/mono/issues/325): Документирование смарт-контракта программы "Благорост"
- [#308](https://github.com/coopenomics/mono/issues/308): Улучшение отображения рабочих столов в магазине приложений
- [#307](https://github.com/coopenomics/mono/issues/307): Объединение настроек контракта с нативными настройками приложения
- [#305](https://github.com/coopenomics/mono/issues/305): Объединение полей title и description в проекте
- [#304](https://github.com/coopenomics/mono/issues/304): Доменная модель контракта капитализации на бэкенде
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотр архитектуры парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, внедрение двухконтурной шины данных и обработки микрофорков
- [#301](https://github.com/coopenomics/mono/issues/301): Доработка и отладка контракта "Капитализация РИД" v0.2
- [#222](https://github.com/coopenomics/mono/issues/222): Внедрение метода Водянова для распределения пула премий по программе "Благорост"
- [#212](https://github.com/coopenomics/mono/issues/212): Снижение точности валютных значений до двух знаков после запятой в документах
#releases
---
# v2025.12.28
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
---
### ✨ Новые функции
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию для передачи документов и проводок
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование LMS TUTOR для образовательных задач
- [#328](https://github.com/coopenomics/mono/issues/328): Размещение прототипов мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности
- [#324](https://github.com/coopenomics/mono/issues/324): Смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#322](https://github.com/coopenomics/mono/issues/322): Поставка обновлений ПО с нулевым даунтаймом по blue-green стратегии
- [#321](https://github.com/coopenomics/mono/issues/321): Внедрение системы проводок по фондам для контрактов
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#316](https://github.com/coopenomics/mono/issues/316): Переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёртывание GlitchTip для мониторинга ошибок
- [#306](https://github.com/coopenomics/mono/issues/306): Модуль запросов и мутаций для контракта капитализации
### 🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправление подписки на изменение статуса коммитов
- [#309](https://github.com/coopenomics/mono/issues/309): Исправление отображения чужих билетов времени в трекере
### 🔧 Улучшения
- [#331](https://github.com/coopenomics/mono/issues/331): Настройка системы мониторинга сбоев и ошибок на базе GlitchTIP, Loki, Prometheus
- [#327](https://github.com/coopenomics/mono/issues/327): Пользовательская документация по интерфейсам цифрового кооператива
- [#325](https://github.com/coopenomics/mono/issues/325): Документирование смарт-контракта программы "Благорост"
- [#308](https://github.com/coopenomics/mono/issues/308): Улучшение отображения рабочих столов в магазине приложений
- [#307](https://github.com/coopenomics/mono/issues/307): Объединение настроек контракта с нативными настройками приложения
- [#305](https://github.com/coopenomics/mono/issues/305): Объединение полей title и description в проекте
- [#304](https://github.com/coopenomics/mono/issues/304): Доменная модель контракта капитализации на бэкенде
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотр архитектуры парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, внедрение двухконтурной шины данных и обработки микрофорков
- [#301](https://github.com/coopenomics/mono/issues/301): Доработка и отладка контракта "Капитализация РИД" v0.2
- [#222](https://github.com/coopenomics/mono/issues/222): Внедрение метода Водянова для распределения пула премий по программе "Благорост"
- [#212](https://github.com/coopenomics/mono/issues/212): Снижение точности валютных значений до двух знаков после запятой в документах
#releases
---
# v2025.12.28
В этом релизе реализован смарт-контракт генерации и капитализации результатов интеллектуальной деятельности, завершена интеграция с учётными системами, улучшены интерфейсы и документация. Подробнее о контракте: https://coopenomics.world/contracts/group__public__capital.html
✨ Новые функции
- [#324](https://github.com/coopenomics/mono/issues/324): Реализован смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#301](https://github.com/coopenomics/mono/issues/301): Контракт "Капитализация РИД" v0.2
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию: выгрузка документов и проводки по счетам
- [#322](https://github.com/coopenomics/mono/issues/322): Обновления ПО с нулевым даунтаймом по blue-green стратегии
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#308](https://github.com/coopenomics/mono/issues/308): Магазин приложений с поддержкой подключения нескольких рабочих столов одним приложением
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности по программе "Благорост"
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправлена ошибка со статусом коммитов — подписка теперь работает корректно
- [#309](https://github.com/coopenomics/mono/issues/309): Исправлен баг с отображением чужих билетов времени в трекере
🔧 Улучшения
- [#325](https://github.com/coopenomics/mono/issues/325): Документирован смарт-контракт программы "Благорост"
- [#327](https://github.com/coopenomics/mono/issues/327): Подготовлена пользовательская документация цифрового кооператива по интерфейсам
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование образовательной платформы LMS TUTOR на Wordpress
- [#328](https://github.com/coopenomics/mono/issues/328): Размещены прототипы мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#321](https://github.com/coopenomics/mono/issues/321): Встроена система проводок по фондам и интеграция с контрактами
- [#318](https://github.com/coopenomics/mono/issues/318): Настроены Loki & Grafana для выгрузки логов из контейнеров
- [#316](https://github.com/coopenomics/mono/issues/316): Завершён переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёрнут GlitchTip как альтернатива Sentry
- [#307](https://github.com/coopenomics/mono/issues/307): Интеграция настроек контракта с нативными настройками приложения, поддержка импорта после конфигурации
- [#306](https://github.com/coopenomics/mono/issues/306): Собран модуль запросов и мутаций контракта капитализации
- [#305](https://github.com/coopenomics/mono/issues/305): Упрощена структура проекта — title & description объединены в одно поле
- [#304](https://github.com/coopenomics/mono/issues/304): Реализована доменная модель контракта капитализации на бэкенде с поддержкой микрофорков
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотрена архитектура парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, реализована двухконтурная шина данных и обработка микрофорков
- [#212](https://github.com/coopenomics/mono/issues/212): Уменьшена точность валютных значений в документах с четырёх до двух знаков
#releases
---
# v2025.9.1
В системе Кооперативной Экономики развернут смарт-контракт CAPITAL v0.2 для генерации и капитализации результатов интеллектуальной деятельности. Контракт описывает и обеспечивает:
- бизнес-процесс производства результатов интеллектуальной деятельности в кооперативах создателями, авторами, инвесторами, координаторами, мастерами и собственниками имущества в кооперации на проектах;
- приём результатов интеллектуальной деятельности в качестве паевых взносов в кооператив;
- распределение потока членских взносов среди вкладчиков;
- капитализацию результатов интеллектуальной деятельности новыми результатами по модели золотого сечения;
- оценку вкладов авторов и создателей по методу Водянова;
В основе математической модели контракта лежит принцип распределения справедливой выгоды между вкладчиками согласно концепции "Общественно-полезного времени", разработанной в рамках теорий Кузнецова и академика Глушкова при работе над проектом общегосударственной автоматизированной системы учета и обработки информации (ОГАС).
Подробнее в документации: https://coopenomics.world/contracts/group__public__capital.html
✨ Новые функции
- [#301](https://github.com/coopenomics/mono/issues/301): Реализация контракта "Капитализация" v0.2: регистрация вкладчиков, создание и управление проектами, поддержка инвестиций, ссуд, членских взносов, проведение голосований, расчет капитализации, интеграция с внешними контрактами, поддержка импорта данных.
#releases
---
# v2025.7.1-1
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
✨ Новые функции
- [#276](https://github.com/coopenomics/mono/issues/276): Реализовать путь возврата паевого взноса из кошелька пайщика
- [#281](https://github.com/coopenomics/mono/issues/281): Генерация заявления на возврат паевого взноса
- [#282](https://github.com/coopenomics/mono/issues/282): Генерация решения совета на возврат паевого взноса
- [#278](https://github.com/coopenomics/mono/issues/278): Введение методов управления возвратами паевых взносов в контроллере
- [#279](https://github.com/coopenomics/mono/issues/279): Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
🐛 Исправления ошибок
- [#286](https://github.com/coopenomics/mono/issues/286): Поправить ошибки склонений времени
- [#262](https://github.com/coopenomics/mono/issues/262): Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
- [#261](https://github.com/coopenomics/mono/issues/261): Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
🔧 Улучшения
- [#285](https://github.com/coopenomics/mono/issues/285): Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
- [#284](https://github.com/coopenomics/mono/issues/284): Разместить кошелек на главную вместо профиля
- [#283](https://github.com/coopenomics/mono/issues/283): Ввести лоадер на переходе между рабочими столами
- [#280](https://github.com/coopenomics/mono/issues/280): Мигрировать имеющиеся данные о входящих платежах в новую модель
- [#277](https://github.com/coopenomics/mono/issues/277): Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
#releases
---
# v2025.6.14
Разработан модуль для проведения очередных общих собраний пайщиков. Исправлены баги, внесены улучшения пользовательского интерфейса.
✨ Новые функции
- [#264](https://github.com/coopenomics/mono/issues/264): Разработан смарт-контракт общего собрания пайщиков (meet)
- [#263](https://github.com/coopenomics/mono/issues/263): Реализован модуль оповещений на электронные почты по жизненному циклу общего собрания пайщиков
- [#273](https://github.com/coopenomics/mono/issues/273): Встроены реальные шаблоны документов общего собрания
- [#268](https://github.com/coopenomics/mono/issues/268): Добавлена страница результатов общего собрания
- [#267](https://github.com/coopenomics/mono/issues/267): Добавлена страница просмотра и скачивания бюллетеней и уведомлений по собранию
- [#270](https://github.com/coopenomics/mono/issues/270): Ссылка в оповещении ведет на страницу собрания с документом-уведомлением для подписи
🐛 Исправления ошибок
- [#262](https://github.com/coopenomics/mono/issues/262): Исправлено некорректное открытие рабочего стола совета
- [#261](https://github.com/coopenomics/mono/issues/261): Исправлена ошибка с редиректом при первой загрузке и прямом переходе на собрание
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика
- [#258](https://github.com/coopenomics/mono/issues/258): Убран hover-эффект на документе и пайщике в таблице
🔧 Улучшения
- [#274](https://github.com/coopenomics/mono/issues/274): Настроена рассылка оповещений на почту при получении решения о проведении собрания
- [#272](https://github.com/coopenomics/mono/issues/272): Подписанные уведомления сохраняются и извлекаются из реестра по Graph-QL
- [#271](https://github.com/coopenomics/mono/issues/271): Ведется подсчет количества пайщиков в каждом кооперативе при добавлении и удалении
- [#260](https://github.com/coopenomics/mono/issues/260): Введен счетчик количества пайщиков в кооперативе на контракте регистратора
- [#256](https://github.com/coopenomics/mono/issues/256): Отображается статус членства каждого пайщика
- [#255](https://github.com/coopenomics/mono/issues/255): В разделе Платежи отображается ФИО/Наименование плательщика
- [#269](https://github.com/coopenomics/mono/issues/269): Реализована рассылка уведомлений перед началом собрания
- [#265](https://github.com/coopenomics/mono/issues/265): Проведена отладка и тестирование процесса общего собрания пайщиков
- [#266](https://github.com/coopenomics/mono/issues/266): Протестированы все процессы общего собрания
#releases
---
# v2025.5.14
В этом релизе реализован новый стандарт передачи и хранения документов по блокчейну с поддержкой неограниченного количества подписей и их валидацией. Также внесены улучшения в интерфейс и исправлены ошибки.
✨ Новые функции
- [#252](https://github.com/coopenomics/mono/issues/252): Внедрение обновленного стандарта хранения и передачи документов по блокчейну
- [#251](https://github.com/coopenomics/mono/issues/251): Реализация версионированного мигратора данных для контроллера кооператива
- [#244](https://github.com/coopenomics/mono/issues/244): Обновление стандарта сборки документов и переход на хэш-идентификаторы
🐛 Исправления ошибок
- [#249](https://github.com/coopenomics/mono/issues/249): Исправлена спутанная маршрутизация между рабочими столами кооперативов
- [#253](https://github.com/coopenomics/mono/issues/253): Исправлена избыточная точность суммы оплаты в заявлении на вступление
🔧 Улучшения
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика на странице пайщиков
- [#258](https://github.com/coopenomics/mono/issues/258): Удалён hover-эффект для документов и пайщиков в таблице
- [#257](https://github.com/coopenomics/mono/issues/257): Добавлено сохранение светлой/тёмной темы в localStorage и восстановление при загрузке страницы
- [#256](https://github.com/coopenomics/mono/issues/256): Отображение статуса членства каждого пайщика в разделе "Пайщики"
- [#255](https://github.com/coopenomics/mono/issues/255): Отображение ФИО/Наименования плательщика в разделе "Платежи"
#releases
---
# v2025.5.2
В этом релизе рабочие столы переведены на серверный рендеринг, что улучшает стабильность развертывания и упрощает автоматизацию поставки ПО. Данный релиз является подготовительным к переходу на новый стандарт цифровых документов на платформе.
✨ Новые функции
- [#245](https://github.com/coopenomics/mono/issues/245): Перевод десктопа на серверный рендеринг с поддержкой динамических переменных окружения
🐛 Исправления ошибок
- [#247](https://github.com/coopenomics/mono/issues/247): Исправлен баг с повторной поставкой ПО при обрывах соединения между серверами
🔧 Улучшения
- [#246](https://github.com/coopenomics/mono/issues/246): Перевод CI/CD на сборку и поставку предсобранных докер-контейнеров
#releases
---
# MONO v2025.4.29
В этом релизе реализована новая архитектура рабочих столов с установкой через маркетплейс. Добавлены стол пайщика и стол совета, переработаны разделы документов и подписей, улучшено разделение кошелька и профиля для повышения удобства пользователей.
✨ Новые функции
- [#234](https://github.com/coopenomics/mono/issues/234): Маркетплейс рабочих столов с поддержкой разных ролей и микросервисной архитектурой
- [#238](https://github.com/coopenomics/mono/issues/238): Контроллер общего собрания пайщиков с поддержкой документооборота и подписей
- [#233](https://github.com/coopenomics/mono/issues/233): Минимальный смарт-контракт общих собраний пайщиков
- [#232](https://github.com/coopenomics/mono/issues/232): Бэкенд полного обозревателя блоков
🐛 Исправления ошибок
- [#231](https://github.com/coopenomics/mono/issues/231): Исправления ошибок регистрации, выхода из системы, отображения платежей и редактирования организации
🔧 Улучшения
- [#243](https://github.com/coopenomics/mono/issues/243): Контроль прав доступа на получении документов пайщика
- [#242](https://github.com/coopenomics/mono/issues/242): Бесконечный скролл на документах пайщика и кооператива
- [#241](https://github.com/coopenomics/mono/issues/241): Раздел "Документы" для пайщика
- [#240](https://github.com/coopenomics/mono/issues/240): Множественные подписи на одном документе
- [#239](https://github.com/coopenomics/mono/issues/239): Последовательные методы подписи протокола общего собрания
- [#237](https://github.com/coopenomics/mono/issues/237): Мобильная вёрстка на страницах стола совета
- [#236](https://github.com/coopenomics/mono/issues/236): Пересобран лендинг для MONO
- [#235](https://github.com/coopenomics/mono/issues/235): Настроен флоу гитхаб-релизов с описаниями
- [#226](https://github.com/coopenomics/mono/issues/226): Встроено редактирование пайщиков
- [#224](https://github.com/coopenomics/mono/issues/224): Добавлено ТЗ по контракту капитализации
---
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.10](https://github.com/coopenomics/mono/compare/v2.2.9...v2.2.10) (2025-03-27)
### Bug Fixes
* add Zeus type ([7bcb6e3](https://github.com/coopenomics/mono/commit/7bcb6e30a77b0ab89c5293188b58f08f19c8761e))
## [2.2.9](https://github.com/coopenomics/mono/compare/v2.2.8...v2.2.9) (2025-03-12)
**Note:** Version bump only for package monocoop
## [2.2.8](https://github.com/coopenomics/monocoop/compare/v2.2.7...v2.2.8) (2025-02-10)
**Note:** Version bump only for package monocoop
## [2.2.7](https://github.com/coopenomics/monocoop/compare/v2.2.6...v2.2.7) (2025-02-07)
**Note:** Version bump only for package monocoop
## [2.2.6](https://github.com/coopenomics/monocoop/compare/v2.2.6-alpha.0...v2.2.6) (2025-01-27)
**Note:** Version bump only for package monocoop
## [2.2.5](https://github.com/coopenomics/monocoop/compare/v2.2.4...v2.2.5) (2025-01-18)
**Note:** Version bump only for package monocoop
## [2.2.4](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.4) (2025-01-17)
**Note:** Version bump only for package monocoop
## [2.2.3](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.3) (2025-01-16)
**Note:** Version bump only for package monocoop
## [2.2.1](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.1) (2025-01-14)
**Note:** Version bump only for package monocoop
## [2.1.9](https://github.com/coopenomics/monocoop/compare/v2.1.8...v2.1.9) (2025-01-14)
**Note:** Version bump only for package monocoop
## [2.1.8](https://github.com/coopenomics/monocoop/compare/v2.1.6...v2.1.8) (2024-12-24)
**Note:** Version bump only for package monocoop
## [2.1.7](https://github.com/coopenomics/monocoop/compare/v2.1.6...v2.1.7) (2024-12-03)
**Note:** Version bump only for package monocoop
## [2.1.6](https://github.com/coopenomics/monocoop/compare/v2.1.5...v2.1.6) (2024-10-30)
**Note:** Version bump only for package monocoop
## [2.1.5](https://github.com/coopenomics/monocoop/compare/v2.1.4...v2.1.5) (2024-10-28)
**Note:** Version bump only for package monocoop
## [2.1.4](https://github.com/coopenomics/monocoop/compare/v2.1.4-alpha.2...v2.1.4) (2024-10-28)
**Note:** Version bump only for package monocoop
## [2.1.3](https://github.com/coopenomics/monocoop/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package monocoop
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package monocoop
# [2.1.0](https://github.com/coopenomics/monocoop/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
### Features
* запрос соглашений ([9a6a72f](https://github.com/coopenomics/monocoop/commit/9a6a72f605ba52eef2ed6f18ccee6fbed287ea00))
## [2.0.9](https://github.com/coopenomics/monocoop/compare/v2.0.8...v2.0.9) (2024-10-10)
**Note:** Version bump only for package monocoop
## [2.0.8](https://github.com/coopenomics/monocoop/compare/v2.0.7...v2.0.8) (2024-10-09)
**Note:** Version bump only for package monocoop
## [2.0.7](https://github.com/coopenomics/monocoop/compare/v2.0.6...v2.0.7) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.6](https://github.com/coopenomics/monocoop/compare/v2.0.5...v2.0.6) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.5](https://github.com/coopenomics/monocoop/compare/v2.0.5-alpha.0...v2.0.5) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.2](https://github.com/coopenomics/monocoop/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
**Note:** Version bump only for package monocoop
# 2.1.0 (2024-09-29)
### Bug Fixes
* **importers:** исправлена ошибка в импорте модуля @wharfkit/contract ([bf89032](https://github.com/coopenomics/monocoop/commit/bf89032d8f66444804a2521f4eb96ffc75b0f605))
* **terminal:** исправлен шрифт в README ([889447a](https://github.com/coopenomics/monocoop/commit/889447a93ceadb577613ddb5b1cb2ef1cc3d54b6))
* **terminal:** исправлено описание проекта ([989b318](https://github.com/coopenomics/monocoop/commit/989b3180ded99a871e018cf26e6c493449223c01))
* **terminal:** fix typo in README.md ([fdf9996](https://github.com/coopenomics/monocoop/commit/fdf999619d2d69e960b062fe6815f5b057c95f48))
### Features
* добавлен docker-compose.yaml ([6a46697](https://github.com/coopenomics/monocoop/commit/6a46697c9d6cc3cde14dbce8f70997c00f9850de))
* **package:** добавлен скрипт gpt-commit для удобного коммита ([e0b5107](https://github.com/coopenomics/monocoop/commit/e0b510799bb0ac68890d572deb652beefd0651c4))
* **terminal:** добавлена поддержка новых команд ([73e4ca2](https://github.com/coopenomics/monocoop/commit/73e4ca226acebcbea3ae59a62def99d86efb1353))
+39
View File
@@ -0,0 +1,39 @@
k# Conventional Commits
### Основные типы коммитов:
- **`feat`**: Добавление новой функциональности (feature).
- Пример: `feat: добавлена возможность загрузки файлов`
- **`fix`**: Исправление бага.
- Пример: `fix: исправлена ошибка отображения кнопки на мобильных устройствах`
- **`chore`**: Изменения, не влияющие на исходный код (например, обновление зависимостей или инструментария).
- Пример: `chore: обновление зависимостей`
- **`docs`**: Изменения в документации.
- Пример: `docs: обновлено руководство пользователя`
- **`style`**: Изменения стиля кода, не влияющие на его функциональность (форматирование, пробелы, и т.д.).
- Пример: `style: исправлены отступы в коде`
- **`refactor`**: Изменения в коде, которые не исправляют баги и не добавляют новую функциональность.
- Пример: `refactor: улучшена структура класса`
- **`perf`**: Изменения, направленные на улучшение производительности.
- Пример: `perf: оптимизирована работа с массивами`
- **`test`**: Добавление или изменение тестов.
- Пример: `test: добавлен тест для проверки функции отправки сообщений`
### Дополнительные типы коммитов:
- **`build`**: Изменения, связанные с процессом сборки или зависимостями.
- Пример: `build: обновление конфигурации Webpack`
- **`ci`**: Изменения, касающиеся настроек непрерывной интеграции (например, Travis, Jenkins).
- Пример: `ci: настройка Travis для автоматических сборок`
- **`revert`**: Откат на предыдущие изменения.
- Пример: `revert: откат коммита e0b5107`
+49
View File
@@ -0,0 +1,49 @@
FROM node:22-slim AS builder
WORKDIR /app
# Сразу копируем все файлы
COPY . .
# Устанавливаем инструменты
RUN npm install -g pnpm lerna
# Установка зависимостей
# Используем версию pnpm, совместимую с существующим lock-файлом
RUN pnpm install
# Установка системных зависимостей для WeasyPrint и диагностических утилит (Debian/Ubuntu версии)
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-venv \
gcc \
g++ \
python3-dev \
libpango-1.0-0 \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
libcairo2 \
libcairo2-dev \
libffi-dev \
shared-mime-info \
zlib1g-dev \
libjpeg-dev \
libopenjp2-7-dev \
procps \
wget \
&& python3 -m venv /venv \
&& /venv/bin/pip install WeasyPrint==67 \
&& rm -rf /var/lib/apt/lists/*
# Сборка всех компонентов
RUN lerna run build
# Финальный образ
FROM builder AS runtime
# Настройка переменных окружения
ENV PATH="/venv/bin:$PATH"
# Проверка WeasyPrint
RUN weasyprint --version
+119 -3
View File
@@ -1,4 +1,120 @@
# MONOCOOP
# Цифровой Кооператив
Моно-репозиторий компонент Цифрового Кооператива.
<!-- badges -->
![License](https://img.shields.io/badge/license-BY--NC--SA%204.0-blue)
![Node](https://img.shields.io/badge/node-%3E%3D20-green)
![pnpm](https://img.shields.io/badge/pnpm-9-orange)
Платформа «Цифровой Кооператив» — комплексное программное обеспечение для управления кооперативными организациями на основе блокчейна EOSIO. Система обеспечивает полный цикл управления кооперативом: от регистрации пайщиков и электронного документооборота до проведения собраний и финансового учёта. Построена на принципах прозрачности, децентрализации и простой электронной подписи.
Проект является частью экосистемы [Кооперативная Экономика](https://coopenomics.world).
## Архитектура
| Компонент | Пакет | Описание |
|-----------|-------|----------|
| [boot](components/boot) | `@coopenomics/boot` | CLI для инициализации и управления блокчейн-инфраструктурой |
| [cleos](components/cleos) | `@coopenomics/cleos` | Утилита командной строки для работы с блокчейн-кошельком |
| [contracts](components/contracts) | `@coopenomics/contracts` | Смарт-контракты EOSIO на C++ |
| [controller](components/controller) | `@coopenomics/controller` | GraphQL API сервер (NestJS) |
| [cooptypes](components/cooptypes) | `cooptypes` | Общие типы и интерфейсы блокчейн-контрактов |
| [desktop](components/desktop) | `@coopenomics/desktop` | Рабочий стол кооператива (Vue 3 + Quasar) |
| [factory](components/factory) | `@coopenomics/factory` | Генератор юридических документов |
| [migrator](components/migrator) | `migrator` | Утилита миграции данных |
| [notifications](components/notifications) | `@coopenomics/notifications` | Библиотека уведомлений на основе Novu |
| [parser](components/parser) | `@coopenomics/parser` | Индексатор блокчейна через State History Plugin |
| [sdk](components/sdk) | `@coopenomics/sdk` | TypeScript SDK для GraphQL API |
| [setup](components/setup) | `@coopenomics/setup` | Мастер первоначальной настройки |
## Быстрый старт
### Предварительные требования
- Node.js >= 20
- pnpm 9
- Docker и Docker Compose
- [WeasyPrint](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation) (для генерации PDF)
### Установка
```bash
pnpm install
```
### Конфигурация
```bash
pnpm run setup
```
Интерактивный мастер создаст необходимые `.env` файлы для всех компонентов.
### Запуск инфраструктуры
```bash
docker compose up -d
pnpm run reboot
```
## Разработка
### Бэкенд (controller + parser)
```bash
pnpm run dev:backend
```
### Фронтенд (desktop)
```bash
pnpm run dev:desktop
```
### Библиотеки (factory + cooptypes)
```bash
pnpm run dev:lib
```
### Все сервисы одновременно
```bash
pnpm run dev:all
```
> **Примечание:** установка пакетов производится только через фильтр: `pnpm add <пакет> --filter <компонент>`
## Тестирование
```bash
# Все тесты
pnpm run test
# Юнит-тесты (cooptypes, parser, notifications)
pnpm run test:unit
# Компонентные тесты (factory)
pnpm run test:component
# Интеграционные тесты (boot + blockchain)
pnpm run test:integration
```
## Сборка
```bash
# Библиотеки (cooptypes, factory)
pnpm run build:lib
# Смарт-контракты
pnpm run build:contracts:all
# Desktop (SSR)
pnpm --filter @coopenomics/desktop run build
```
## Лицензия
Продукт Потребительского Кооператива «ВОСХОД» распространяется по лицензии [BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru).
Разрешено делиться, копировать и распространять материал, адаптировать и создавать производные произведения при условии указания авторства и сохранения той же лицензии. Коммерческое использование запрещено.
+129
View File
@@ -0,0 +1,129 @@
# 📋 Резюме создания компонентов Notifications
## ✅ Что создано
### 1. 📚 Библиотека `@monocoop/notifications`
**Расположение:** `components/notifications/`
**Функции:**
- ✅ Типизированные интерфейсы для workflow с Zod
- ✅ Builder паттерн для создания workflow
- ✅ Базовые шаблоны для email, in-app, push, SMS
- ✅ Автоматическая конвертация Zod схем в JSON Schema для Novu
- ✅ Экспорт всех workflow для использования в других пакетах
**Структура:**
```
src/
├── types/ # Базовые типы и интерфейсы
├── base/ # Утилиты и настройки по умолчанию
├── workflows/ # Папки с workflow
│ └── welcome/ # Пример приветственного workflow
└── index.ts # Главный экспорт
```
### 2. 🚀 NestJS приложение `@monocoop/notificator2`
**Расположение:** `components/notificator2/`
**Функции:**
- ✅ Автоматический upsert всех workflow в Novu при запуске
- ✅ RESTful API для триггера уведомлений
- ✅ Типизированная валидация payload
- ✅ Health check endpoints
- ✅ Использует библиотеку notifications для типов
**API Endpoints:**
- `GET /api/notifications/health` - Health check
- `GET /api/notifications/workflows` - Список workflow
- `POST /api/notifications/trigger` - Универсальный триггер
- `POST /api/notifications/trigger/welcome` - Триггер welcome workflow
- `POST /api/notifications/workflows/upsert-all` - Принудительный upsert
## 🔧 Как использовать
### 1. Настройка библиотеки notifications
```bash
cd components/notifications
pnpm install # или npm install
pnpm build # для компиляции TypeScript
```
### 2. Настройка notificator2
```bash
cd components/notificator2
pnpm install # или npm install
# Настройка окружения
cp .env.example .env
# Добавить NOVU_API_KEY в .env
# Запуск
pnpm start:dev
```
### 3. Добавление нового workflow
**В библиотеке notifications:**
```typescript
// components/notifications/src/workflows/order/order-workflow.ts
export const orderWorkflow = WorkflowBuilder
.create<OrderPayload>()
.name('Order Confirmation')
.workflowId('order-confirmation')
.payloadSchema(orderPayloadSchema)
.addSteps([...])
.build();
```
**Регистрация в index.ts:**
```typescript
// components/notifications/src/workflows/index.ts
import { orderWorkflow } from './order';
export const allWorkflows = [
welcomeWorkflow,
orderWorkflow, // ← добавить новый workflow
];
```
### 4. Использование API
**Триггер welcome workflow:**
```bash
curl -X POST http://localhost:3000/api/notifications/trigger/welcome \
-H "Content-Type: application/json" \
-d '{
"subscriberId": "user-123",
"email": "user@example.com",
"payload": {
"userName": "Иван Иванов",
"userEmail": "user@example.com",
"age": 25
}
}'
```
## 🎯 Преимущества архитектуры
1. **Типобезопасность** - Zod схемы обеспечивают валидацию на уровне TypeScript и runtime
2. **Разделение ответственности** - Библиотека содержит только типы, сервер - логику
3. **Расширяемость** - Легко добавлять новые workflow
4. **Автоматизация** - Workflow автоматически синхронизируются с Novu
5. **Переиспользование** - Типы можно использовать в других частях системы
## 🔄 Workflow при запуске
1. **Запуск notificator2**
2. **Чтение всех workflow из библиотеки**
3. **Проверка существования в Novu**
4. **Создание/обновление workflow**
5. **Готов к приёму запросов на триггеры**
## 📁 Структура как в testFramework2.ts
Вся логика из `testFramework2.ts` была перенесена в structured архитектуру:
-`buildWorkflowData``WorkflowBuilder`
-`baseSteps``createEmailStep`, `createInAppStep`, etc.
-`upsertWorkflow``NovuService.upsertWorkflow`
-`triggerWorkflow``NovuService.triggerWorkflow`
- ✅ Типизация payload → Zod схемы + TypeScript типы
+55
View File
@@ -0,0 +1,55 @@
# TASKS.md — Прогресс выполнения задач
## Завершённые задачи
### 1-6. Предыдущие задачи (см. git history)
- ✅ Dev-окружение, Security, Тесты, README, AGENTS.md, Setup
- ✅ Поисковая система документов (OpenSearch)
- ✅ Процессы (Capital extension)
---
## Текущая задача: Генерация отчётов ФНС (расширение reports)
### Документы ФНС для генерации:
1. **6-НДФЛ** — ежеквартально (XSD: NO_NDFL6.2)
2. **4-ФСС (ЕФС-1)** — ежеквартально
3. **РСВ** — ежеквартально (XSD: NO_RASCHSV)
4. **ПСВ** — ежемесячно (XSD: NO_PERSSVFL)
5. **Бухгалтерский баланс** — ежегодно (XSD: NO_BUHOTCH) — КЛЮЧЕВОЙ
6. **ДУСН** — декларация УСН ежегодно (XSD: NO_USN)
7. **Уведомление о страховых взносах** — ежемесячно с 2026 (XSD: UT_UVISCHSUMNAL)
8. **УУСН** — уведомление УСН
### 10. Генерация отчётов ФНС (доработка) ✅
- [x] Фабрика генераторов (ReportRegistryService)
- [x] 8 генераторов (Бухбаланс, 6-НДФЛ, РСВ, ПСВ, ДУСН, 4-ФСС, Увед. взносы, УУСН)
- [x] GraphQL API (getAvailableReports, generateReport)
- [x] Генераторы переписаны по XSD — структура соответствует схемам ФНС
- [x] 48 unit-тестов для всех генераторов
- [x] Desktop UI (страница отчётов) — расширение reports
- [x] Интеграция с реальными данными ledger через LedgerInteractor
- [x] OrganizationDataInput DTO для передачи данных организации
### Архитектура:
- Расширение `reports` в `components/controller/src/extensions/`
- Фабрика XML отчётов: на вход данные за период → на выходе XML
- Валидация по XSD схемам
- Desktop UI: магазин приложений → установка → рабочий стол отчётов
### Подзадачи:
- [x] **8.1 Исследование**: Все XSD разобраны, format.nalog.ru изучен
- [x] **8.2 Инфраструктура**: Расширение reports, ReportRegistryService (фабрика)
- [x] **8.3 XML генератор**: Фабричный подход — IReportGenerator interface
- [x] **8.4 Бухбаланс**: BuhotchGenerator — счета 51, 80, 86 из ledger
- [x] **8.5 6-НДФЛ**: Ndfl6Generator — нулевая
- [x] **8.6 4-ФСС**: Zero generator — нулевая
- [x] **8.7 РСВ**: Zero generator — нулевая
- [x] **8.8 ПСВ**: Zero generator — нулевая
- [x] **8.9 ДУСН**: Zero generator — нулевая
- [x] **8.10 Уведомление о взносах**: Zero generator — нулевая
- [x] **8.11 УУСН**: Zero generator — нулевая
- [x] **8.12 GraphQL API**: getAvailableReports + generateReport
- [ ] **8.13 XSD валидация + тесты**: Проверка по схемам
- [ ] **8.14 Desktop UI**: Страница отчётов в магазине приложений
- [ ] **8.15 Ledger интеграция**: Реальные данные из ledger_operations
+64
View File
@@ -0,0 +1,64 @@
# Test Plan — pnpm run test
## Архитектура
```
pnpm run test
├── cooptypes — vitest run (smoke tests exports) ✅ 4/4
├── parser — vitest run (config smoke tests) ✅ 3/3
├── factory — vitest run (document generation tests) 🔧 17/85 → need mocks
├── sdk — vitest run (API integration tests) 🔧 TODO
├── notifications — vitest run (workflow tests) 🔧 TODO
├── controller — jest / vitest (NestJS unit tests) 🔧 TODO
└── boot — vitest run (blockchain integration) 🔧 53/60
```
## Статус по компонентам
### cooptypes ✅ DONE
- 4 smoke-теста экспортов
- Не требует инфраструктуры
### parser ✅ DONE
- 3 smoke-теста конфигурации
- Не требует инфраструктуры
### factory 🔧 IN PROGRESS
- **Проблема**: тесты обращаются к parser API (`SIMPLE_EXPLORER_API`) для get-tables/get-actions
- **Решение**: Уже есть мок-система в `src/Utils/testMocks.ts` + `matchMock.ts`
- **Нужно**: Добавить моки для ВСЕХ документов:
- [ ] cooperative data mock (registrator.coops table)
- [ ] soviet boards mock — уже есть через test setup в MongoDB
- [ ] draft templates mock (draft.drafts + draft.translations tables)
- [ ] decision data mocks (soviet.decisions table)
- [ ] Мок для ReturnByMoney документов
- [ ] Все документы 1000+ серии
- **Текущие рабочие моки**: meet tables, votefor actions, returnByMoneyDecision actions
- **После мокирования**: все 85 тестов должны проходить
### boot 🔧 NEEDS REBOOT
- capital.test — 53/60 тестов проходят (после чистого reboot)
- wallet.test — нужен полный boot с agreements
- registrator.test — нужен полный boot
- capital-import.test — отдельный тест импорта
- **Требует**: pnpm run reboot перед запуском
### sdk 🔧 TODO
- 1 тест файл с login + fetch extensions
- Требует запущенный controller
- Нужно: обновить chain_id, api_url, credentials
### controller 🔧 TODO
- Все старые тесты удалены (устаревшие)
- NestJS-приложение — нужны тесты через @nestjs/testing
- Минимум: unit-тесты domain-логики, smoke-тест GraphQL API
### notifications 🔧 TODO
- Нет тестов
- Нужно: smoke-тесты workflow builder
### desktop — SKIP (нет тестов, UI-тестирование)
## Root script требования
- `pnpm run test` — запускает ВСЕ тесты
- fail-fast: если один пакет падает — весь pipeline падает
- Последовательный запуск (не параллельный)
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
# @coopenomics/blago-cli
CLI синхронизации артефактов Благорост (проекты, задачи, требования) с бэкендом через `@coopenomics/sdk`.
## Базовый каталог и корень копии
Порядок выбора **базового каталога**:
1. **`--dir <path>`** (или **`--dir=<path>`**) — можно указать **в любом месте** командной строки, до или после подкоманды, например `blago pull --dir ~/results` или `blago --dir ~/results pull`.
2. Переменная окружения **`BLAGO_WORKSPACE`**.
3. Текущий рабочий каталог (**cwd**).
**Корень рабочей копии** — каталог, в котором (или выше по дереву от базового каталога) лежит `.blago/config.json`. Поиск идёт вверх от базы, пока не найден файл.
Команда **`blago init [directory]`** создаёт `.blago` в `path.resolve(база, directory)`; по умолчанию `[directory]``.`.
## Справка по командам
```text
blago --help
blago <команда> --help
```
У подкоманд в help выводится блок **Global Options** (в том числе `--dir` и версия), если смотрите справку не с корневого уровня.
В конце help добавляется строка **текущей сессии** (активная среда и пользователь из сохранённого `blago login`), если найдена копия.
## Прочее
- Форматы сущностей и коммиты в репозитории кода: в корне копии **`blago docs`** → `BLAGO-FORMATS.md`, `BLAGO-COMMITS.md`.
- Артефакты для агентов: **`blago skills install`** → целиком `~/.claude/skills/blago/ai/` (скилл `skills/cli/SKILL.md`, каталог `commands/` и т.д.).
@@ -0,0 +1,77 @@
---
name: blago-cli
description: |
Синхронизация задач (issue) и требований (story) в копии Благорост через blago CLI — pull, правки, add, push, черновики create, конфликты по updated_at.
Триггеры: blago, Capital, Благорост, issue.md, story, requirements, staging, push/pull, YAML frontmatter задач.
---
# blago CLI — задачи и требования
Корень копии: `.blago/config.json` вверх от базового каталога. База: `blago --dir <path> …` (в любом месте строки, приоритет над `BLAGO_WORKSPACE`), иначе `BLAGO_WORKSPACE`, иначе cwd. Сетевые команды — готовая сессия у оператора.
В установке путь к этому скиллу: **`…/ai/skills/cli/SKILL.md`**. Каталог **`ai/commands/`** (рядом с `skills/` в дереве `ai/`) — slash-команды и сценарии; в **`ai/skills/`** при необходимости — другие скиллы.
## Поток данных
| Действие | Команды |
|----------|---------|
| Забрать с сервера | `blago pull` |
| Увидеть staging и расхождения с индексом | `blago status`, `blago diff` |
| Пометить изменённые .md к отправке | `blago add <пути…>` (каталог — рекурсия по .md) |
| Отправить | `blago push` (только staging) |
| Убрать из staging | `blago remove <пути…>` или `blago remove --all` |
| Перезаписать файл с сервера | `blago restore <относительный-путь>` |
Цепочка после правок: `add``push`. `add` берёт только файлы, изменённые относительно `.blago/index.json`, и новые без записи в индексе.
## Конфликт версий
Если push падает с **другой версией на сервере (updated_at)**:
1. `blago pull`
2. Вручную свести содержимое и frontmatter с актуальным сервером (не трогать `hash` у существующих сущностей без понимания последствий)
3. `blago add …``blago push`
Локальный откат одного файла к серверу: `blago restore <path>`.
## Создание
| Сущность | Команда |
|----------|---------|
| Черновик задачи | `blago create issue <basePath> "<title>"``basePath`: каталог проекта/компонента или путь к `project.md` / `component.md` |
| Черновик требования (story) | `blago create req <basePath> "<title>"` — опции `--format markdown\|mermaid\|drawio\|bpmn` |
Черновик попадает в staging; первый `push` создаёт сущность на сервере. Опции `--set-self`, `--creators`, `--submaster` (issue) / `--set-self` (req) — см. `blago create … --help`.
## Обновление существующих
1. После `pull` править тело Markdown и допустимые поля frontmatter (см. ниже). **Поле `hash` у уже синхронизированных сущностей не менять.**
2. `blago add <файл>``blago push`.
Тип **result** через этот CLI не синхронизируется.
## Frontmatter (YAML между `---` в начале файла)
Общее: все сущности — Markdown + YAML; `hash` — стабильный id сущности на сервере.
### `project` — `…/project.md` или `…/component.md`
Порядок полей: `type`, `id`, `title`; у компонента сразу `parent_title`, `parent_hash`; далее `coopname`, `status`, `hash`, `created_at`, `updated_at` (ISO-8601). Тело — описание.
### `issue` — `issues/<id>-<slug>.md`
Порядок: `type`, `id`, `title`, `project_title`, `component_title`, `status`, `priority`, `estimate`, `creators`, `labels`, опционально `cycle_id`, `submaster`, затем `hash`, `project_hash`, даты. Поля `created_by` и `sort_order` в файле обычно не задаются (при push `sort_order` может уйти как 0). Тело — описание задачи.
### `story` (требование) — `requirements/<prefix>-<slug>.md` или под `issues/…/requirements/…`
`type: story`, при наличии серверного id — поле `id`; далее `title`, `hash`, `content_format` (например `MARKDOWN`), `status`, `created_by`, `sort_order`, привязка `project_hash` и/или `issue_hash`. Тело — текст требования.
## Коммиты в Git репозитория кода (не blago)
В сообщении указывать минимум: **username** участника и **hash задачи** из frontmatter issue.
Примеры: `ant CAPITAL-42 правка API`, `ant | проект x | задача CAPITAL-12 | фикс`.
## Справка в дереве пакета
Полные тексты форматов: `blago docs` в корне копии пишет `BLAGO-FORMATS.md` и `BLAGO-COMMITS.md`.
+10
View File
@@ -0,0 +1,10 @@
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: ['src/index'],
declaration: true,
clean: true,
rollup: {
emitCJS: false,
},
})
+21
View File
@@ -0,0 +1,21 @@
// @ts-check
import antfu from '@antfu/eslint-config'
export default antfu(
{
type: 'lib',
rules: {
'eslint-comments/no-unlimited-disable': 'off',
'no-console': 'off',
'node/prefer-global/buffer': 'off',
'node/prefer-global/process': 'off',
'perfectionist/sort-named-imports': 'off',
'regexp/no-obscure-range': 'off',
'sort-imports': 'off',
'ts/explicit-function-return-type': 'off',
},
},
{
ignores: ['dist/**', 'package.json', 'tsconfig.json'],
},
)
+51
View File
@@ -0,0 +1,51 @@
{
"name": "@coopenomics/blago-cli",
"version": "2026.4.6-4",
"description": "CLI синхронизации артефактов Благорост с бэкендом через @coopenomics/sdk",
"type": "module",
"private": true,
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
"license": "MIT",
"bin": {
"blago": "./dist/index.mjs"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"files": [
"dist",
"ai",
"README.md"
],
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings 0",
"test": "echo \"No tests yet\" && exit 0"
},
"dependencies": {
"@coopenomics/sdk": "workspace:*",
"commander": "^12.1.0",
"cooptypes": "workspace:*",
"gray-matter": "^4.0.3",
"picocolors": "^1.1.1",
"yaml": "^2.6.0"
},
"devDependencies": {
"@antfu/eslint-config": "^3.3.2",
"@types/node": "^22.5.4",
"eslint": "^9.9.1",
"typescript": "^5.9.3",
"unbuild": "^2.0.0"
},
"engines": {
"node": ">=20"
}
}
+353
View File
@@ -0,0 +1,353 @@
// Commander: подкоманды; базовый каталог — --dir, иначе BLAGO_WORKSPACE, иначе cwd; корень копии — поиск .blago/config.json вверх.
import * as path from 'node:path'
import { Command } from 'commander'
import pkg from '../../package.json'
import {
type BlagoConfigFile,
type BlagoRemoteProfile,
getActiveProfile,
initBlagoWorkspace,
loadConfig,
saveConfig,
} from '../config/index.js'
import { findBlagoRoot } from '../config/paths.js'
import { peelBlagoDirFromArgv, resolveBlagoStartDir, setBlagoCliExplicitStartDir } from '../config/start-dir.js'
import { runCreateIssue } from '../create/run-create-issue.js'
import { runCreateStory } from '../create/run-create-story.js'
import { writeLlmDocs } from '../llm-docs/index.js'
import { applySession, createClient, ensureAuthenticatedContext, loginInteractive, promptLine } from '../session/index.js'
import {
describeBlagoSessionLine,
formatBlagoSessionStatusHelpExtra,
readSessionUsernameSync,
} from '../session/status-text.js'
import { runAdd } from '../sync/add.js'
import { runClean } from '../sync/clean.js'
import { runDiff } from '../sync/diff.js'
import { normalizeRelativePath } from '../sync/index-store.js'
import { runPull } from '../sync/pull.js'
import { runPush } from '../sync/push.js'
import { runClearStaging, runRemove } from '../sync/remove.js'
import { runRestore } from '../sync/restore.js'
import { runStatus } from '../sync/status.js'
import { error, formatThrownValue, info, success, warn } from '../ui/output.js'
import { runSkillsInstall } from './run-skills-install.js'
function startDir(): string {
return resolveBlagoStartDir()
}
function requireRoot(): string {
const root = findBlagoRoot(startDir())
if (!root) {
throw new Error(
'Не найдена рабочая копия blago (.blago/config.json). Задайте «blago --dir <каталог> …», переменную BLAGO_WORKSPACE или выполните «blago init» в корне проекта.',
)
}
return root
}
export async function runCli(originalArgv: string[]): Promise<void> {
const { argv: argvForCommander, dir: dirFromArgv } = peelBlagoDirFromArgv(originalArgv)
setBlagoCliExplicitStartDir(dirFromArgv)
const program = new Command()
.name('blago')
.description('Синхронизация проектов, задач и требований Благорост с бэкендом (GraphQL SDK). Подробности: README пакета.')
.configureHelp({ showGlobalOptions: true })
.version(pkg.version)
.option(
'--dir <path>',
'базовый каталог: поиск .blago/config.json вверх и относительные пути; выше приоритета, чем BLAGO_WORKSPACE; допускается до или после подкоманды',
)
.addHelpText('afterAll', () => formatBlagoSessionStatusHelpExtra())
program
.command('init')
.description('Создать .blago/config.json в текущем или указанном каталоге')
.argument('[directory]', 'каталог для инициализации', '.')
.option(
'--coopname <name>',
'записать это имя кооператива во все среды в config (потом можно развести по-разному в JSON)',
)
.option('--force', 'перезаписать config.json значениями по умолчанию')
.option('--with-llm-docs', 'создать BLAGO-FORMATS.md и BLAGO-COMMITS.md в корне')
.action(async (directory: string, opts: { coopname?: string, force?: boolean, withLlmDocs?: boolean }) => {
const base = path.resolve(startDir(), directory)
await initBlagoWorkspace(base, { coopname: opts.coopname, force: opts.force })
if (opts.withLlmDocs) {
await writeLlmDocs(base)
}
success(`Инициализировано: ${path.join(base, '.blago')}`)
})
const createCmd = program
.command('create')
.description(
'Локальный черновик задачи или требования (без сети). Файл попадает в staging; первый push — мутация create на сервере.',
)
createCmd
.command('issue')
.description('Создать файл задачи (type: issue) и запись в .blago/pending-create.json')
.argument('<basePath>', 'каталог проекта/компонента или путь к project.md / component.md')
.argument('<title>', 'заголовок')
.option(
'--set-self',
'добавить username из сессии активной среды первым в creators (нужен blago login); created_by не задаётся',
)
.option(
'--creators <list>',
'список создателей через запятую; с --set-self — после текущего пользователя; без флагов — creators: []',
)
.option('--submaster <username>', 'явный submaster (только с этим флагом)')
.action(
async (
basePath: string,
title: string,
opts: { setSelf?: boolean, creators?: string, submaster?: string },
) => {
const root = requireRoot()
const cfg = await loadConfig(root)
const { relativePath } = await runCreateIssue(root, cfg, basePath, title, {
setSelf: opts.setSelf,
creatorsCsv: opts.creators,
submaster: opts.submaster,
})
success(`Создан черновик задачи: ${relativePath} (добавлено в staging)`)
},
)
createCmd
.command('req')
.alias('requirement')
.description('Создать файл требования / story (type: story)')
.argument('<basePath>', 'каталог проекта/компонента или путь к project.md / component.md')
.argument('<title>', 'заголовок')
.option(
'--set-self',
'заполнить created_by username из сессии активной среды (нужен blago login)',
)
.option(
'--format <name>',
'содержимое: markdown | mermaid | drawio | bpmn',
'markdown',
)
.action(
async (basePath: string, title: string, opts: { setSelf?: boolean, format?: string }) => {
const root = requireRoot()
const cfg = await loadConfig(root)
const { relativePath } = await runCreateStory(root, cfg, basePath, title, {
setSelf: opts.setSelf,
format: opts.format,
})
success(`Создан черновик требования: ${relativePath} (добавлено в staging)`)
},
)
program
.command('login')
.description('Интерактивный вход (email + WIF); сохраняет сессию для активной среды')
.action(async () => {
const root = requireRoot()
const cfg = await loadConfig(root)
const profile = getActiveProfile(cfg)
const client = createClient(profile)
const session = await loginInteractive(client, root, cfg.activeEnv)
await applySession(client, session)
success('Вход выполнен, сессия сохранена в .blago/')
})
program
.command('pull')
.description('Скачать с сервера проекты, задачи и требования (coopname в config или из blago init)')
.action(async () => {
const root = requireRoot()
const cfg = await loadConfig(root)
const ctx = await ensureAuthenticatedContext(root, cfg)
await runPull(ctx)
success('pull завершён')
})
program
.command('add')
.description(
'Добавить в staging только изменённые относительно индекса .md (или без записи в индексе); каталог рекурсивно',
)
.argument('<paths...>', 'пути относительно корня рабочей копии')
.action(async (paths: string[]) => {
const root = requireRoot()
const { stagedPaths, skippedUnchanged, skippedIgnored } = await runAdd(root, paths)
success(
`Staging обновлён: в списке ${stagedPaths.length} путь(ей). Пропущено без изменений относительно индекса: ${skippedUnchanged}; по .blagoignore: ${skippedIgnored}.`,
)
})
program
.command('remove')
.alias('rm')
.description('Убрать файлы .md из staging (каталог — рекурсивно все .md внутри)')
.option('-a, --all', 'очистить staging целиком')
.argument('[paths...]', 'пути относительно корня рабочей копии')
.action(async (paths: string[], opts: { all?: boolean }) => {
const root = requireRoot()
if (opts.all) {
await runClearStaging(root)
success('Staging очищен (--all).')
return
}
const { removedCount, remainingPaths, notStagedCount } = await runRemove(root, paths)
success(
`Из staging убрано путей: ${removedCount}. Осталось в списке: ${remainingPaths.length}. Не было в staging: ${notStagedCount}.`,
)
})
program
.command('restore')
.description('Восстановить один .md с сервера по пути из индекса (перезапись файла и обновление индекса)')
.argument('<path>', 'относительный путь к файлу, например 2-proekt-2/project.md')
.action(async (filePath: string) => {
const root = requireRoot()
const cfg = await loadConfig(root)
const ctx = await ensureAuthenticatedContext(root, cfg)
await runRestore(ctx, filePath)
success(`Восстановлено с сервера: ${normalizeRelativePath(filePath)}`)
})
program
.command('push')
.description('Отправить staged файлы на сервер (проверка версии, без тихой перезаписи)')
.action(async () => {
const root = requireRoot()
const cfg = await loadConfig(root)
const ctx = await ensureAuthenticatedContext(root, cfg)
await runPush(ctx)
success('push завершён')
})
program
.command('status')
.description('Показать staging и файлы с локальными изменениями относительно индекса')
.action(async () => {
const root = requireRoot()
await runStatus(root)
})
program
.command('diff')
.description('Файлы, где содержимое разошлось с индексом: SHA256 и превью строк (не сравнение с сервером)')
.action(async () => {
const root = requireRoot()
await runDiff(root)
})
program
.command('clean')
.description(
'Удалить верхнеуровневые каталоги проектов из индекса (целиком) и обнулить index/staging; .blago не трогает',
)
.action(async () => {
const root = requireRoot()
const line = await promptLine(
'Будут удалены каталоги проектов (по индексу) и очищены index/staging. Введите yes для подтверждения: ',
)
if (line.trim().toLowerCase() !== 'yes') {
warn('Отменено.')
return
}
await runClean(root)
success('Индекс и staging очищены, каталоги проектов удалены.')
})
const envCmd = program
.command('env')
.description(
'Управление именованными средами (URL API и блокчейна). Без подкоманды — показать активную среду и сессию.',
)
envCmd
.command('use')
.description('Сделать среду активной')
.argument('<name>', 'имя среды')
.action(async (name: string) => {
const root = requireRoot()
const cfg = await loadConfig(root)
if (!cfg.environments[name]) {
throw new Error(`Среда «${name}» не описана. Сначала: blago env set ${name}`)
}
const next: BlagoConfigFile = { ...cfg, activeEnv: name }
await saveConfig(root, next)
success(`Активная среда: ${name}`)
})
envCmd
.command('set')
.description('Задать параметры среды')
.argument('<name>', 'имя среды')
.requiredOption('--api-url <url>', 'URL GraphQL, например http://127.0.0.1:2998/v1/graphql')
.requiredOption('--chain-url <url>', 'URL ноды EOSIO')
.option('--chain-id <id>', 'CHAIN_ID сети')
.option('--label <text>', 'подпись для человека')
.action(async (name: string, opts: { apiUrl: string, chainUrl: string, chainId?: string, label?: string }) => {
const root = requireRoot()
const cfg = await loadConfig(root)
const prev: BlagoRemoteProfile = cfg.environments[name] ?? {
api_url: '',
chain_url: '',
chain_id: '',
}
const profile: BlagoRemoteProfile = {
api_url: opts.apiUrl,
chain_url: opts.chainUrl,
chain_id: opts.chainId ?? prev.chain_id ?? '',
label: opts.label ?? prev.label,
}
const next: BlagoConfigFile = {
...cfg,
environments: { ...cfg.environments, [name]: profile },
}
await saveConfig(root, next)
success(`Среда «${name}» обновлена`)
})
envCmd.action(async () => {
const root = requireRoot()
const cfg = await loadConfig(root)
info(describeBlagoSessionLine(cfg, readSessionUsernameSync(root, cfg.activeEnv)))
info(`Среды в config: ${Object.keys(cfg.environments).sort().join(', ')}`)
})
program
.command('docs')
.description('Записать в корень копии BLAGO-FORMATS.md и BLAGO-COMMITS.md')
.action(async () => {
const root = requireRoot()
await writeLlmDocs(root)
success('Документы для LLM обновлены')
})
program
.command('skills')
.description('Скиллы для агентов (Claude и др.)')
.command('install')
.description('Скопировать ~/.claude/skills/blago/ai/ из пакета (skills/cli/SKILL.md, commands/, …; каталог целиком)')
.action(async () => {
const { dest } = await runSkillsInstall()
success(`Установлено: ${dest}`)
})
try {
await program.parseAsync(argvForCommander, { from: 'node' })
}
catch (e) {
error(formatThrownValue(e))
process.exitCode = 1
}
finally {
setBlagoCliExplicitStartDir(null)
}
}
@@ -0,0 +1,37 @@
// Копирует каталог ai/ (skills/cli/SKILL.md, commands/, …) в ~/.claude/skills/blago/ai/ целиком.
import * as fs from 'node:fs'
import * as fsp from 'node:fs/promises'
import * as os from 'node:os'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
/** dist/index.mjs → ../ai; dist/cmd/*.mjs → ../../ai; src/cmd/*.ts (stub) → ../../../ai */
const AI_DIR_REL_TO_META = ['../ai', '../../ai', '../../../ai'] as const
function bundledAiDir(): string {
for (const rel of AI_DIR_REL_TO_META) {
const dir = fileURLToPath(new URL(rel, import.meta.url))
const skillMd = path.join(dir, 'skills', 'cli', 'SKILL.md')
try {
fs.accessSync(skillMd)
return dir
}
catch {
/* следующий кандидат */
}
}
const tried = AI_DIR_REL_TO_META.map(rel => fileURLToPath(new URL(rel, import.meta.url))).join('; ')
throw new Error(
`Не найден каталог ai/ пакета blago-cli (ожидается skills/cli/SKILL.md). Проверены пути: ${tried}`,
)
}
export async function runSkillsInstall(): Promise<{ source: string, dest: string }> {
const source = bundledAiDir()
const dest = path.join(os.homedir(), '.claude', 'skills', 'blago', 'ai')
await fsp.rm(dest, { recursive: true, force: true })
await fsp.mkdir(path.dirname(dest), { recursive: true })
await fsp.cp(source, dest, { recursive: true })
return { source, dest }
}
@@ -0,0 +1,13 @@
/**
* Desktop передаёт в SDK `BACKEND_URL + '/v1/graphql'`. В config blago ожидаем тот же базовый BACKEND_URL.
*/
export function toGraphqlApiUrl(apiUrl: string): string {
const base = apiUrl.trim().replace(/\/+$/, '')
if (base.length === 0) {
return apiUrl.trim()
}
if (/\/v1\/graphql$/i.test(base) || /\/graphql$/i.test(base)) {
return base
}
return `${base}/v1/graphql`
}
+166
View File
@@ -0,0 +1,166 @@
// .blago/config.json: activeEnv, coopname, environments { api_url, chain_url, chain_id, coopname? }.
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { blagoDir, CONFIG_FILE, configPath, gitignorePath } from './paths.js'
export type BlagoEnvironmentName = 'dev' | 'testnet' | 'production' | (string & {})
/**
* Имя кооператива в шаблоне среды (новый config). Поменяйте per-env в .blago/config.json под каждую среду.
*/
export const DEFAULT_COOPNAME = 'voskhod'
export interface BlagoRemoteProfile {
/** Базовый URL контроллера (как BACKEND_URL у desktop); без суффикса к пути добавится /v1/graphql. */
readonly api_url: string
readonly chain_url: string
readonly chain_id: string
readonly label?: string
/** Short name кооператива для этой среды (фильтры Capital / pull). У разных сред может быть разное значение. */
readonly coopname?: string
}
export interface BlagoConfigFile {
/** Имя активной среды */
activeEnv: string
/**
* Запасной coopname, если у активной среды поле environments.<activeEnv>.coopname пустое (legacy / общий fallback).
*/
coopname?: string
environments: Record<string, BlagoRemoteProfile>
}
const DEFAULT_ENVS: Record<string, BlagoRemoteProfile> = {
dev: {
api_url: 'http://127.0.0.1:2998',
chain_url: 'http://127.0.0.1:8888',
chain_id: 'cae86058a6d8698833afb474ab8a5ad8599c6cf54f9ebcf275dbac7055c16fe1',
label: 'Локальная разработка',
coopname: DEFAULT_COOPNAME,
},
testnet: {
api_url: 'https://testnet.coopenomics.world/backend',
chain_url: 'https://testnet.coopenomics.world/api',
chain_id: 'f0364a3f9fd913081f1c0b05c6f8f50a59b2ba60bb928cb321ba3a9a36316624',
label: 'Testnet',
coopname: DEFAULT_COOPNAME,
},
production: {
api_url: 'https://лк.цифровой-кооператив.рф/backend',
chain_url: 'https://лк.цифровой-кооператив.рф/api',
chain_id: '6e37f9ac0f0ea717bfdbf57d1dd5d7f0e2d773227d9659a63bbf86eec0326c1b',
label: 'Production',
coopname: DEFAULT_COOPNAME,
},
}
export function defaultConfig(): BlagoConfigFile {
return {
activeEnv: 'dev',
environments: { ...DEFAULT_ENVS },
}
}
/** Эффективный coopname: сначала у активной среды, иначе верхний уровень config (fallback). */
export function resolveCoopname(cfg: BlagoConfigFile): string | undefined {
const profile = cfg.environments[cfg.activeEnv]
const fromEnv = profile?.coopname?.trim()
if (fromEnv) {
return fromEnv
}
return cfg.coopname?.trim() || undefined
}
function environmentsWithOptionalCoopname(
envs: Record<string, BlagoRemoteProfile>,
coopname: string | undefined,
): Record<string, BlagoRemoteProfile> {
if (coopname === undefined) {
return envs
}
return Object.fromEntries(
Object.entries(envs).map(([name, profile]) => [name, { ...profile, coopname }]),
) as Record<string, BlagoRemoteProfile>
}
export async function loadConfig(root: string): Promise<BlagoConfigFile> {
const raw = await fs.readFile(configPath(root), 'utf8')
const parsed = JSON.parse(raw) as BlagoConfigFile
if (!parsed.activeEnv || typeof parsed.environments !== 'object') {
throw new Error('Некорректный config.json: нужны activeEnv и environments')
}
return parsed
}
export async function saveConfig(root: string, cfg: BlagoConfigFile): Promise<void> {
await fs.mkdir(blagoDir(root), { recursive: true })
await fs.writeFile(configPath(root), `${JSON.stringify(cfg, null, 2)}\n`, 'utf8')
}
const BLAGO_SESSION_GITIGNORE_LINE = 'session.*.json'
const BLAGO_GITIGNORE_SNIPPET = `# Токены blago login — не коммитить в репозиторий
${BLAGO_SESSION_GITIGNORE_LINE}
`
/** Создаёт или дополняет .blago/.gitignore, чтобы не коммитить файлы сессии. */
export async function ensureBlagoGitignore(root: string): Promise<void> {
const p = gitignorePath(root)
let existing = ''
try {
existing = await fs.readFile(p, 'utf8')
}
catch {
/* файла ещё нет */
}
if (existing.includes(BLAGO_SESSION_GITIGNORE_LINE)) {
return
}
const body = existing.trim().length > 0
? `${existing.replace(/\s+$/, '')}\n\n${BLAGO_GITIGNORE_SNIPPET}`
: BLAGO_GITIGNORE_SNIPPET
await fs.writeFile(p, body.endsWith('\n') ? body : `${body}\n`, 'utf8')
}
export async function initBlagoWorkspace(
root: string,
options?: { coopname?: string, force?: boolean },
): Promise<void> {
const dir = blagoDir(root)
const cfgFile = path.join(dir, CONFIG_FILE)
if (!options?.force && (await fileExists(cfgFile))) {
await ensureBlagoGitignore(root)
return
}
await fs.mkdir(dir, { recursive: true })
const cfg = defaultConfig()
const next: BlagoConfigFile = {
...cfg,
environments: environmentsWithOptionalCoopname(cfg.environments, options?.coopname),
}
await saveConfig(root, next)
await ensureBlagoGitignore(root)
}
async function fileExists(p: string): Promise<boolean> {
try {
await fs.access(p)
return true
}
catch {
return false
}
}
export function getActiveProfile(cfg: BlagoConfigFile): BlagoRemoteProfile {
const profile = cfg.environments[cfg.activeEnv]
if (!profile) {
throw new Error(`Среда «${cfg.activeEnv}» не найдена в config.json`)
}
if (!profile.api_url?.trim() || !profile.chain_url?.trim()) {
throw new Error(`У среды «${cfg.activeEnv}» задайте api_url и chain_url (blago env set …)`)
}
return profile
}
+54
View File
@@ -0,0 +1,54 @@
// Пути под .blago/ и поиск корня по наличию config.json вверх по дереву.
import * as fs from 'node:fs'
import * as path from 'node:path'
export const BLAGO_DIR = '.blago'
export const GITIGNORE_FILE = '.gitignore'
export const CONFIG_FILE = 'config.json'
export const INDEX_FILE = 'index.json'
export const STAGING_FILE = 'staging.json'
export const PENDING_CREATE_FILE = 'pending-create.json'
export function configPath(root: string): string {
return path.join(root, BLAGO_DIR, CONFIG_FILE)
}
export function indexPath(root: string): string {
return path.join(root, BLAGO_DIR, INDEX_FILE)
}
export function stagingPath(root: string): string {
return path.join(root, BLAGO_DIR, STAGING_FILE)
}
export function pendingCreatePath(root: string): string {
return path.join(root, BLAGO_DIR, PENDING_CREATE_FILE)
}
export function sessionPath(root: string, envName: string): string {
return path.join(root, BLAGO_DIR, `session.${envName}.json`)
}
export function blagoDir(root: string): string {
return path.join(root, BLAGO_DIR)
}
export function gitignorePath(root: string): string {
return path.join(root, BLAGO_DIR, GITIGNORE_FILE)
}
export function findBlagoRoot(startDir: string): string | null {
let current = path.resolve(startDir)
for (;;) {
const candidate = configPath(current)
if (fs.existsSync(candidate)) {
return current
}
const parent = path.dirname(current)
if (parent === current) {
return null
}
current = parent
}
}
@@ -0,0 +1,46 @@
// Базовый каталог для поиска .blago/ и относительных путей: --dir (в любом месте argv) > BLAGO_WORKSPACE > cwd.
import * as path from 'node:path'
let explicitDirFromArgv: string | null = null
/** Вызывается из runCli: снять --dir с argv до parse, чтобы подкоманды не падали на «unknown option». */
export function peelBlagoDirFromArgv(argv: readonly string[]): { argv: string[], dir: string | null } {
let dir: string | null = null
const out: string[] = []
for (let i = 0; i < argv.length; i++) {
const a = argv[i]
if (a === undefined) {
continue
}
if (a === '--dir') {
const next = argv[i + 1]
if (next !== undefined && !next.startsWith('-')) {
dir = next
i++
}
continue
}
if (a.startsWith('--dir=')) {
const v = a.slice('--dir='.length).trim()
dir = v.length > 0 ? v : null
continue
}
out.push(a)
}
return { argv: out, dir }
}
export function setBlagoCliExplicitStartDir(dir: string | null): void {
explicitDirFromArgv = dir !== null && dir.trim().length > 0 ? path.resolve(dir.trim()) : null
}
export function resolveBlagoStartDir(): string {
if (explicitDirFromArgv !== null) {
return explicitDirFromArgv
}
if (process.env.BLAGO_WORKSPACE) {
return path.resolve(process.env.BLAGO_WORKSPACE)
}
return process.cwd()
}
@@ -0,0 +1,95 @@
// Поиск project.md / component.md относительно базового пути (архитектура §13.2).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { parseBlagoMarkdown } from '../format/index.js'
import { normalizeRelativePath } from '../sync/index-store.js'
async function fileExists(abs: string): Promise<boolean> {
try {
await fs.access(abs)
return true
}
catch {
return false
}
}
export interface ResolvedProjectMarker {
/** hash проекта/компонента из frontmarker. */
readonly project_hash: string
/** coopname из файла маркера (может быть пустым). */
readonly coopnameFromFile: string
/** Каталог с project.md или component.md, относительно корня копии. */
readonly markerRelDir: string
}
export async function resolveProjectMarker(root: string, basePathInput: string): Promise<ResolvedProjectMarker> {
const normalized = normalizeRelativePath(basePathInput)
const absInput = path.resolve(root, normalized)
const rootAbs = path.resolve(root)
const stat = await fs.stat(absInput).catch(() => null)
let searchDir = absInput
if (stat?.isFile()) {
const base = path.basename(absInput)
if (base !== 'project.md' && base !== 'component.md') {
throw new Error(
`Ожидался каталог или файл project.md / component.md, получено: «${basePathInput}»`,
)
}
searchDir = path.dirname(absInput)
}
else if (!stat?.isDirectory()) {
throw new Error(`Путь не найден: «${basePathInput}»`)
}
if (
!(path.resolve(searchDir) === rootAbs
|| path.resolve(searchDir).startsWith(rootAbs + path.sep))
) {
throw new Error('Базовый путь должен быть внутри корня рабочей копии blago')
}
let current: string | null = searchDir
for (let depth = 0; depth < 500 && current; depth += 1) {
for (const name of ['component.md', 'project.md'] as const) {
const f = path.join(current, name)
if (await fileExists(f)) {
const raw = await fs.readFile(f, 'utf8')
const parsed = parseBlagoMarkdown(raw)
if (parsed.type !== 'project') {
continue
}
const project_hash = String(parsed.data.hash ?? '').trim()
if (!project_hash) {
throw new Error(`В «${path.relative(root, f)}» отсутствует hash проекта/компонента`)
}
const coopnameFromFile = String(parsed.data.coopname ?? '').trim()
return {
project_hash,
coopnameFromFile,
markerRelDir: normalizeRelativePath(path.relative(root, current)),
}
}
}
if (path.resolve(current) === rootAbs) {
break
}
const parent = path.dirname(current)
if (parent === current) {
break
}
const parentRes = path.resolve(parent)
if (!(parentRes === rootAbs || parentRes.startsWith(rootAbs + path.sep))) {
break
}
current = parent
}
throw new Error(
'Не найден project.md или component.md. Укажите каталог проекта/компонента или выполните «blago pull».',
)
}
@@ -0,0 +1,126 @@
// Локальное создание черновика задачи (FR-016).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import type { BlagoConfigFile } from '../config/index.js'
import { resolveCoopname } from '../config/index.js'
import { serializeBlagoMarkdown } from '../format/index.js'
import { generateEntityHashHex64 } from '../lib/generate-entity-hash.js'
import { loadSession } from '../session/index.js'
import { appendPendingItem } from '../sync/pending-create.js'
import { loadProjectMapsFromIndex } from '../sync/project-index-map.js'
import { appendPathsToStaging, loadIndex, normalizeRelativePath } from '../sync/index-store.js'
import { generateSlug, issueFileRelativePath, workspaceBasePath } from '../sync/layout.js'
import { resolveProjectMarker } from './resolve-base.js'
import { buildDraftIssueFrontmatterAndBody } from './templates.js'
async function fileExistsAbs(abs: string): Promise<boolean> {
try {
await fs.access(abs)
return true
}
catch {
return false
}
}
function parseCreatorsCsv(v: string | undefined): string[] {
if (v === undefined || v.trim() === '') {
return []
}
return v.split(',').map(s => s.trim()).filter(Boolean)
}
async function pickIssueDraftRelativePath(
root: string,
title: string,
basePath: string,
pendingHash: string,
): Promise<string> {
const primary = issueFileRelativePath(title, basePath, '0')
const absPrimary = path.join(root, primary)
if (!(await fileExistsAbs(absPrimary))) {
return normalizeRelativePath(primary)
}
const slug = generateSlug(title) || 'issue'
const dir = `${basePath.replace(/\\/g, '/')}/issues`
const alt = `${dir}/0-${slug}-${pendingHash.slice(0, 8)}.md`
const absAlt = path.join(root, alt)
if (!(await fileExistsAbs(absAlt))) {
return normalizeRelativePath(alt)
}
throw new Error(`Не удалось подобрать свободное имя файла в «${dir}»`)
}
export interface RunCreateIssueOptions {
readonly setSelf?: boolean
readonly creatorsCsv?: string
readonly submaster?: string
}
export async function runCreateIssue(
root: string,
cfg: BlagoConfigFile,
basePathArg: string,
title: string,
options: RunCreateIssueOptions,
): Promise<{ relativePath: string }> {
const coopCfg = resolveCoopname(cfg)
if (!coopCfg) {
throw new Error('Задайте coopname в config активной среды (или «blago init --coopname»)')
}
const index = await loadIndex(root)
const { projectByHash, projectRowByHash } = await loadProjectMapsFromIndex(root, index)
const marker = await resolveProjectMarker(root, basePathArg)
const proj = projectByHash.get(marker.project_hash)
if (!proj) {
throw new Error(
`Проект «${marker.project_hash}» не найден в индексе. Выполните «blago pull» и повторите create.`,
)
}
const fromCsv = parseCreatorsCsv(options.creatorsCsv)
let creators: string[]
if (options.setSelf) {
const session = await loadSession(root, cfg.activeEnv)
if (!session) {
throw new Error(
'Для --set-self нужна сохранённая сессия активной среды. Выполните «blago login».',
)
}
const u = session.username.trim()
creators = [u, ...fromCsv.filter(c => c !== u)]
}
else {
creators = fromCsv
}
const pendingHash = generateEntityHashHex64()
const { data, body } = buildDraftIssueFrontmatterAndBody({
title,
pendingHashHex64: pendingHash,
project_hash: marker.project_hash,
projectRowByHash,
creators,
submaster: options.submaster,
})
const wsBase = workspaceBasePath(proj, projectByHash)
const rel = await pickIssueDraftRelativePath(root, title, wsBase, pendingHash)
const abs = path.join(root, rel)
await fs.mkdir(path.dirname(abs), { recursive: true })
await fs.writeFile(abs, serializeBlagoMarkdown(data, body), 'utf8')
const createdAt = new Date().toISOString()
await appendPendingItem(root, {
kind: 'issue',
entity_hash: pendingHash,
relative_path: rel,
created_at: createdAt,
})
await appendPathsToStaging(root, [rel])
return { relativePath: rel }
}
@@ -0,0 +1,117 @@
// Локальное создание черновика требования (story) (FR-016).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import type { BlagoConfigFile } from '../config/index.js'
import { resolveCoopname } from '../config/index.js'
import { serializeBlagoMarkdown } from '../format/index.js'
import { generateEntityHashHex64 } from '../lib/generate-entity-hash.js'
import { loadSession } from '../session/index.js'
import { appendPendingItem } from '../sync/pending-create.js'
import { loadProjectMapsFromIndex } from '../sync/project-index-map.js'
import { appendPathsToStaging, loadIndex, normalizeRelativePath } from '../sync/index-store.js'
import { storyRequirementIdPrefix2 } from '../lib/capital-id-path.js'
import { generateSlug, storyFileRelativePath, workspaceBasePath } from '../sync/layout.js'
import { resolveProjectMarker } from './resolve-base.js'
import { storyContentFormatFromCliOption } from './story-format.js'
import { buildDraftStoryFrontmatterAndBody } from './templates.js'
async function fileExistsAbs(abs: string): Promise<boolean> {
try {
await fs.access(abs)
return true
}
catch {
return false
}
}
async function pickStoryDraftRelativePath(
root: string,
title: string,
basePath: string,
storyHash: string,
): Promise<string> {
const primary = storyFileRelativePath(title, basePath, '', storyHash, undefined)
const absPrimary = path.join(root, primary)
if (!(await fileExistsAbs(absPrimary))) {
return normalizeRelativePath(primary)
}
const slug = generateSlug(title) || 'requirement'
const p2 = storyRequirementIdPrefix2('', storyHash)
const dir = `${basePath.replace(/\\/g, '/')}/requirements`
const alt = `${dir}/${p2}-${slug}-${storyHash.slice(0, 6)}.md`
const absAlt = path.join(root, alt)
if (!(await fileExistsAbs(absAlt))) {
return normalizeRelativePath(alt)
}
throw new Error(`Не удалось подобрать свободное имя файла в «${dir}»`)
}
export interface RunCreateStoryOptions {
readonly setSelf?: boolean
/** markdown | mermaid | drawio | bpmn */
readonly format?: string
}
export async function runCreateStory(
root: string,
cfg: BlagoConfigFile,
basePathArg: string,
title: string,
options: RunCreateStoryOptions,
): Promise<{ relativePath: string }> {
const coopCfg = resolveCoopname(cfg)
if (!coopCfg) {
throw new Error('Задайте coopname в config активной среды (или «blago init --coopname»)')
}
const index = await loadIndex(root)
const { projectByHash } = await loadProjectMapsFromIndex(root, index)
const marker = await resolveProjectMarker(root, basePathArg)
const proj = projectByHash.get(marker.project_hash)
if (!proj) {
throw new Error(
`Проект «${marker.project_hash}» не найден в индексе. Выполните «blago pull» и повторите create.`,
)
}
let created_by: string | undefined
if (options.setSelf) {
const session = await loadSession(root, cfg.activeEnv)
if (!session) {
throw new Error(
'Для --set-self нужна сохранённая сессия активной среды. Выполните «blago login».',
)
}
created_by = session.username
}
const content_format = storyContentFormatFromCliOption(options.format ?? 'markdown')
const storyHash = generateEntityHashHex64()
const { data, body } = buildDraftStoryFrontmatterAndBody({
title,
story_hash: storyHash,
project_hash: marker.project_hash,
content_format,
created_by,
})
const wsBase = workspaceBasePath(proj, projectByHash)
const rel = await pickStoryDraftRelativePath(root, title, wsBase, storyHash)
const abs = path.join(root, rel)
await fs.mkdir(path.dirname(abs), { recursive: true })
await fs.writeFile(abs, serializeBlagoMarkdown(data, body), 'utf8')
const createdAt = new Date().toISOString()
await appendPendingItem(root, {
kind: 'story',
entity_hash: storyHash,
relative_path: rel,
created_at: createdAt,
})
await appendPathsToStaging(root, [rel])
return { relativePath: rel }
}
@@ -0,0 +1,18 @@
/** Значения CapitalStoryContentFormat в frontmatter (MARKDOWN, …). */
export function storyContentFormatFromCliOption(s: string): string {
const k = s.trim().toLowerCase()
const map: Record<string, string> = {
markdown: 'MARKDOWN',
mermaid: 'MERMAID',
drawio: 'DRAWIO',
bpmn: 'BPMN',
}
const v = map[k]
if (!v) {
throw new Error(
`Неизвестный --format «${s}». Допустимо: markdown, mermaid, drawio, bpmn`,
)
}
return v
}
@@ -0,0 +1,15 @@
/** Минимальный BPMN 2.0 (стартовое событие + DI), как в bpmn.io. */
export const storyStubBpmn = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Coopenomics" exporterVersion="1.0">
<bpmn:process id="Process_1" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
`
@@ -0,0 +1,15 @@
/** Минимальная схема draw.io / diagrams.net (одна фигура «Старт»). */
export const storyStubDrawio = `<mxfile host="app.diagrams.net" agent="blago-cli" version="22.1.0">
<diagram name="Страница-1" id="stub-page">
<mxGraphModel dx="800" dy="600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="2" value="Старт" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="360" y="240" width="120" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
`
@@ -0,0 +1,22 @@
// Рыбы тел story по content_format (Capital) — чтобы черновик сразу отображался в UI.
import { storyStubBpmn } from './bpmn.js'
import { storyStubDrawio } from './drawio.js'
import { storyStubMarkdown } from './markdown.js'
import { storyStubMermaid } from './mermaid.js'
const STUB_BY_FORMAT: Record<'MARKDOWN' | 'MERMAID' | 'DRAWIO' | 'BPMN', string> = {
MARKDOWN: storyStubMarkdown,
MERMAID: storyStubMermaid,
DRAWIO: storyStubDrawio,
BPMN: storyStubBpmn,
}
export function storyStubBodyForFormat(contentFormat: string): string {
const key = contentFormat as keyof typeof STUB_BY_FORMAT
const body = STUB_BY_FORMAT[key]
if (body === undefined) {
throw new Error(`Нет рыбы для content_format «${contentFormat}»`)
}
return body
}
@@ -0,0 +1,2 @@
/** Минимальное тело MARKDOWN: корректный заголовок и место для текста. */
export const storyStubMarkdown = `# Новое требование`
@@ -0,0 +1,4 @@
/** Минимальная диаграмма MERMAID (отображается в превью). */
export const storyStubMermaid = `flowchart TD
A[Старт]
`
@@ -0,0 +1,79 @@
// Черновики frontmatter для локального create (без API).
import type { ProjectRowLite } from '../sync/project-index-map.js'
import { issueWorkspaceTitlesFromProjects } from '../format/index.js'
import { storyStubBodyForFormat } from './story-stubs/index.js'
export interface DraftIssueFields {
readonly title: string
readonly pendingHashHex64: string
readonly project_hash: string
readonly projectRowByHash: Map<string, ProjectRowLite>
readonly creators: string[]
readonly submaster?: string
}
export function buildDraftIssueFrontmatterAndBody(f: DraftIssueFields): {
data: Record<string, unknown>
body: string
} {
const workspace = issueWorkspaceTitlesFromProjects(f.project_hash, f.projectRowByHash)
const now = new Date().toISOString()
const data: Record<string, unknown> = {
type: 'issue',
id: '0',
title: f.title,
status: 'BACKLOG',
priority: 'MEDIUM',
estimate: 0,
creators: f.creators,
labels: [],
hash: f.pendingHashHex64,
project_hash: f.project_hash,
created_at: now,
updated_at: now,
}
if (workspace.project_title.trim() !== '') {
data.project_title = workspace.project_title.trim()
}
if (workspace.component_title !== undefined && workspace.component_title.trim() !== '') {
data.component_title = workspace.component_title.trim()
}
if (f.submaster !== undefined && f.submaster.trim() !== '') {
data.submaster = f.submaster.trim()
}
return { data, body: '' }
}
export interface DraftStoryFields {
readonly title: string
readonly story_hash: string
readonly project_hash: string
readonly content_format: string
readonly created_by?: string
/** Явное тело; иначе подставляется рыба под content_format (см. create/story-stubs/). */
readonly body?: string
}
export function buildDraftStoryFrontmatterAndBody(f: DraftStoryFields): {
data: Record<string, unknown>
body: string
} {
const now = new Date().toISOString()
const data: Record<string, unknown> = {
type: 'story',
title: f.title,
hash: f.story_hash,
content_format: f.content_format,
status: 'PENDING',
sort_order: 0,
project_hash: f.project_hash,
created_at: now,
updated_at: now,
}
if (f.created_by !== undefined && f.created_by.trim() !== '') {
data.created_by = f.created_by.trim()
}
const body = f.body ?? storyStubBodyForFormat(f.content_format)
return { data, body }
}
+231
View File
@@ -0,0 +1,231 @@
// Frontmatter ↔ тело; сериализация как у Capital FileFormatService.
import matter from 'gray-matter'
import { effectiveParentHash } from '../lib/parent-hash.js'
export type EntityFrontmatterType = 'project' | 'issue' | 'story'
export interface ParsedBlagoFile {
readonly type: EntityFrontmatterType
readonly data: Record<string, unknown>
readonly body: string
}
function toIso(value: unknown): string | undefined {
if (value === undefined || value === null) {
return undefined
}
if (value instanceof Date) {
return value.toISOString()
}
if (typeof value === 'string') {
return value
}
return undefined
}
export function parseBlagoMarkdown(raw: string): ParsedBlagoFile {
const file = matter(raw)
const typeRaw = file.data.type
if (typeRaw !== 'project' && typeRaw !== 'issue' && typeRaw !== 'story') {
throw new Error(`Неизвестный или отсутствующий type во frontmatter: ${String(typeRaw)}`)
}
return {
type: typeRaw,
data: file.data as Record<string, unknown>,
body: file.content.trimEnd(),
}
}
export function serializeBlagoMarkdown(data: Record<string, unknown>, body: string): string {
const trimmed = body.endsWith('\n') ? body : `${body}\n`
return matter.stringify(trimmed, data)
}
export function issueLabelsFromMetadata(metadata: unknown): string[] {
if (metadata && typeof metadata === 'object' && metadata !== null && 'labels' in metadata) {
const labels = (metadata as { labels?: unknown }).labels
if (Array.isArray(labels)) {
return labels.map(x => String(x)).filter(s => s.trim() !== '')
}
}
if (typeof metadata === 'string' && metadata.trim()) {
try {
const parsed = JSON.parse(metadata) as { labels?: unknown }
if (Array.isArray(parsed.labels)) {
return parsed.labels.map(x => String(x)).filter(s => s.trim() !== '')
}
}
catch {
return []
}
}
return []
}
/** Цепочка родителей: [проект задачи, …, корневой проект] — для подписей в issue. */
export function issueWorkspaceTitlesFromProjects(
issueProjectHash: string,
rowByHash: ReadonlyMap<string, { title?: string | null, parent_hash?: string | null }>,
): { project_title: string, component_title?: string } {
const titles: string[] = []
let cur: string | undefined = issueProjectHash
const seen = new Set<string>()
while (cur && !seen.has(cur)) {
seen.add(cur)
const row = rowByHash.get(cur)
if (!row) {
break
}
titles.push(String(row.title ?? '').trim())
const parentRef = effectiveParentHash(row.parent_hash)
cur = parentRef || undefined
}
const project_title = titles.length > 0 ? titles[titles.length - 1] : ''
const component_title = titles.length > 1 ? titles[0] : undefined
return {
project_title,
...(component_title !== undefined && component_title !== '' ? { component_title } : {}),
}
}
/** Проект из ответа capitalProjects / capitalProject */
export function projectToFrontmatterAndBody(project: {
id?: number | null
title?: string | null
description?: string | null
project_hash: string
coopname: string
parent_hash?: string | null
parent_title?: string | null
status?: string | null
created_at?: Date | string | null
_updated_at?: Date | string | null
}): { data: Record<string, unknown>, body: string } {
const data: Record<string, unknown> = {}
data.type = 'project'
if (project.id !== undefined && project.id !== null && Number.isFinite(Number(project.id))) {
data.id = Number(project.id)
}
data.title = project.title ?? ''
const parentRef = effectiveParentHash(project.parent_hash)
if (parentRef) {
const pt = project.parent_title
if (pt !== undefined && pt !== null && String(pt).trim() !== '') {
data.parent_title = String(pt).trim()
}
data.parent_hash = parentRef
}
data.coopname = project.coopname
data.status = project.status ?? undefined
data.hash = project.project_hash
const c = toIso(project.created_at)
if (c) {
data.created_at = c
}
const u = toIso(project._updated_at)
if (u) {
data.updated_at = u
}
return { data, body: project.description ?? '' }
}
export function issueToFrontmatterAndBody(
issue: {
id?: string | null
title: string
description?: string | null
issue_hash: string
project_hash: string
cycle_id?: string | null
status?: string | null
priority?: string | null
estimate?: number | null
submaster?: string | null
creators?: string[] | null
metadata?: unknown
_created_at?: Date | string | null
_updated_at?: Date | string | null
},
workspace: { project_title: string, component_title?: string },
): { data: Record<string, unknown>, body: string } {
const data: Record<string, unknown> = {}
data.type = 'issue'
const capitalIssueId
= issue.id !== undefined && issue.id !== null && String(issue.id).trim() !== ''
? String(issue.id).trim()
: issue.issue_hash
data.id = capitalIssueId
data.title = issue.title
if (workspace.project_title.trim() !== '') {
data.project_title = workspace.project_title.trim()
}
if (workspace.component_title !== undefined && workspace.component_title.trim() !== '') {
data.component_title = workspace.component_title.trim()
}
data.status = issue.status ?? 'backlog'
data.priority = issue.priority ?? 'medium'
data.estimate = issue.estimate ?? 0
data.creators = issue.creators ?? []
data.labels = issueLabelsFromMetadata(issue.metadata)
if (issue.cycle_id) {
data.cycle_id = issue.cycle_id
}
if (issue.submaster) {
data.submaster = issue.submaster
}
data.hash = issue.issue_hash
data.project_hash = issue.project_hash
const c = toIso(issue._created_at)
if (c) {
data.created_at = c
}
const u = toIso(issue._updated_at)
if (u) {
data.updated_at = u
}
return { data, body: issue.description ?? '' }
}
export function storyToFrontmatterAndBody(story: {
_id?: string | null
title: string
description?: string | null
story_hash: string
content_format?: string | null
status?: string | null
created_by?: string | null
sort_order?: number | null
project_hash?: string | null
issue_hash?: string | null
_created_at?: Date | string | null
_updated_at?: Date | string | null
}): { data: Record<string, unknown>, body: string } {
const data: Record<string, unknown> = {}
data.type = 'story'
if (story._id !== undefined && story._id !== null && String(story._id).trim() !== '') {
data.id = String(story._id)
}
data.title = story.title
data.hash = story.story_hash
data.content_format = story.content_format ?? 'MARKDOWN'
data.status = story.status ?? 'pending'
data.created_by = story.created_by ?? ''
data.sort_order = story.sort_order ?? 0
if (story.project_hash) {
data.project_hash = story.project_hash
}
if (story.issue_hash) {
data.issue_hash = story.issue_hash
}
const c = toIso(story._created_at)
if (c) {
data.created_at = c
}
const u = toIso(story._updated_at)
if (u) {
data.updated_at = u
}
return { data, body: story.description ?? '' }
}
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env node
// Точка входа бинарника → runCli.
import { runCli } from './cmd/run-cli.js'
async function main(): Promise<void> {
await runCli(process.argv)
}
main().catch((err: unknown) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,25 @@
/** Префикс сегмента пути из Capital id (число проекта или строка задачи вида PREFIX-12). */
export function capitalIdPathPrefix(capitalId: string | number): string {
const s = String(capitalId).trim()
if (s === '') {
return '0'
}
const t = s.replace(/[^\w.-]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')
return t.length > 0 ? t : 'id'
}
/**
* Два символа в начале имени файла требования (как короткий префикс id у задач):
* из букв/цифр `_id`; если меньше 2 — добираем из story_hash (только запасной пул символов).
*/
export function storyRequirementIdPrefix2(recordId: string, storyHashFallback: string): string {
const alnum = (s: string) => s.replace(/[^a-z0-9]/gi, '').toLowerCase()
let pool = alnum(recordId)
if (pool.length < 2) {
pool = `${pool}${alnum(storyHashFallback)}`
}
if (pool.length >= 2) {
return pool.slice(0, 2)
}
return `${pool}00`.slice(0, 2)
}
@@ -0,0 +1,7 @@
// 64 hex — как generateUniqueHash на desktop / канон Capital для story_hash.
import { randomBytes } from 'node:crypto'
export function generateEntityHashHex64(): string {
return randomBytes(32).toString('hex')
}
+7
View File
@@ -0,0 +1,7 @@
// Эталон содержимого файла в индексе (content_etag_local).
import { createHash } from 'node:crypto'
export function sha256Hex(content: string): string {
return createHash('sha256').update(content, 'utf8').digest('hex')
}
@@ -0,0 +1,17 @@
/**
* В Capital родитель у корневого проекта часто приходит как null/«» или нулевой checksum256 (64׫0»).
* Такая строка truthy в JS — без нормализации сущность ошибочно считается вложенным компонентом.
*/
export function effectiveParentHash(parent_hash: string | null | undefined): string | undefined {
if (parent_hash === undefined || parent_hash === null) {
return undefined
}
const t = String(parent_hash).trim()
if (t === '') {
return undefined
}
if (/^0+$/.test(t)) {
return undefined
}
return t
}
@@ -0,0 +1,73 @@
// BLAGO-FORMATS.md / BLAGO-COMMITS.md в корень копии.
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
const FORMATS = `# Форматы файлов blago-cli
Синхронизируются типы: **project**, **issue**, **story**. Тип **result** через CLI не синхронизируется.
Все файлы — Markdown с YAML frontmatter между тройными дефисами (---) в первой строке файла. Поле **hash** — стабильный идентификатор сущности; **не менять вручную**.
## project (каталог '<capital_id>-<slug>/project.md' или '…/components/<capital_id>-<slug>/component.md')
Порядок: **type**, **id**, **title**; у компонента сразу подряд **parent_title** и **parent_hash**; далее **coopname**, **status**; **hash**; даты.
- **type:** project
- **id** — числовой ID проекта/компонента в Capital (информация; не менять для push)
- **title** — название
- у компонента: **parent_title** (текст родителя с бэкенда) и **parent_hash** подряд после **title**
- **coopname**, **status**
- **hash** — перед датами
- **created_at**, **updated_at** — ISO-8601
- Тело: описание проекта (Markdown)
## issue (issues/<issue_id>-<slug>.md — уникальность по человекочитаемому id задачи)
Порядок: **type**, **id**, **title** (название задачи), затем **project_title** / **component_title**; далее **status**, **priority**, **estimate**, **creators**, **labels**, **cycle_id**, **submaster**; внизу **hash** и **project_hash** перед датами.
- **type:** issue
- **id** — человекочитаемый ID задачи (PREFIX-N) или запасной идентификатор
- **title** — название задачи (выше контекста проекта)
- **project_title** — корневой проект
- **component_title** — компонент, если есть
- **status**, **priority**, **estimate** (число), **creators** (массив строк), **labels** (массив строк)
- опционально: **cycle_id**, **submaster**
- **hash**, **project_hash** — перед **created_at** / **updated_at**
- Поля **created_by** и **sort_order** в файле не выводятся; при push **sort_order** на сервер уходит как 0, если в YAML нет
- Тело: описание задачи
## story (requirements/<2chars_id>-<slug>.md или issues/<issue_id>-<issueSlug>-requirements/<2chars_id>-<slug>.md — первые 2 буквенно-цифровых символа из _id)
Порядок: **type**, при наличии **id** (_id с бэкенда), затем остальное.
- **type:** story
- **id** — внутренний '_id' записи требования в Capital (строка), если есть
- **title**, **hash**, **content_format** (например MARKDOWN), **status**, **created_by**, **sort_order**
- **project_hash** и/или **issue_hash**
- Тело: текст требования
После правок: **blago add** добавляет в staging только **изменённые** относительно индекса .md (и файлы без записи в индексе), затем **blago push**. Просмотр отличий от эталона индекса: blago diff; краткий статус: blago status.
`
const COMMITS = `# Сообщения коммитов в репозитории кода (FR-012)
В **прикладном** Git-репозитории с кодом указывайте в сообщении коммита как минимум:
1. **username** участника (блокчейн-аккаунт / логин в кооперативе)
2. **hash задачи** из frontmatter файла задачи (поле **hash** в issue)
Расширенный вариант (до четырёх параметров): username, проект, компонент, задача — для однозначной трассировки.
Примеры:
- ant CAPITAL-42 правка API оплат
- ant | проект blago-cli | задача CAPITAL-12 | фикс валидации
Username берите из контекста входа; hash задачи — из выгруженного файла issues/…md.
`
export async function writeLlmDocs(root: string): Promise<void> {
await fs.writeFile(path.join(root, 'BLAGO-FORMATS.md'), FORMATS, 'utf8')
await fs.writeFile(path.join(root, 'BLAGO-COMMITS.md'), COMMITS, 'utf8')
}
+206
View File
@@ -0,0 +1,206 @@
// Сессия: login (email+WIF), токены в .blago/session.<env>.json, refresh при 401.
import type { ReadStream } from 'node:tty'
import * as fs from 'node:fs/promises'
import * as readline from 'node:readline'
import { Client, Mutations, Queries } from '@coopenomics/sdk'
import { toGraphqlApiUrl } from '../config/api-url.js'
import { type BlagoConfigFile, type BlagoRemoteProfile, getActiveProfile } from '../config/index.js'
import { sessionPath } from '../config/paths.js'
export interface BlagoSessionFile {
readonly accessToken: string
readonly refreshToken: string
readonly username: string
}
export async function promptLine(question: string): Promise<string> {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
try {
return await new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer.trim())
})
})
}
finally {
rl.close()
}
}
export async function promptSecret(question: string): Promise<string> {
const stdin = process.stdin as ReadStream
const stdout = process.stdout
if (!stdin.isTTY) {
throw new Error('Скрытый ввод ключа возможен только в интерактивном TTY')
}
stdout.write(question)
let onData: ((chunk: string | Buffer) => void) | undefined
try {
stdin.setRawMode(true)
stdin.resume()
stdin.setEncoding('utf8')
return await new Promise((resolve, reject) => {
let result = ''
onData = (chunk: string | Buffer): void => {
const s = typeof chunk === 'string' ? chunk : chunk.toString('utf8')
for (const ch of s) {
const code = ch.charCodeAt(0)
if (code === 3) {
stdout.write('\n')
reject(new Error('прервано'))
return
}
if (code === 13 || code === 10) {
stdout.write('\n')
resolve(result)
return
}
if (code === 127 || code === 8) {
result = result.slice(0, -1)
}
else {
result += ch
}
}
}
stdin.on('data', onData)
})
}
finally {
try {
stdin.setRawMode(false)
}
catch {
/* Windows или stdin уже не TTY */
}
if (onData) {
stdin.off('data', onData)
}
stdin.pause()
}
}
export async function saveSession(root: string, envName: string, data: BlagoSessionFile): Promise<void> {
const p = sessionPath(root, envName)
await fs.writeFile(p, `${JSON.stringify(data, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 })
try {
await fs.chmod(p, 0o600)
}
catch {
/* Windows и др. */
}
}
export async function loadSession(root: string, envName: string): Promise<BlagoSessionFile | null> {
try {
const raw = await fs.readFile(sessionPath(root, envName), 'utf8')
const parsed = JSON.parse(raw) as BlagoSessionFile
if (!parsed.accessToken || !parsed.refreshToken || !parsed.username) {
return null
}
return parsed
}
catch {
return null
}
}
export function createClient(profile: BlagoRemoteProfile): Client {
return Client.create({
api_url: toGraphqlApiUrl(profile.api_url),
chain_url: profile.chain_url,
chain_id: profile.chain_id,
})
}
export async function loginInteractive(
client: Client,
root: string,
envName: string,
): Promise<BlagoSessionFile> {
const email = await promptLine('Email (логин в кооперативе): ')
if (!email) {
throw new Error('Email не может быть пустым')
}
const wif = await promptSecret('Приватный ключ (WIF), ввод скрыт: ')
if (!wif) {
throw new Error('Ключ не может быть пустым')
}
const loginResult = await client.login(email, wif)
const access = loginResult.tokens.access.token
const refresh = loginResult.tokens.refresh.token
const username = loginResult.account.username
const session: BlagoSessionFile = { accessToken: access, refreshToken: refresh, username }
await saveSession(root, envName, session)
return session
}
export async function applySession(client: Client, session: BlagoSessionFile): Promise<void> {
client.setToken(session.accessToken)
}
export async function refreshSession(client: Client, session: BlagoSessionFile): Promise<BlagoSessionFile> {
const { [Mutations.Auth.Refresh.name]: result } = await client.Mutation(Mutations.Auth.Refresh.mutation, {
variables: {
data: {
access_token: session.accessToken,
refresh_token: session.refreshToken,
},
},
})
const next: BlagoSessionFile = {
accessToken: result.tokens.access.token,
refreshToken: result.tokens.refresh.token,
username: result.account.username,
}
return next
}
export interface AuthenticatedContext {
readonly root: string
readonly config: BlagoConfigFile
readonly client: Client
readonly session: BlagoSessionFile
}
// Токен из файла → проверка запросом; при сбое — refresh или интерактивный login.
export async function ensureAuthenticatedContext(
root: string,
cfg: BlagoConfigFile,
options?: { allowInteractiveLogin?: boolean },
): Promise<AuthenticatedContext> {
const profile = getActiveProfile(cfg)
const client = createClient(profile)
let session = await loadSession(root, cfg.activeEnv)
if (!session) {
if (!options?.allowInteractiveLogin) {
throw new Error('Нет сохранённой сессии. Выполните: blago login')
}
session = await loginInteractive(client, root, cfg.activeEnv)
}
await applySession(client, session)
try {
await client.Query(Queries.Desktop.GetDesktop.query, { variables: {} })
}
catch {
try {
session = await refreshSession(client, session)
await applySession(client, session)
await saveSession(root, cfg.activeEnv, session)
}
catch {
if (!options?.allowInteractiveLogin) {
throw new Error('Сессия устарела. Выполните: blago login')
}
session = await loginInteractive(client, root, cfg.activeEnv)
await applySession(client, session)
}
}
return { root, config: cfg, client, session }
}
@@ -0,0 +1,62 @@
// Синхронное описание активной среды и сессии (для help Commander и «blago env»).
import type { BlagoConfigFile } from '../config/index.js'
import * as fs from 'node:fs'
import { configPath, findBlagoRoot, sessionPath } from '../config/paths.js'
import { resolveBlagoStartDir } from '../config/start-dir.js'
interface SessionFileShape {
username?: string
}
function readJsonIfExists<T>(filePath: string): T | null {
try {
const raw = fs.readFileSync(filePath, 'utf8')
return JSON.parse(raw) as T
}
catch {
return null
}
}
export function readSessionUsernameSync(root: string, envName: string): string | null {
const session = readJsonIfExists<SessionFileShape>(sessionPath(root, envName))
const u = session?.username?.trim()
return u && u.length > 0 ? u : null
}
/** Одна строка: активная среда и пользователь (или «вход не выполнен»). */
export function describeBlagoSessionLine(cfg: BlagoConfigFile, username: string | null): string {
const envName = cfg.activeEnv
const profile = cfg.environments[envName]
const label = profile?.label?.trim()
const envHuman = label ? `«${envName}» (${label})` : `«${envName}»`
const userHuman = username
? `пользователь: ${username}`
: 'вход не выполнен (blago login)'
return `Сессия: среда ${envHuman}; ${userHuman}.`
}
/** Текст в конец blago --help и blago <cmd> --help (синхронно). */
export function formatBlagoSessionStatusHelpExtra(): string {
const root = findBlagoRoot(resolveBlagoStartDir())
if (!root) {
return `
Копия blago не найдена: нет .blago/config.json вверх от базового каталога (--dir, иначе BLAGO_WORKSPACE, иначе cwd).`
}
const cfg = readJsonIfExists<BlagoConfigFile>(configPath(root))
if (!cfg?.activeEnv || typeof cfg.environments !== 'object') {
return `
Корень: ${root}
Файл .blago/config.json некорректен (нужны activeEnv и environments).`
}
const line = describeBlagoSessionLine(cfg, readSessionUsernameSync(root, cfg.activeEnv))
return `
${line}`
}
+87
View File
@@ -0,0 +1,87 @@
// add: только .md, с учётом .blagoignore; в staging — только файлы, изменённые относительно индекса (или без записи в индексе).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { sha256Hex } from '../lib/hash.js'
import { isIgnoredRelativePath, loadBlagoIgnoreRules } from './ignore.js'
import { findByRelativePath, loadIndex, loadStaging, normalizeRelativePath, saveStaging } from './index-store.js'
async function collectMarkdownFiles(absDir: string): Promise<string[]> {
const out: string[] = []
const entries = await fs.readdir(absDir, { withFileTypes: true })
for (const e of entries) {
const abs = path.join(absDir, e.name)
if (e.isDirectory()) {
const nested = await collectMarkdownFiles(abs)
out.push(...nested)
}
else if (e.isFile() && e.name.endsWith('.md')) {
out.push(abs)
}
}
return out
}
/** true — файл стоит поставить в staging: нет в индексе или SHA256 на диске ≠ content_etag_local. */
async function isDirtyVsIndex(root: string, rel: string, index: Awaited<ReturnType<typeof loadIndex>>): Promise<boolean> {
const entry = findByRelativePath(index, rel)
if (!entry) {
return true
}
try {
const raw = await fs.readFile(path.join(root, rel), 'utf8')
return sha256Hex(raw) !== entry.content_etag_local
}
catch {
return true
}
}
export interface RunAddResult {
/** Пути, прошедшие фильтр и попавшие в объединённый staging */
stagedPaths: string[]
skippedUnchanged: number
skippedIgnored: number
}
export async function runAdd(root: string, targets: string[]): Promise<RunAddResult> {
if (targets.length === 0) {
throw new Error('Укажите файлы или каталоги: blago add <путь> …')
}
const rules = await loadBlagoIgnoreRules(root)
const index = await loadIndex(root)
const staging = await loadStaging(root)
const set = new Set(staging.paths.map(p => normalizeRelativePath(p)))
let skippedUnchanged = 0
let skippedIgnored = 0
for (const t of targets) {
const abs = path.resolve(root, t)
const st = await fs.stat(abs)
const files: string[] = []
if (st.isDirectory()) {
files.push(...(await collectMarkdownFiles(abs)))
}
else if (st.isFile()) {
files.push(abs)
}
for (const file of files) {
const rel = normalizeRelativePath(path.relative(root, file))
if (isIgnoredRelativePath(rel, rules)) {
skippedIgnored += 1
continue
}
if (!(await isDirtyVsIndex(root, rel, index))) {
skippedUnchanged += 1
continue
}
set.add(rel)
}
}
const stagedPaths = [...set].sort()
await saveStaging(root, { paths: stagedPaths })
return { stagedPaths, skippedUnchanged, skippedIgnored }
}
+86
View File
@@ -0,0 +1,86 @@
// Сброс копии: удалить верхнеуровневые каталоги проектов (всё от pull лежит внутри них) + обнулить index/staging.
// Не поднимаемся по дереву от файлов — только rm одного уровня под корнём копии. .blago не трогаем.
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { loadIndex, normalizeRelativePath, saveIndex, saveStaging } from './index-store.js'
function isInsideRoot(rootAbs: string, absPath: string): boolean {
const rel = path.relative(rootAbs, absPath)
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))
}
/** Запись индекса: без «..», без абсолютного пути и NUL. */
function isSafeIndexRelativePath(rel: string): boolean {
if (!rel || rel.includes('\0')) {
return false
}
const posix = rel.replace(/\\/g, '/')
if (posix.startsWith('/') || /^[a-z]:\//i.test(posix)) {
return false
}
const segments = posix.split('/').filter(Boolean)
return !segments.includes('..')
}
function assertCleanRootIsNotFilesystemRoot(rootAbs: string): void {
const n = path.normalize(rootAbs)
if (n === '/' || n === '\\') {
throw new Error('blago clean: корень копии совпадает с корнем файловой системы — операция запрещена.')
}
if (process.platform === 'win32' && /^[a-z]:[\\/]?$/i.test(n)) {
throw new Error('blago clean: корень копии — корень диска Windows — операция запрещена.')
}
}
/**
* По индексу собираем имена верхних каталогов (первый сегмент пути) и удаляем каждый целиком через fs.rm recursive.
* Конфиг и сессии в .blago/ не затрагиваются.
*/
export async function runClean(root: string): Promise<void> {
let rootAbs = path.resolve(root)
try {
rootAbs = await fs.realpath(rootAbs)
}
catch {
/* каталога нет */
}
assertCleanRootIsNotFilesystemRoot(rootAbs)
const index = await loadIndex(root)
const topNames = new Set<string>()
const blagoName = '.blago'
for (const e of index.entries) {
const rel = normalizeRelativePath(e.relative_path)
if (!isSafeIndexRelativePath(rel)) {
continue
}
const first = rel.split('/').filter(Boolean)[0]
if (!first || first === blagoName || first === '.' || first === '..') {
continue
}
topNames.add(first)
}
for (const name of topNames) {
const target = path.join(rootAbs, name)
if (!isInsideRoot(rootAbs, target)) {
continue
}
const blagoDir = path.join(rootAbs, blagoName)
if (target === blagoDir || target.startsWith(`${blagoDir}${path.sep}`)) {
continue
}
try {
await fs.rm(target, { recursive: true, force: true })
}
catch {
/* нет или нет прав */
}
}
await saveIndex(root, { entries: [] })
await saveStaging(root, { paths: [] })
}
@@ -0,0 +1,16 @@
// Расхождение updated_at: индекс vs свежий ответ API → throw.
export class BlagoConflictError extends Error {
constructor(message: string) {
super(message)
this.name = 'BlagoConflictError'
}
}
export function assertSameRemoteVersion(localIndexIso: string, serverIso: string, entityLabel: string): void {
if (localIndexIso !== serverIso) {
throw new BlagoConflictError(
`Конфликт для ${entityLabel}: на сервере другая версия (updated_at). Выполните «blago pull», разрешите расхождения и повторите push.`,
)
}
}
+47
View File
@@ -0,0 +1,47 @@
// FR-010: отличие рабочих файлов от эталона в индексе (SHA256 + превью; не патч к серверу).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { sha256Hex } from '../lib/hash.js'
import { info } from '../ui/output.js'
import { loadIndex } from './index-store.js'
const PREVIEW_LINES = 24
export async function runDiff(root: string): Promise<void> {
const index = await loadIndex(root)
let shown = false
for (const e of index.entries) {
const abs = path.join(root, e.relative_path)
try {
const raw = await fs.readFile(abs, 'utf8')
const h = sha256Hex(raw)
if (h === e.content_etag_local) {
continue
}
shown = true
info(`${e.relative_path} [${e.entity_type} ${e.entity_hash}]`)
info(` индекс sha256: ${e.content_etag_local}`)
info(` сейчас sha256: ${h}`)
const lines = raw.split('\n')
info(' превью:')
for (let i = 0; i < Math.min(PREVIEW_LINES, lines.length); i++) {
info(` | ${lines[i]}`)
}
if (lines.length > PREVIEW_LINES) {
info(` … ещё ${lines.length - PREVIEW_LINES} строк`)
}
info('')
}
catch {
shown = true
info(`${e.relative_path} — файл отсутствует на диске [${e.entity_type} ${e.entity_hash}]`)
info('')
}
}
if (!shown) {
info('Нет расхождений с индексом по содержимому.')
}
}
+49
View File
@@ -0,0 +1,49 @@
// .blagoignore: простые префиксы/имена (не полный gitignore).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
const DEFAULT_IGNORE = ['.blago/', '.git/']
export async function loadBlagoIgnoreRules(root: string): Promise<string[]> {
const rules: string[] = [...DEFAULT_IGNORE]
try {
const raw = await fs.readFile(path.join(root, '.blagoignore'), 'utf8')
for (const line of raw.split('\n')) {
const t = line.trim()
if (!t || t.startsWith('#')) {
continue
}
rules.push(t)
}
}
catch {
/* нет файла */
}
return rules
}
function matchesRule(relPosix: string, rule: string): boolean {
const r = rule.replace(/\\/g, '/')
const withSlash = r.endsWith('/') ? r.slice(0, -1) : r
if (relPosix === withSlash) {
return true
}
if (relPosix.startsWith(`${withSlash}/`)) {
return true
}
if (!r.includes('/')) {
const parts = relPosix.split('/')
return parts.includes(withSlash)
}
return false
}
export function isIgnoredRelativePath(relPosix: string, rules: string[]): boolean {
for (const rule of rules) {
if (matchesRule(relPosix, rule)) {
return true
}
}
return false
}
@@ -0,0 +1,101 @@
// index.json + staging.json: путь и etag содержимого после последнего pull/push.
import * as fs from 'node:fs/promises'
import { indexPath, stagingPath } from '../config/paths.js'
export type BlagoEntityType = 'project' | 'issue' | 'story'
export interface IndexEntry {
entity_type: BlagoEntityType
entity_hash: string
relative_path: string
remote_updated_at: string
content_etag_local: string
}
export interface IndexFile {
entries: IndexEntry[]
}
export interface StagingFile {
paths: string[]
}
export async function loadIndex(root: string): Promise<IndexFile> {
try {
const raw = await fs.readFile(indexPath(root), 'utf8')
const parsed = JSON.parse(raw) as IndexFile
if (!Array.isArray(parsed.entries)) {
return { entries: [] }
}
return parsed
}
catch {
return { entries: [] }
}
}
export async function saveIndex(root: string, index: IndexFile): Promise<void> {
await fs.writeFile(indexPath(root), `${JSON.stringify(index, null, 2)}\n`, 'utf8')
}
export function findByHash(index: IndexFile, type: BlagoEntityType, hash: string): IndexEntry | undefined {
return index.entries.find(e => e.entity_type === type && e.entity_hash === hash)
}
export function findByRelativePath(index: IndexFile, relativePath: string): IndexEntry | undefined {
const n = normalizeRelativePath(relativePath)
return index.entries.find(e => normalizeRelativePath(e.relative_path) === n)
}
export function upsertEntry(index: IndexFile, entry: IndexEntry): void {
const i = index.entries.findIndex(e => e.entity_type === entry.entity_type && e.entity_hash === entry.entity_hash)
if (i >= 0) {
index.entries[i] = entry
}
else {
index.entries.push(entry)
}
}
export async function loadStaging(root: string): Promise<StagingFile> {
try {
const raw = await fs.readFile(stagingPath(root), 'utf8')
const parsed = JSON.parse(raw) as StagingFile
if (!Array.isArray(parsed.paths)) {
return { paths: [] }
}
return parsed
}
catch {
return { paths: [] }
}
}
export async function saveStaging(root: string, staging: StagingFile): Promise<void> {
await fs.writeFile(stagingPath(root), `${JSON.stringify(staging, null, 2)}\n`, 'utf8')
}
/** Добавить пути в staging без проверки «грязности» (после create). */
export async function appendPathsToStaging(root: string, paths: string[]): Promise<void> {
const staging = await loadStaging(root)
const set = new Set(staging.paths.map(p => normalizeRelativePath(p)))
for (const p of paths) {
set.add(normalizeRelativePath(p))
}
await saveStaging(root, { paths: [...set].sort() })
}
/** Заменить один путь в staging (после переименования файла при push). */
export async function replaceStagingPath(root: string, fromRel: string, toRel: string): Promise<void> {
const from = normalizeRelativePath(fromRel)
const to = normalizeRelativePath(toRel)
const staging = await loadStaging(root)
const next = staging.paths.map(p => (normalizeRelativePath(p) === from ? to : p))
await saveStaging(root, { paths: [...new Set(next)].sort() })
}
export function normalizeRelativePath(p: string): string {
return p.replace(/\\/g, '/').replace(/^\.\/+/, '').replace(/^\/+/, '')
}
+124
View File
@@ -0,0 +1,124 @@
// slug и пути project.md / component.md / issues / requirements (как GitHub-синк).
import { capitalIdPathPrefix, storyRequirementIdPrefix2 } from '../lib/capital-id-path.js'
import { effectiveParentHash } from '../lib/parent-hash.js'
const transliterationMap: Record<string, string> = {
а: 'a',
б: 'b',
в: 'v',
г: 'g',
д: 'd',
е: 'e',
ё: 'e',
ж: 'zh',
з: 'z',
и: 'i',
й: 'y',
к: 'k',
л: 'l',
м: 'm',
н: 'n',
о: 'o',
п: 'p',
р: 'r',
с: 's',
т: 't',
у: 'u',
ф: 'f',
х: 'kh',
ц: 'ts',
ч: 'ch',
ш: 'sh',
щ: 'shch',
ъ: '',
ы: 'y',
ь: '',
э: 'e',
ю: 'yu',
я: 'ya',
}
function hasCyrillic(text: string): boolean {
return /[а-яё]/i.test(text)
}
function transliterate(text: string): string {
return text
.toLowerCase()
.split('')
.map(char => transliterationMap[char] ?? char)
.join('')
}
export function generateSlug(title: string): string {
let processed = title
if (hasCyrillic(title)) {
processed = transliterate(title)
}
return processed
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '')
}
export interface ProjectPathModel {
readonly project_hash: string
readonly title: string
readonly parent_hash?: string | null
/** ID проекта/компонента в Capital (блокчейн), для префикса путей */
readonly capital_id: number
}
export { effectiveParentHash } from '../lib/parent-hash.js'
export function workspaceBasePath(project: ProjectPathModel, projectByHash: ReadonlyMap<string, ProjectPathModel>): string {
const parentRef = effectiveParentHash(project.parent_hash)
const slug = generateSlug(project.title || (parentRef ? 'unnamed-component' : 'unnamed-project'))
const name = `${capitalIdPathPrefix(project.capital_id)}-${slug || (parentRef ? 'unnamed-component' : 'unnamed-project')}`
if (!parentRef) {
return name
}
const parent = projectByHash.get(parentRef)
if (!parent) {
throw new Error(`Родительский проект ${parentRef} не найден для компонента ${project.project_hash}`)
}
const parentBase = workspaceBasePath(parent, projectByHash)
return `${parentBase}/components/${name}`
}
export function projectFileRelativePath(project: ProjectPathModel, projectByHash: ReadonlyMap<string, ProjectPathModel>): string {
const parentRef = effectiveParentHash(project.parent_hash)
if (!parentRef) {
const slug = generateSlug(project.title || 'unnamed-project')
const name = `${capitalIdPathPrefix(project.capital_id)}-${slug || 'unnamed-project'}`
return `${name}/project.md`
}
const base = workspaceBasePath(project, projectByHash)
return `${base}/component.md`
}
/** Имя файла: id задачи (PREFIX-N) + slug title — коллизий нет при уникальном id. */
export function issueFileRelativePath(issueTitle: string, basePath: string, issueCapitalId: string): string {
const slug = generateSlug(issueTitle) || 'issue'
const idp = capitalIdPathPrefix(issueCapitalId)
return `${basePath}/issues/${idp}-${slug}.md`
}
export function storyFileRelativePath(
storyTitle: string,
basePath: string,
storyRecordId: string,
storyHash: string,
issue?: { readonly id: string, readonly titleSlug: string },
): string {
const slug = generateSlug(storyTitle) || 'requirement'
const id2 = storyRequirementIdPrefix2(storyRecordId, storyHash)
if (issue?.titleSlug) {
const ip = capitalIdPathPrefix(issue.id)
return `${basePath}/issues/${ip}-${issue.titleSlug}-requirements/${id2}-${slug}.md`
}
return `${basePath}/requirements/${id2}-${slug}.md`
}
@@ -0,0 +1,84 @@
// Очередь локально созданных сущностей до первого push (FR-016).
import * as fs from 'node:fs/promises'
import { pendingCreatePath } from '../config/paths.js'
import type { BlagoEntityType } from './index-store.js'
import { normalizeRelativePath } from './index-store.js'
export type PendingCreateKind = 'issue' | 'story'
export interface PendingCreateItem {
readonly kind: PendingCreateKind
/** Для issue — временный hash в frontmatter до ответа API; для story — тот же story_hash, что уйдёт в CreateStory. */
readonly entity_hash: string
readonly relative_path: string
readonly created_at: string
}
export interface PendingCreateFile {
items: PendingCreateItem[]
}
export async function loadPendingCreate(root: string): Promise<PendingCreateFile> {
try {
const raw = await fs.readFile(pendingCreatePath(root), 'utf8')
const parsed = JSON.parse(raw) as PendingCreateFile
if (!Array.isArray(parsed.items)) {
return { items: [] }
}
return parsed
}
catch {
return { items: [] }
}
}
export async function savePendingCreate(root: string, data: PendingCreateFile): Promise<void> {
await fs.writeFile(pendingCreatePath(root), `${JSON.stringify(data, null, 2)}\n`, 'utf8')
}
export function findPendingItem(
data: PendingCreateFile,
kind: PendingCreateKind,
entityHash: string,
): PendingCreateItem | undefined {
return data.items.find(
i => i.kind === kind && i.entity_hash === entityHash,
)
}
export async function appendPendingItem(root: string, item: PendingCreateItem): Promise<void> {
const data = await loadPendingCreate(root)
if (findPendingItem(data, item.kind, item.entity_hash)) {
throw new Error(`Запись pending-create уже есть: ${item.kind} ${item.entity_hash}`)
}
const rel = normalizeRelativePath(item.relative_path)
if (data.items.some(i => normalizeRelativePath(i.relative_path) === rel)) {
throw new Error(`Файл уже в pending-create: ${rel}`)
}
data.items.push(item)
await savePendingCreate(root, data)
}
export async function removePendingItem(
root: string,
kind: PendingCreateKind,
entityHash: string,
): Promise<void> {
const data = await loadPendingCreate(root)
const next = data.items.filter(i => !(i.kind === kind && i.entity_hash === entityHash))
if (next.length === data.items.length) {
throw new Error(`Не найдена запись pending-create: ${kind} ${entityHash}`)
}
await savePendingCreate(root, { items: next })
}
/** Соответствие типа сущности и kind в pending. */
export function pendingKindForEntityType(type: BlagoEntityType): PendingCreateKind | undefined {
if (type === 'issue' || type === 'story') {
return type
}
return undefined
}
@@ -0,0 +1,64 @@
// Карта проектов из индекса + файлов project.md / component.md (для layout после create).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { parseBlagoMarkdown } from '../format/index.js'
import type { IndexFile } from './index-store.js'
import type { ProjectPathModel } from './layout.js'
export interface ProjectRowLite {
readonly title: string
readonly parent_hash?: string | null
}
export async function loadProjectMapsFromIndex(
root: string,
index: IndexFile,
): Promise<{
projectByHash: Map<string, ProjectPathModel>
projectRowByHash: Map<string, ProjectRowLite>
}> {
const projectByHash = new Map<string, ProjectPathModel>()
const projectRowByHash = new Map<string, ProjectRowLite>()
for (const e of index.entries) {
if (e.entity_type !== 'project') {
continue
}
const abs = path.join(root, e.relative_path)
let raw: string
try {
raw = await fs.readFile(abs, 'utf8')
}
catch {
continue
}
const parsed = parseBlagoMarkdown(raw)
if (parsed.type !== 'project') {
continue
}
const h = String(parsed.data.hash ?? '').trim()
if (!h) {
continue
}
const idRaw = parsed.data.id
const capital_id
= typeof idRaw === 'number' && Number.isFinite(idRaw)
? idRaw
: Number(idRaw) || 0
projectByHash.set(h, {
project_hash: h,
title: String(parsed.data.title ?? ''),
parent_hash: parsed.data.parent_hash as string | null | undefined,
capital_id,
})
projectRowByHash.set(h, {
title: String(parsed.data.title ?? ''),
parent_hash: parsed.data.parent_hash as string | null | undefined,
})
}
return { projectByHash, projectRowByHash }
}
+441
View File
@@ -0,0 +1,441 @@
// pull: SDK → файлы; индекс по hash; смена slug → перенос (FR-014).
import type { AuthenticatedContext } from '../session/index.js'
import type { BlagoEntityType, IndexFile } from './index-store.js'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { Queries } from '@coopenomics/sdk'
import { type BlagoConfigFile, resolveCoopname } from '../config/index.js'
import {
issueToFrontmatterAndBody,
issueWorkspaceTitlesFromProjects,
projectToFrontmatterAndBody,
serializeBlagoMarkdown,
storyToFrontmatterAndBody,
} from '../format/index.js'
import { sha256Hex } from '../lib/hash.js'
import { warn } from '../ui/output.js'
import { findByHash, loadIndex, normalizeRelativePath, saveIndex, upsertEntry } from './index-store.js'
import {
generateSlug,
issueFileRelativePath,
projectFileRelativePath,
type ProjectPathModel,
storyFileRelativePath,
workspaceBasePath,
} from './layout.js'
interface CapitalProjectRow {
id?: number | null
project_hash: string
title?: string | null
description?: string | null
coopname: string
parent_hash?: string | null
parent_title?: string | null
status?: string | null
created_at?: Date | string | null
_updated_at?: Date | string | null
components?: CapitalProjectRow[] | null
}
interface CapitalIssueRow {
id?: string | null
issue_hash: string
title: string
description?: string | null
project_hash: string
cycle_id?: string | null
status?: string | null
priority?: string | null
estimate?: number | null
created_by?: string | null
submaster?: string | null
creators?: string[] | null
metadata?: unknown
sort_order?: number | null
_created_at?: Date | string | null
_updated_at?: Date | string | null
}
interface CapitalStoryRow {
_id?: string | null
story_hash: string
title: string
description?: string | null
content_format?: string | null
status?: string | null
created_by?: string | null
sort_order?: number | null
project_hash?: string | null
issue_hash?: string | null
_created_at?: Date | string | null
_updated_at?: Date | string | null
}
function flattenProjects(roots: CapitalProjectRow[]): CapitalProjectRow[] {
const out: CapitalProjectRow[] = []
const walk = (p: CapitalProjectRow): void => {
out.push(p)
for (const c of p.components ?? []) {
walk(c)
}
}
for (const r of roots) {
walk(r)
}
return out
}
function toUpdatedIso(v: Date | string | null | undefined): string {
if (v === undefined || v === null) {
return ''
}
if (v instanceof Date) {
return v.toISOString()
}
return new Date(v).toISOString()
}
function asProjectPathModel(p: CapitalProjectRow): ProjectPathModel {
const n = p.id
const capital_id = typeof n === 'number' && Number.isFinite(n) ? n : 0
return {
project_hash: p.project_hash,
title: p.title ?? '',
parent_hash: p.parent_hash,
capital_id,
}
}
async function ensureDirForFile(absFile: string): Promise<void> {
await fs.mkdir(path.dirname(absFile), { recursive: true })
}
async function fileExists(abs: string): Promise<boolean> {
try {
await fs.access(abs)
return true
}
catch {
return false
}
}
async function readFileIfExists(abs: string): Promise<string | null> {
try {
return await fs.readFile(abs, 'utf8')
}
catch {
return null
}
}
// Один файл сущности: новый путь с сервера vs индекс; «грязный» локально → не затирать без явного сценария.
async function syncEntityFile(params: {
root: string
index: IndexFile
entityType: BlagoEntityType
entityHash: string
relativePath: string
content: string
remoteUpdatedAt: string
label: string
}): Promise<void> {
const { root, index, entityType, entityHash, relativePath, content, remoteUpdatedAt, label } = params
const rel = normalizeRelativePath(relativePath)
const absNew = path.join(root, rel)
const prev = findByHash(index, entityType, entityHash)
if (!prev) {
await ensureDirForFile(absNew)
await fs.writeFile(absNew, content, 'utf8')
const etag = sha256Hex(await fs.readFile(absNew, 'utf8'))
upsertEntry(index, {
entity_type: entityType,
entity_hash: entityHash,
relative_path: rel,
remote_updated_at: remoteUpdatedAt,
content_etag_local: etag,
})
return
}
const absOld = path.join(root, prev.relative_path)
if (prev.relative_path !== rel) {
const oldContent = await readFileIfExists(absOld)
const dirty
= oldContent !== null && oldContent !== undefined && sha256Hex(oldContent) !== prev.content_etag_local
if (dirty) {
await ensureDirForFile(absNew)
if (await fileExists(absOld)) {
await fs.rename(absOld, absNew)
}
else {
await fs.writeFile(absNew, content, 'utf8')
}
upsertEntry(index, {
entity_type: entityType,
entity_hash: entityHash,
relative_path: rel,
remote_updated_at: remoteUpdatedAt,
content_etag_local: sha256Hex((await readFileIfExists(absNew)) ?? ''),
})
warn(
`Переименование на сервере: ${label} перенесён на «${rel}» с сохранением локальных правок; проверьте frontmatter (title / updated_at).`,
)
return
}
await ensureDirForFile(absNew)
await fs.writeFile(absNew, content, 'utf8')
if ((await fileExists(absOld)) && path.resolve(absOld) !== path.resolve(absNew)) {
await fs.unlink(absOld)
}
const etagAfterRename = sha256Hex(await fs.readFile(absNew, 'utf8'))
upsertEntry(index, {
entity_type: entityType,
entity_hash: entityHash,
relative_path: rel,
remote_updated_at: remoteUpdatedAt,
content_etag_local: etagAfterRename,
})
return
}
const current = await readFileIfExists(absNew)
const dirty
= current !== null && current !== undefined && sha256Hex(current) !== prev.content_etag_local
if (dirty && remoteUpdatedAt !== prev.remote_updated_at) {
warn(
`Пропуск перезаписи ${label}: есть локальные правки, на сервере новая версия. Смержите вручную или откатите файл, затем снова «blago pull».`,
)
return
}
await ensureDirForFile(absNew)
await fs.writeFile(absNew, content, 'utf8')
const etagOnDisk = sha256Hex(await fs.readFile(absNew, 'utf8'))
upsertEntry(index, {
entity_type: entityType,
entity_hash: entityHash,
relative_path: rel,
remote_updated_at: remoteUpdatedAt,
content_etag_local: etagOnDisk,
})
}
function requireCoopname(cfg: BlagoConfigFile): string {
const c = resolveCoopname(cfg)
if (!c) {
throw new Error(
'Укажите coopname в .blago/config.json в environments.<активнаяСреда> (или запасной «coopname» сверху), либо: blago init --coopname <имя>',
)
}
return c
}
export async function runPull(ctx: AuthenticatedContext): Promise<void> {
const coopname = requireCoopname(ctx.config)
const index = await loadIndex(ctx.root)
const projectByHash = new Map<string, ProjectPathModel>()
const allProjects: CapitalProjectRow[] = []
const seen = new Set<string>()
let page = 1
for (;;) {
const { [Queries.Capital.GetProjects.name]: pageResult } = await ctx.client.Query(
Queries.Capital.GetProjects.query,
{
variables: {
filter: { coopname },
options: { limit: 100, page, sortOrder: 'DESC' },
},
},
)
const chunk = pageResult.items as CapitalProjectRow[]
for (const item of chunk) {
for (const p of flattenProjects([item])) {
if (!seen.has(p.project_hash)) {
seen.add(p.project_hash)
allProjects.push(p)
}
}
}
if (page >= pageResult.totalPages) {
break
}
page += 1
}
page = 1
for (;;) {
const { [Queries.Capital.GetProjects.name]: pageResult } = await ctx.client.Query(
Queries.Capital.GetProjects.query,
{
variables: {
filter: { coopname, is_component: true },
options: { limit: 100, page, sortOrder: 'DESC' },
},
},
)
const chunk = pageResult.items as CapitalProjectRow[]
for (const item of chunk) {
for (const p of flattenProjects([item])) {
if (!seen.has(p.project_hash)) {
seen.add(p.project_hash)
allProjects.push(p)
}
}
}
if (page >= pageResult.totalPages) {
break
}
page += 1
}
for (const p of allProjects) {
projectByHash.set(p.project_hash, asProjectPathModel(p))
}
const projectRowByHash = new Map<string, CapitalProjectRow>()
for (const p of allProjects) {
projectRowByHash.set(p.project_hash, p)
}
for (const p of allProjects) {
const { data, body } = projectToFrontmatterAndBody(p)
const content = serializeBlagoMarkdown(data, body)
const rel = projectFileRelativePath(asProjectPathModel(p), projectByHash)
await syncEntityFile({
root: ctx.root,
index,
entityType: 'project',
entityHash: p.project_hash,
relativePath: rel,
content,
remoteUpdatedAt: toUpdatedIso(p._updated_at),
label: `проект ${p.project_hash}`,
})
}
const issues: CapitalIssueRow[] = []
page = 1
for (;;) {
const { [Queries.Capital.GetIssues.name]: pageResult } = await ctx.client.Query(
Queries.Capital.GetIssues.query,
{
variables: {
filter: { coopname },
options: { limit: 200, page, sortOrder: 'DESC' },
},
},
)
const chunk = pageResult.items as CapitalIssueRow[]
issues.push(...chunk)
if (page >= pageResult.totalPages) {
break
}
page += 1
}
const issueByHash = new Map<string, CapitalIssueRow>()
for (const i of issues) {
issueByHash.set(i.issue_hash, i)
}
for (const i of issues) {
const proj = projectByHash.get(i.project_hash)
if (!proj) {
warn(`Задача ${i.issue_hash}: проект ${i.project_hash} не найден в выборке, пропуск`)
continue
}
const basePath = workspaceBasePath(proj, projectByHash)
const issueCapitalId = i.id !== undefined && i.id !== null && String(i.id).trim() !== '' ? String(i.id) : i.issue_hash
const rel = issueFileRelativePath(i.title, basePath, issueCapitalId)
const workspace = issueWorkspaceTitlesFromProjects(i.project_hash, projectRowByHash)
const { data, body } = issueToFrontmatterAndBody(i, workspace)
const content = serializeBlagoMarkdown(data, body)
await syncEntityFile({
root: ctx.root,
index,
entityType: 'issue',
entityHash: i.issue_hash,
relativePath: rel,
content,
remoteUpdatedAt: toUpdatedIso(i._updated_at),
label: `задача ${i.issue_hash}`,
})
}
const stories: CapitalStoryRow[] = []
page = 1
for (;;) {
const { [Queries.Capital.GetStories.name]: pageResult } = await ctx.client.Query(
Queries.Capital.GetStories.query,
{
variables: {
filter: {
coopname,
show_issues_requirements: true,
show_components_requirements: true,
},
options: { limit: 200, page, sortOrder: 'DESC' },
},
},
)
const chunk = pageResult.items as CapitalStoryRow[]
stories.push(...chunk)
if (page >= pageResult.totalPages) {
break
}
page += 1
}
for (const s of stories) {
if (!s.project_hash) {
warn(`Требование ${s.story_hash}: нет project_hash, пропуск`)
continue
}
const proj = projectByHash.get(s.project_hash)
if (!proj) {
warn(`Требование ${s.story_hash}: проект ${s.project_hash} не найден, пропуск`)
continue
}
const basePath = workspaceBasePath(proj, projectByHash)
let issueArg: { id: string, titleSlug: string } | undefined
if (s.issue_hash) {
const iss = issueByHash.get(s.issue_hash)
if (iss) {
const issueId = iss.id !== undefined && iss.id !== null && String(iss.id).trim() !== '' ? String(iss.id) : iss.issue_hash
issueArg = {
id: issueId,
titleSlug: generateSlug(iss.title) || 'issue',
}
}
}
const storyRecordId
= s._id !== undefined && s._id !== null && String(s._id).trim() !== '' ? String(s._id) : s.story_hash
const rel = storyFileRelativePath(s.title, basePath, storyRecordId, s.story_hash, issueArg)
const { data, body } = storyToFrontmatterAndBody(s)
const content = serializeBlagoMarkdown(data, body)
await syncEntityFile({
root: ctx.root,
index,
entityType: 'story',
entityHash: s.story_hash,
relativePath: rel,
content,
remoteUpdatedAt: toUpdatedIso(s._updated_at),
label: `требование ${s.story_hash}`,
})
}
await saveIndex(ctx.root, index)
}
@@ -0,0 +1,306 @@
// Первая отправка локально созданных issue / story (ветка Create в push).
import type { AuthenticatedContext } from '../session/index.js'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { Mutations } from '@coopenomics/sdk'
import { resolveCoopname } from '../config/index.js'
import {
issueToFrontmatterAndBody,
issueWorkspaceTitlesFromProjects,
parseBlagoMarkdown,
type ParsedBlagoFile,
serializeBlagoMarkdown,
storyToFrontmatterAndBody,
} from '../format/index.js'
import { sha256Hex } from '../lib/hash.js'
import { findPendingItem, loadPendingCreate, removePendingItem, type PendingCreateItem } from './pending-create.js'
import { loadProjectMapsFromIndex } from './project-index-map.js'
import {
findByHash,
normalizeRelativePath,
replaceStagingPath,
upsertEntry,
type IndexFile,
} from './index-store.js'
import { generateSlug, issueFileRelativePath, storyFileRelativePath, workspaceBasePath } from './layout.js'
function toIso(v: unknown): string {
if (v === undefined || v === null) {
return ''
}
if (v instanceof Date) {
return v.toISOString()
}
if (typeof v === 'string') {
return new Date(v).toISOString()
}
if (typeof v === 'number' && Number.isFinite(v)) {
return new Date(v).toISOString()
}
return ''
}
function requireCoopname(cfg: AuthenticatedContext['config']): string {
const c = resolveCoopname(cfg)
if (!c) {
throw new Error(
'Укажите coopname в .blago/config.json (активная среда или общий fallback)',
)
}
return c
}
async function ensureDirForFile(absFile: string): Promise<void> {
await fs.mkdir(path.dirname(absFile), { recursive: true })
}
/** issue_hash из индекса → id и slug для пути требования под задачей. */
async function issueLinkForStoryPath(
root: string,
index: IndexFile,
issueHash: string,
): Promise<{ id: string, titleSlug: string } | undefined> {
const entry = findByHash(index, 'issue', issueHash)
if (!entry) {
return undefined
}
const raw = await fs.readFile(path.join(root, entry.relative_path), 'utf8')
const parsed = parseBlagoMarkdown(raw)
if (parsed.type !== 'issue') {
return undefined
}
const idRaw = parsed.data.id
const id = idRaw !== undefined && idRaw !== null && String(idRaw).trim() !== ''
? String(idRaw).trim()
: String(parsed.data.hash ?? '')
const titleSlug = generateSlug(String(parsed.data.title ?? '')) || 'issue'
return { id, titleSlug }
}
export async function pushCreateIssue(
ctx: AuthenticatedContext,
index: IndexFile,
rel: string,
parsed: ParsedBlagoFile,
pendingItem: PendingCreateItem,
): Promise<void> {
const coopname = requireCoopname(ctx.config)
const { projectByHash, projectRowByHash } = await loadProjectMapsFromIndex(ctx.root, index)
const projectHash = String(parsed.data.project_hash ?? '')
const proj = projectByHash.get(projectHash)
if (!proj) {
throw new Error(`Задача: проект «${projectHash}» не найден в индексе (нужен «blago pull»).`)
}
const creators = Array.isArray(parsed.data.creators)
? (parsed.data.creators as unknown[]).map(x => String(x))
: []
const labels = Array.isArray(parsed.data.labels)
? (parsed.data.labels as unknown[]).map(x => String(x))
: []
const issueInput: Mutations.Capital.CreateIssue.IInput['data'] = {
coopname,
project_hash: projectHash,
title: String(parsed.data.title ?? ''),
description: parsed.body,
status: parsed.data.status as Mutations.Capital.CreateIssue.IInput['data']['status'],
priority: parsed.data.priority as Mutations.Capital.CreateIssue.IInput['data']['priority'],
estimate: Number(parsed.data.estimate ?? 0),
sort_order: Number(parsed.data.sort_order ?? 0),
creators,
labels,
}
if (parsed.data.cycle_id) {
issueInput.cycle_id = String(parsed.data.cycle_id)
}
if (parsed.data.submaster) {
issueInput.submaster = String(parsed.data.submaster)
}
const mutationResult = await ctx.client.Mutation(Mutations.Capital.CreateIssue.mutation, {
variables: { data: issueInput },
})
const created = mutationResult[Mutations.Capital.CreateIssue.name]
if (created == null) {
throw new Error('Создание задачи: пустой ответ API')
}
const workspace = issueWorkspaceTitlesFromProjects(created.project_hash, projectRowByHash)
const { data, body } = issueToFrontmatterAndBody(
{
id: created.id,
title: created.title,
description: created.description,
issue_hash: created.issue_hash,
project_hash: created.project_hash,
cycle_id: created.cycle_id,
status: created.status,
priority: created.priority,
estimate: created.estimate,
submaster: created.submaster,
creators: created.creators,
metadata: created.metadata,
sort_order: created.sort_order,
_created_at: created._created_at,
_updated_at: created._updated_at,
},
workspace,
)
const content = serializeBlagoMarkdown(data, body)
const basePath = workspaceBasePath(projectByHash.get(created.project_hash)!, projectByHash)
const issueCapitalId
= created.id !== undefined && created.id !== null && String(created.id).trim() !== ''
? String(created.id).trim()
: created.issue_hash
const newRel = normalizeRelativePath(
issueFileRelativePath(created.title, basePath, issueCapitalId),
)
const oldRel = normalizeRelativePath(rel)
const absNew = path.join(ctx.root, newRel)
const absOld = path.join(ctx.root, oldRel)
await ensureDirForFile(absNew)
await fs.writeFile(absNew, content, 'utf8')
if (oldRel !== newRel && (await fileExists(absOld))) {
await fs.unlink(absOld)
}
if (oldRel !== newRel) {
await replaceStagingPath(ctx.root, oldRel, newRel)
}
const etag = sha256Hex(await fs.readFile(absNew, 'utf8'))
upsertEntry(index, {
entity_type: 'issue',
entity_hash: created.issue_hash,
relative_path: newRel,
remote_updated_at: toIso(created._updated_at),
content_etag_local: etag,
})
await removePendingItem(ctx.root, 'issue', pendingItem.entity_hash)
}
async function fileExists(abs: string): Promise<boolean> {
try {
await fs.access(abs)
return true
}
catch {
return false
}
}
export async function pushCreateStory(
ctx: AuthenticatedContext,
index: IndexFile,
rel: string,
parsed: ParsedBlagoFile,
pendingItem: PendingCreateItem,
): Promise<void> {
const coopname = requireCoopname(ctx.config)
const { projectByHash } = await loadProjectMapsFromIndex(ctx.root, index)
const storyHash = String(parsed.data.hash ?? '')
const projectHash = parsed.data.project_hash ? String(parsed.data.project_hash) : ''
const issueHash = parsed.data.issue_hash ? String(parsed.data.issue_hash) : ''
if (!projectHash && !issueHash) {
throw new Error('Требование: укажите project_hash и/или issue_hash во frontmatter')
}
const proj = projectHash ? projectByHash.get(projectHash) : undefined
if (projectHash && !proj) {
throw new Error(`Требование: проект «${projectHash}» не найден в индексе`)
}
const storyInput: Mutations.Capital.CreateStory.IInput['data'] = {
coopname,
story_hash: storyHash,
title: String(parsed.data.title ?? ''),
description: parsed.body,
content_format: parsed.data.content_format as Mutations.Capital.CreateStory.IInput['data']['content_format'],
status: parsed.data.status as Mutations.Capital.CreateStory.IInput['data']['status'],
sort_order: Number(parsed.data.sort_order ?? 0),
}
if (projectHash) {
storyInput.project_hash = projectHash
}
if (issueHash) {
storyInput.issue_hash = issueHash
}
const mutationResult = await ctx.client.Mutation(Mutations.Capital.CreateStory.mutation, {
variables: { data: storyInput },
})
const created = mutationResult[Mutations.Capital.CreateStory.name]
if (created == null) {
throw new Error('Создание требования: пустой ответ API')
}
const { data, body } = storyToFrontmatterAndBody({
_id: created._id,
title: created.title,
description: created.description,
story_hash: created.story_hash,
content_format: created.content_format,
status: created.status,
created_by: created.created_by,
sort_order: created.sort_order,
project_hash: created.project_hash,
issue_hash: created.issue_hash,
_created_at: created._created_at,
_updated_at: created._updated_at,
})
const content = serializeBlagoMarkdown(data, body)
const projRow = created.project_hash ? projectByHash.get(String(created.project_hash)) : undefined
if (!projRow) {
throw new Error('Требование после create: не удалось разрешить project_hash для пути файла')
}
const basePath = workspaceBasePath(projRow, projectByHash)
let issueArg: { id: string, titleSlug: string } | undefined
if (created.issue_hash) {
issueArg = await issueLinkForStoryPath(ctx.root, index, String(created.issue_hash))
}
const storyRecordId
= created._id !== undefined && created._id !== null && String(created._id).trim() !== ''
? String(created._id).trim()
: created.story_hash
const newRel = normalizeRelativePath(
storyFileRelativePath(created.title, basePath, storyRecordId, created.story_hash, issueArg),
)
const oldRel = normalizeRelativePath(rel)
const absNew = path.join(ctx.root, newRel)
const absOld = path.join(ctx.root, oldRel)
await ensureDirForFile(absNew)
await fs.writeFile(absNew, content, 'utf8')
if (oldRel !== newRel && (await fileExists(absOld))) {
await fs.unlink(absOld)
}
if (oldRel !== newRel) {
await replaceStagingPath(ctx.root, oldRel, newRel)
}
const etag = sha256Hex(await fs.readFile(absNew, 'utf8'))
upsertEntry(index, {
entity_type: 'story',
entity_hash: created.story_hash,
relative_path: newRel,
remote_updated_at: toIso(created._updated_at),
content_etag_local: etag,
})
await removePendingItem(ctx.root, 'story', pendingItem.entity_hash)
}
export async function findPendingForParsed(
root: string,
kind: 'issue' | 'story',
entityHash: string,
): Promise<PendingCreateItem | undefined> {
const data = await loadPendingCreate(root)
return findPendingItem(data, kind, entityHash)
}
+276
View File
@@ -0,0 +1,276 @@
// push: только staging; remote_updated_at из индекса должен совпасть с сервером.
import type { AuthenticatedContext } from '../session/index.js'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { Mutations, Queries } from '@coopenomics/sdk'
import { parseBlagoMarkdown } from '../format/index.js'
import { sha256Hex } from '../lib/hash.js'
import { effectiveParentHash } from '../lib/parent-hash.js'
import { validateParsedForPush } from '../validate/index.js'
import { assertSameRemoteVersion } from './conflicts.js'
import {
findByHash,
loadIndex,
loadStaging,
normalizeRelativePath,
saveIndex,
saveStaging,
upsertEntry,
} from './index-store.js'
import { pendingKindForEntityType } from './pending-create.js'
import {
findPendingForParsed,
pushCreateIssue,
pushCreateStory,
} from './push-create.js'
// Ответы GraphQL/Zeus часто дают _updated_at как unknown — сужаем безопасно.
function toIso(v: unknown): string {
if (v === undefined || v === null) {
return ''
}
if (v instanceof Date) {
return v.toISOString()
}
if (typeof v === 'string') {
return new Date(v).toISOString()
}
if (typeof v === 'number' && Number.isFinite(v)) {
return new Date(v).toISOString()
}
return ''
}
export async function runPush(ctx: AuthenticatedContext): Promise<void> {
const staging = await loadStaging(ctx.root)
if (staging.paths.length === 0) {
throw new Error('Нечего отправлять. Добавьте файлы: blago add <путь>')
}
const index = await loadIndex(ctx.root)
const normalizedList = [...new Set(staging.paths.map(p => normalizeRelativePath(p)))]
const remaining = new Set(normalizedList)
for (const rel of normalizedList) {
const n = rel
const abs = path.join(ctx.root, n)
const raw = await fs.readFile(abs, 'utf8')
const parsed = parseBlagoMarkdown(raw)
const { type, hash } = validateParsedForPush(parsed)
const entry = findByHash(index, type, hash)
const pKind = pendingKindForEntityType(type)
const pending = pKind ? await findPendingForParsed(ctx.root, pKind, hash) : undefined
if (entry && pending) {
throw new Error(
`Файл «${n}»: сущность ${type} уже в индексе, но есть запись pending-create. Удалите pending-create.json вручную или выполните pull.`,
)
}
if (!entry && pending) {
if (normalizeRelativePath(pending.relative_path) !== n) {
throw new Error(
`Файл «${n}» не совпадает с путём в pending-create («${pending.relative_path}»).`,
)
}
if (type === 'issue') {
await pushCreateIssue(ctx, index, n, parsed, pending)
}
else if (type === 'story') {
await pushCreateStory(ctx, index, n, parsed, pending)
}
remaining.delete(n)
continue
}
if (!entry) {
throw new Error(
`Файл «${n}»: сущность ${type} ${hash} не в индексе. Выполните «blago pull» или «blago create» и снова add.`,
)
}
if (normalizeRelativePath(entry.relative_path) !== n) {
throw new Error(
`Файл «${n}» не совпадает с каноническим путём в индексе «${entry.relative_path}». Выполните «blago pull» для выравнивания путей.`,
)
}
const etag = sha256Hex(raw)
if (type === 'project') {
const coopname = String(parsed.data.coopname ?? '')
const rawParent = parsed.data.parent_hash
const parentHash = effectiveParentHash(
rawParent === undefined || rawParent === null ? undefined : String(rawParent),
)
const projectQuery = await ctx.client.Query(
Queries.Capital.GetProject.query,
{
variables: {
data: {
hash,
parent_hash: parentHash,
},
},
},
)
const remote = projectQuery[Queries.Capital.GetProject.name]
if (remote == null) {
throw new Error(`Проект «${hash}» не найден на сервере.`)
}
assertSameRemoteVersion(entry.remote_updated_at, toIso(remote._updated_at), `проект ${hash}`)
const projectData: Mutations.Capital.EditProject.IInput['data'] = {
coopname,
project_hash: hash,
title: String(parsed.data.title ?? ''),
description: parsed.body,
data: remote.data ?? '',
meta: remote.meta ?? '',
invite: remote.invite ?? '',
}
await ctx.client.Mutation(
Mutations.Capital.EditProject.mutation,
{
variables: {
data: projectData,
},
},
)
const projectAfterQuery = await ctx.client.Query(
Queries.Capital.GetProject.query,
{
variables: {
data: {
hash,
parent_hash: parentHash,
},
},
},
)
const after = projectAfterQuery[Queries.Capital.GetProject.name]
if (after == null) {
throw new Error(`Проект «${hash}» не найден на сервере после сохранения.`)
}
upsertEntry(index, {
entity_type: 'project',
entity_hash: hash,
relative_path: entry.relative_path,
remote_updated_at: toIso(after._updated_at),
content_etag_local: etag,
})
}
else if (type === 'issue') {
const issueQuery = await ctx.client.Query(
Queries.Capital.GetIssue.query,
{
variables: {
data: { issue_hash: hash },
},
},
)
const remote = issueQuery[Queries.Capital.GetIssue.name]
if (remote == null) {
throw new Error(`Задача «${hash}» не найдена на сервере.`)
}
assertSameRemoteVersion(entry.remote_updated_at, toIso(remote._updated_at), `задача ${hash}`)
const creators = Array.isArray(parsed.data.creators)
? (parsed.data.creators as unknown[]).map(x => String(x))
: []
const labels = Array.isArray(parsed.data.labels)
? (parsed.data.labels as unknown[]).map(x => String(x))
: []
/** Только поля GraphQL UpdateIssueInput (schema.gql); лишние ключи ломают capitalUpdateIssue. */
const issueData: Mutations.Capital.UpdateIssue.IInput['data'] = {
issue_hash: hash,
title: String(parsed.data.title ?? ''),
description: parsed.body,
status: parsed.data.status as Mutations.Capital.UpdateIssue.IInput['data']['status'],
priority: parsed.data.priority as Mutations.Capital.UpdateIssue.IInput['data']['priority'],
estimate: Number(parsed.data.estimate ?? 0),
sort_order: Number(parsed.data.sort_order ?? 0),
creators,
labels,
}
if (parsed.data.cycle_id) {
issueData.cycle_id = String(parsed.data.cycle_id)
}
if (parsed.data.submaster) {
issueData.submaster = String(parsed.data.submaster)
}
const issueMutation = await ctx.client.Mutation(
Mutations.Capital.UpdateIssue.mutation,
{
variables: {
data: issueData,
},
},
)
const updated = issueMutation[Mutations.Capital.UpdateIssue.name]
if (updated == null) {
throw new Error(`Не удалось обновить задачу «${hash}» (пустой ответ мутации).`)
}
upsertEntry(index, {
entity_type: 'issue',
entity_hash: hash,
relative_path: entry.relative_path,
remote_updated_at: toIso(updated._updated_at),
content_etag_local: etag,
})
}
else if (type === 'story') {
const storyQuery = await ctx.client.Query(
Queries.Capital.GetStory.query,
{
variables: {
data: { story_hash: hash },
},
},
)
const remote = storyQuery[Queries.Capital.GetStory.name]
if (remote == null) {
throw new Error(`Требование «${hash}» не найдено на сервере.`)
}
assertSameRemoteVersion(entry.remote_updated_at, toIso(remote._updated_at), `требование ${hash}`)
const storyData: Mutations.Capital.UpdateStory.IInput['data'] = {
story_hash: hash,
title: String(parsed.data.title ?? ''),
description: parsed.body,
content_format: parsed.data.content_format as Mutations.Capital.UpdateStory.IInput['data']['content_format'],
status: parsed.data.status as Mutations.Capital.UpdateStory.IInput['data']['status'],
sort_order: Number(parsed.data.sort_order ?? 0),
}
if (parsed.data.project_hash) {
storyData.project_hash = String(parsed.data.project_hash)
}
if (parsed.data.issue_hash) {
storyData.issue_hash = String(parsed.data.issue_hash)
}
const storyMutation = await ctx.client.Mutation(
Mutations.Capital.UpdateStory.mutation,
{
variables: {
data: storyData,
},
},
)
const updated = storyMutation[Mutations.Capital.UpdateStory.name]
if (updated == null) {
throw new Error(`Не удалось обновить требование «${hash}» (пустой ответ мутации).`)
}
upsertEntry(index, {
entity_type: 'story',
entity_hash: hash,
relative_path: entry.relative_path,
remote_updated_at: toIso(updated._updated_at),
content_etag_local: etag,
})
}
remaining.delete(n)
}
await saveIndex(ctx.root, index)
await saveStaging(ctx.root, { paths: [...remaining] })
}
+83
View File
@@ -0,0 +1,83 @@
// remove: убрать пути из staging.json (аналог git restore --staged для .md).
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { loadStaging, normalizeRelativePath, saveStaging } from './index-store.js'
async function collectMarkdownFiles(absDir: string): Promise<string[]> {
const out: string[] = []
const entries = await fs.readdir(absDir, { withFileTypes: true })
for (const e of entries) {
const abs = path.join(absDir, e.name)
if (e.isDirectory()) {
const nested = await collectMarkdownFiles(abs)
out.push(...nested)
}
else if (e.isFile() && e.name.endsWith('.md')) {
out.push(abs)
}
}
return out
}
export interface RunRemoveResult {
readonly removedCount: number
readonly remainingPaths: string[]
readonly notStagedCount: number
}
/** Полностью очистить staging. */
export async function runClearStaging(root: string): Promise<void> {
await saveStaging(root, { paths: [] })
}
/**
* Убрать из staging указанные файлы или все .md под каталогом (пути относительно root).
* Пути, которых нет в staging, пропускаются (счётчик notStaged).
*/
export async function runRemove(root: string, targets: string[]): Promise<RunRemoveResult> {
if (targets.length === 0) {
throw new Error('Укажите файлы или каталоги: blago remove <путь> … либо blago remove --all')
}
const staging = await loadStaging(root)
const stagedSet = new Set(staging.paths.map(p => normalizeRelativePath(p)))
const toRemove = new Set<string>()
for (const t of targets) {
const abs = path.resolve(root, t)
let st: Awaited<ReturnType<typeof fs.stat>>
try {
st = await fs.stat(abs)
}
catch {
continue
}
const files: string[] = []
if (st.isDirectory()) {
files.push(...(await collectMarkdownFiles(abs)))
}
else if (st.isFile() && path.extname(abs).toLowerCase() === '.md') {
files.push(abs)
}
for (const file of files) {
toRemove.add(normalizeRelativePath(path.relative(root, file)))
}
}
let removedCount = 0
let notStagedCount = 0
for (const rel of toRemove) {
if (stagedSet.has(rel)) {
stagedSet.delete(rel)
removedCount += 1
}
else {
notStagedCount += 1
}
}
const remainingPaths = [...stagedSet].sort()
await saveStaging(root, { paths: remainingPaths })
return { removedCount, remainingPaths, notStagedCount }
}
+356
View File
@@ -0,0 +1,356 @@
// restore: один файл с сервера по пути из индекса (как git checkout -- file из удалённого).
import type { AuthenticatedContext } from '../session/index.js'
import type { IndexEntry, IndexFile } from './index-store.js'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { Queries } from '@coopenomics/sdk'
import {
issueToFrontmatterAndBody,
issueWorkspaceTitlesFromProjects,
parseBlagoMarkdown,
projectToFrontmatterAndBody,
serializeBlagoMarkdown,
storyToFrontmatterAndBody,
} from '../format/index.js'
import { sha256Hex } from '../lib/hash.js'
import { effectiveParentHash } from '../lib/parent-hash.js'
import {
findByRelativePath,
loadIndex,
loadStaging,
normalizeRelativePath,
saveIndex,
saveStaging,
upsertEntry,
} from './index-store.js'
import {
generateSlug,
issueFileRelativePath,
type ProjectPathModel,
storyFileRelativePath,
workspaceBasePath,
} from './layout.js'
interface CapitalProjectRow {
id?: number | null
project_hash: string
title?: string | null
description?: string | null
coopname: string
parent_hash?: string | null
parent_title?: string | null
status?: string | null
created_at?: Date | string | null
_updated_at?: Date | string | null
}
interface CapitalIssueRow {
id?: string | null
issue_hash: string
title: string
description?: string | null
project_hash: string
cycle_id?: string | null
status?: string | null
priority?: string | null
estimate?: number | null
submaster?: string | null
creators?: string[] | null
metadata?: unknown
_created_at?: Date | string | null
_updated_at?: Date | string | null
}
interface CapitalStoryRow {
_id?: string | null
story_hash: string
title: string
description?: string | null
content_format?: string | null
status?: string | null
project_hash?: string | null
issue_hash?: string | null
_created_at?: Date | string | null
_updated_at?: Date | string | null
}
function toUpdatedIso(v: Date | string | null | undefined): string {
if (v === undefined || v === null) {
return ''
}
if (v instanceof Date) {
return v.toISOString()
}
return new Date(v).toISOString()
}
function asProjectPathModel(p: CapitalProjectRow): ProjectPathModel {
const n = p.id
const capital_id = typeof n === 'number' && Number.isFinite(n) ? n : 0
return {
project_hash: p.project_hash,
title: p.title ?? '',
parent_hash: p.parent_hash,
capital_id,
}
}
/** Цепочка предков от листа к корню (для путей issue/story). */
async function loadProjectChainFromLeaf(
ctx: AuthenticatedContext,
leafHash: string,
firstFetchParent: string | undefined,
): Promise<Map<string, CapitalProjectRow>> {
const map = new Map<string, CapitalProjectRow>()
let hash: string | undefined = leafHash
let parentArg: string | undefined = firstFetchParent
for (let i = 0; i < 64 && hash; i++) {
const parentRef = effectiveParentHash(parentArg)
const queryResult = await ctx.client.Query(
Queries.Capital.GetProject.query,
{
variables: {
data: {
hash,
parent_hash: parentRef,
},
},
},
)
const row = queryResult[Queries.Capital.GetProject.name] as CapitalProjectRow | null | undefined
if (!row) {
throw new Error(`Проект «${hash}» не найден на сервере.`)
}
map.set(row.project_hash, row)
const up = effectiveParentHash(row.parent_hash)
if (!up) {
break
}
hash = up
parentArg = undefined
}
return map
}
function mapsFromProjectRows(rows: Map<string, CapitalProjectRow>): {
projectByHash: Map<string, ProjectPathModel>
projectRowByHash: Map<string, CapitalProjectRow>
} {
const projectByHash = new Map<string, ProjectPathModel>()
const projectRowByHash = new Map<string, CapitalProjectRow>()
for (const [h, r] of rows) {
projectByHash.set(h, asProjectPathModel(r))
projectRowByHash.set(h, r)
}
return { projectByHash, projectRowByHash }
}
async function writeRestoredFile(params: {
root: string
index: IndexFile
entry: IndexEntry
content: string
remoteUpdatedAt: string
}): Promise<void> {
const { root, index, entry, content, remoteUpdatedAt } = params
const rel = normalizeRelativePath(entry.relative_path)
const abs = path.join(root, rel)
await fs.mkdir(path.dirname(abs), { recursive: true })
await fs.writeFile(abs, content, 'utf8')
const etag = sha256Hex(await fs.readFile(abs, 'utf8'))
upsertEntry(index, {
entity_type: entry.entity_type,
entity_hash: entry.entity_hash,
relative_path: rel,
remote_updated_at: remoteUpdatedAt,
content_etag_local: etag,
})
}
async function unstagedPath(root: string, rel: string): Promise<void> {
const staging = await loadStaging(root)
const n = normalizeRelativePath(rel)
const next = staging.paths.filter(p => normalizeRelativePath(p) !== n)
if (next.length !== staging.paths.length) {
await saveStaging(root, { paths: next.sort() })
}
}
async function projectParentHintFromDisk(absFile: string, entityHash: string): Promise<string | undefined> {
try {
const raw = await fs.readFile(absFile, 'utf8')
const parsed = parseBlagoMarkdown(raw)
if (parsed.type !== 'project') {
return undefined
}
const h = String(parsed.data.hash ?? '')
if (h !== entityHash) {
return undefined
}
return effectiveParentHash(
parsed.data.parent_hash === undefined || parsed.data.parent_hash === null
? undefined
: String(parsed.data.parent_hash),
)
}
catch {
return undefined
}
}
export async function runRestore(ctx: AuthenticatedContext, userPath: string): Promise<void> {
const rel = normalizeRelativePath(userPath)
if (!rel || !rel.endsWith('.md')) {
throw new Error('Укажите относительный путь к .md файлу сущности (как в индексе).')
}
const index = await loadIndex(ctx.root)
const entry = findByRelativePath(index, rel)
if (!entry) {
throw new Error(
`Путь «${rel}» не найден в индексе (.blago/index.json). Выполните «blago pull» или проверьте путь.`,
)
}
if (entry.entity_type === 'project') {
const abs = path.join(ctx.root, rel)
const hint = await projectParentHintFromDisk(abs, entry.entity_hash)
const chain = await loadProjectChainFromLeaf(ctx, entry.entity_hash, hint)
const row = chain.get(entry.entity_hash)
if (!row) {
throw new Error(`Не удалось загрузить проект «${entry.entity_hash}» с сервера.`)
}
const { data, body } = projectToFrontmatterAndBody(row)
const content = serializeBlagoMarkdown(data, body)
await writeRestoredFile({
root: ctx.root,
index,
entry,
content,
remoteUpdatedAt: toUpdatedIso(row._updated_at),
})
await saveIndex(ctx.root, index)
await unstagedPath(ctx.root, rel)
return
}
if (entry.entity_type === 'issue') {
const issueQuery = await ctx.client.Query(
Queries.Capital.GetIssue.query,
{
variables: {
data: { issue_hash: entry.entity_hash },
},
},
)
const issueRow = issueQuery[Queries.Capital.GetIssue.name] as CapitalIssueRow | null | undefined
if (!issueRow) {
throw new Error(`Задача «${entry.entity_hash}» не найдена на сервере.`)
}
const chain = await loadProjectChainFromLeaf(ctx, issueRow.project_hash, undefined)
const { projectByHash, projectRowByHash } = mapsFromProjectRows(chain)
const proj = projectByHash.get(issueRow.project_hash)
if (!proj) {
throw new Error(`Проект задачи «${issueRow.project_hash}» не найден в цепочке на сервере.`)
}
const basePath = workspaceBasePath(proj, projectByHash)
const issueCapitalId
= issueRow.id !== undefined && issueRow.id !== null && String(issueRow.id).trim() !== ''
? String(issueRow.id)
: issueRow.issue_hash
const canonicalRel = issueFileRelativePath(issueRow.title, basePath, issueCapitalId)
if (normalizeRelativePath(canonicalRel) !== rel) {
throw new Error(
`Канонический путь задачи на сервере «${canonicalRel}» не совпадает с «${rel}». Выполните «blago pull» для выравнивания путей.`,
)
}
const workspace = issueWorkspaceTitlesFromProjects(issueRow.project_hash, projectRowByHash)
const { data, body } = issueToFrontmatterAndBody(issueRow, workspace)
const content = serializeBlagoMarkdown(data, body)
await writeRestoredFile({
root: ctx.root,
index,
entry,
content,
remoteUpdatedAt: toUpdatedIso(issueRow._updated_at),
})
await saveIndex(ctx.root, index)
await unstagedPath(ctx.root, rel)
return
}
if (entry.entity_type === 'story') {
const storyQuery = await ctx.client.Query(
Queries.Capital.GetStory.query,
{
variables: {
data: { story_hash: entry.entity_hash },
},
},
)
const storyRow = storyQuery[Queries.Capital.GetStory.name] as CapitalStoryRow | null | undefined
if (!storyRow || !storyRow.project_hash) {
throw new Error(`Требование «${entry.entity_hash}» не найдено на сервере или без project_hash.`)
}
const chain = await loadProjectChainFromLeaf(ctx, storyRow.project_hash, undefined)
const { projectByHash } = mapsFromProjectRows(chain)
const proj = projectByHash.get(storyRow.project_hash)
if (!proj) {
throw new Error(`Проект требования «${storyRow.project_hash}» не найден в цепочке на сервере.`)
}
const basePath = workspaceBasePath(proj, projectByHash)
let issueArg: { id: string, titleSlug: string } | undefined
if (storyRow.issue_hash) {
const issueQuery = await ctx.client.Query(
Queries.Capital.GetIssue.query,
{
variables: {
data: { issue_hash: storyRow.issue_hash },
},
},
)
const iss = issueQuery[Queries.Capital.GetIssue.name] as CapitalIssueRow | null | undefined
if (iss) {
const issueId = iss.id !== undefined && iss.id !== null && String(iss.id).trim() !== '' ? String(iss.id) : iss.issue_hash
issueArg = {
id: issueId,
titleSlug: generateSlug(iss.title) || 'issue',
}
}
}
const storyRecordId
= storyRow._id !== undefined && storyRow._id !== null && String(storyRow._id).trim() !== ''
? String(storyRow._id)
: storyRow.story_hash
const canonicalRel = storyFileRelativePath(
storyRow.title,
basePath,
storyRecordId,
storyRow.story_hash,
issueArg,
)
if (normalizeRelativePath(canonicalRel) !== rel) {
throw new Error(
`Канонический путь требования на сервере «${canonicalRel}» не совпадает с «${rel}». Выполните «blago pull» для выравнивания путей.`,
)
}
const { data, body } = storyToFrontmatterAndBody(storyRow)
const content = serializeBlagoMarkdown(data, body)
await writeRestoredFile({
root: ctx.root,
index,
entry,
content,
remoteUpdatedAt: toUpdatedIso(storyRow._updated_at),
})
await saveIndex(ctx.root, index)
await unstagedPath(ctx.root, rel)
return
}
throw new Error(`Неизвестный тип сущности в индексе: ${entry.entity_type}`)
}
+45
View File
@@ -0,0 +1,45 @@
// staging + список путей, у которых sha256 ≠ content_etag_local в индексе.
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { sha256Hex } from '../lib/hash.js'
import { info } from '../ui/output.js'
import { loadIndex, loadStaging, normalizeRelativePath } from './index-store.js'
export async function runStatus(root: string): Promise<void> {
const staging = await loadStaging(root)
const index = await loadIndex(root)
info('В индексе для отправки (staging):')
if (staging.paths.length === 0) {
info(' (пусто)')
}
else {
for (const p of staging.paths) {
info(` + ${normalizeRelativePath(p)}`)
}
}
info('Изменённые относительно последней синхронизации (по содержимому):')
let any = false
for (const e of index.entries) {
const abs = path.join(root, e.relative_path)
try {
const raw = await fs.readFile(abs, 'utf8')
const h = sha256Hex(raw)
if (h !== e.content_etag_local) {
info(` M ${e.relative_path} [${e.entity_type} ${e.entity_hash}]`)
any = true
}
}
catch {
info(` ? ${e.relative_path} (файл отсутствует)`)
any = true
}
}
if (!any) {
info(' (нет расхождений)')
}
}
+1
View File
@@ -0,0 +1 @@
export * from './output.js'
+40
View File
@@ -0,0 +1,40 @@
import pc from 'picocolors'
/** SDK при GraphQL-ошибках бросает массив объектов `{ message }`, не `Error` — String() даёт «[object Object]». */
export function formatThrownValue(e: unknown): string {
if (e instanceof Error) {
return e.message
}
if (Array.isArray(e)) {
return e.map(item => formatThrownValue(item)).filter(s => s.length > 0).join('\n')
}
if (typeof e === 'object' && e !== null) {
const o = e as Record<string, unknown>
if (typeof o.message === 'string' && o.message.length > 0) {
return o.message
}
try {
return JSON.stringify(e)
}
catch {
return String(e)
}
}
return String(e)
}
export function info(message: string): void {
console.log(pc.cyan(message))
}
export function success(message: string): void {
console.log(pc.green(message))
}
export function warn(message: string): void {
console.warn(pc.yellow(message))
}
export function error(message: string): void {
console.error(pc.red(message))
}
@@ -0,0 +1,72 @@
// Минимальные проверки полей перед push.
import type { EntityFrontmatterType, ParsedBlagoFile } from '../format/index.js'
export class BlagoValidationError extends Error {
constructor(message: string) {
super(message)
this.name = 'BlagoValidationError'
}
}
function reqString(data: Record<string, unknown>, key: string, entity: string): string {
const v = data[key]
if (v === undefined || v === null || String(v).trim() === '') {
throw new BlagoValidationError(`${entity}: отсутствует или пустое поле frontmatter «${key}»`)
}
return String(v)
}
function optStringArray(data: Record<string, unknown>, key: string, entity: string): string[] {
const v = data[key]
if (v === undefined || v === null) {
return []
}
if (!Array.isArray(v)) {
throw new BlagoValidationError(`${entity}: поле «${key}» должно быть массивом строк`)
}
return v.map(x => String(x))
}
export function validateParsedForPush(parsed: ParsedBlagoFile): {
type: EntityFrontmatterType
hash: string
} {
const { type, data } = parsed
if (type === 'project') {
reqString(data, 'title', 'project')
const hash = reqString(data, 'hash', 'project')
reqString(data, 'coopname', 'project')
return { type, hash }
}
if (type === 'issue') {
reqString(data, 'title', 'issue')
const hash = reqString(data, 'hash', 'issue')
reqString(data, 'project_hash', 'issue')
reqString(data, 'status', 'issue')
reqString(data, 'priority', 'issue')
if (data.estimate === undefined || data.estimate === null || Number.isNaN(Number(data.estimate))) {
throw new BlagoValidationError('issue: поле «estimate» должно быть числом')
}
optStringArray(data, 'creators', 'issue')
optStringArray(data, 'labels', 'issue')
if (data.created_by !== undefined && data.created_by !== null && typeof data.created_by !== 'string') {
throw new BlagoValidationError('issue: поле created_by должно быть строкой')
}
return { type, hash }
}
if (type === 'story') {
reqString(data, 'title', 'story')
const hash = reqString(data, 'hash', 'story')
reqString(data, 'content_format', 'story')
reqString(data, 'status', 'story')
if (data.created_by !== undefined && data.created_by !== null && typeof data.created_by !== 'string') {
throw new BlagoValidationError('story: поле created_by должно быть строкой')
}
if (!data.project_hash && !data.issue_hash) {
throw new BlagoValidationError('story: нужен project_hash и/или issue_hash')
}
return { type, hash }
}
throw new BlagoValidationError(`Неизвестный тип сущности: ${type}`)
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noEmit": true,
"esModuleInterop": true,
"skipLibCheck": true,
"verbatimModuleSyntax": true,
"isolatedModules": true
},
"include": ["src/**/*.ts", "build.config.ts"]
}
+15
View File
@@ -0,0 +1,15 @@
EOSIO_PUB_KEY=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
EOSIO_PRV_KEY=5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
DATADIR=blockchain-data
BPACCOUNT=eosio
PASSWORD=PW5JGe4WsTPGrjnMzkGd4wfVdzoHCEySgbyq2WBWGAxSfevbXqAG4
SERVER_SECRET=SECRET
SERVER_URL=http://127.0.0.1:2998
MONGO_URI=mongodb://127.0.0.1:27017/cooperative-x
SIMPLE_EXPLORER_API=http://localhost:4000
SKIP_BLOCK_FETCH=TRUE
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DATABASE=voskhod
+14
View File
@@ -0,0 +1,14 @@
.cache
.DS_Store
.idea
*.log
*.tgz
coverage
dist
lib-cov
logs
node_modules
temp
.env
blockchain-data/*
wallet-data/*
+22
View File
@@ -0,0 +1,22 @@
{
"configurations": [
{
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/lib/**",
"${workspaceFolder}/lib/eosiolib/contracts/**",
"${workspaceFolder}/lib/eosiolib/core/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"int128_t=__int128_t",
"uint128_t=__uint128_t"
],
"cStandard": "c17",
"cppStandard": "c++20"
}
],
"version": 4
}
+68
View File
@@ -0,0 +1,68 @@
{
// Enable the ESlint flat config support
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
// "source.fixAll": "explicit",
// "source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
// "[cpp]": {
// "editor.defaultFormatter": "ms-vscode.cpptools",
// },
// "C_Cpp.vcFormat.indent.withinParentheses": "indent",
"C_Cpp.errorSquiggles": "disabled"
}
+102
View File
@@ -0,0 +1,102 @@
# @coopenomics/boot
## Назначение
CLI-утилита для инициализации блокчейна EOSIO и развёртывания кооператива. Выполняет полный цикл: запуск ноды → создание аккаунтов → активация фич → деплой контрактов → выпуск токенов → загрузка шаблонов → создание кооператива и совета.
## Структура
```
src/
├── index.ts — CLI точка входа (commander): boot, boot:clean, boot:extra,
│ start, stop, clear, cleos, deploy, create-coop, unlock
├── init/
│ ├── booter.ts — Главные сценарии: boot(), bootClean(), bootExtra()
│ ├── infra.ts — startInfra() — базовая инфраструктура блокчейна
│ │ installInitialData() — начальные данные (пользователи, совет)
│ │ installExtraData() — расширенные данные
│ └── cooperative.ts — CooperativeClass — создание программ ЦПП, регистрация
│ └── participant.ts — Логика регистрации пайщика
├── blockchain/
│ └── index.ts — Класс Blockchain — обёртка над eosjs для работы с EOSIO
│ (создание аккаунтов, деплой контрактов, транзакции, токены)
├── configs/
│ ├── index.ts — Главная конфигурация (аккаунты, ключи, токен, эмиссия)
│ ├── networks.ts — Настройки сети (протокол, хост, порт)
│ ├── contracts.ts — Список контрактов и путей к их WASM/ABI файлам
│ └── protocol_features/ — JSON-файлы фич для активации на блокчейне
├── docker/ — Управление Docker-контейнером ноды блокчейна
│ ├── container.ts, run.ts, stop.ts, find.ts, exec.ts, deploy.ts
│ ├── health.ts — Проверка готовности ноды
│ └── purge.ts — Очистка данных блокчейна и БД
├── *-init.ts — Инициализация реляционной БД (пользователи, vault, статус, extensions)
├── tests/ — Интеграционные тесты
│ ├── capital.test.ts — Основной тест (полный цикл ЦПП)
│ ├── wallet.test.ts — Тесты кошелька
│ ├── registrator.test.ts — Тесты регистрации
│ └── capital/ — Вспомогательные функции для тестов капитала
└── utils/ — Утилиты (sleep, randomHash, randomUsername, и т.д.)
```
## Порядок загрузки (boot flow)
1. **startInfra()** — основа:
- Инициализация `Blockchain` с ключами
- Создание системных аккаунтов (eosio.token, registrator, soviet, и т.д.)
- Активация фич протокола (PREACTIVATE_FEATURE → остальные)
- Деплой смарт-контрактов (eosio.boot → eosio.system → все остальные)
- Создание и выпуск токенов
- Загрузка шаблонов документов из `@coopenomics/factory` Registry
- Настройка ресурсов (powerup)
2. **installInitialData()** — данные кооператива:
- Сохранение организации, платёжных методов, физлиц в MongoDB
- Сохранение переменных кооператива (vars)
- Регистрация пользователей в блокчейне
- Инициализация реляционной БД (пользователи, vault)
- Создание совета (board)
- Создание программ ЦПП (Благорост, маркетплейс)
3. **bootClean()** — минимальная загрузка (только инфра + программы, без данных)
4. **bootExtra()** — расширенная загрузка (5 членов совета, дополнительные пайщики)
## Режимы контрактов
- **test** — контракты собранные с флагом `test` (позволяют boot с 1 членом совета)
- **production** — стандартные контракты (требуют кворум)
Пути к WASM/ABI файлам указаны в `configs/contracts.ts`, относительно `../contracts/build/contracts/`.
## Тесты
- Фреймворк: **Vitest**
- Основной тест: `src/tests/capital.test.ts` — полный цикл ЦПП (создание программ, проектов, инвестирование, голосование, конвертация)
- Требует: полностью загруженный блокчейн (`pnpm run reboot`), MongoDB, реляционную БД
### Запуск
```
pnpm --filter @coopenomics/boot test
```
### Важно: Duplicate transaction
EOSIO отклоняет транзакции с одинаковым хешем (TAPOS block + action data). При повторных вызовах одинаковых действий нужен `await sleep(500)` между ними.
## Скрипты package.json
| Скрипт | Описание |
|--------|----------|
| `boot` | Полная загрузка: инфра + данные + кооператив |
| `boot:clean` | Чистая загрузка: только инфра + программы |
| `boot:extra` | Расширенная загрузка: инфра + 5 членов совета |
| `create-coop` | Создание кооператива после boot:clean |
| `test` | Vitest (capital.test.ts, timeout 240s) |
| `test:all` | Все тесты |
| `clear` | Очистка блокчейна и перезапуск ноды |
## Зависимости от других компонентов
- `cooptypes` (workspace) — типы контрактов
- `@coopenomics/factory` (workspace) — реестр шаблонов, сохранение данных в MongoDB
+80
View File
@@ -0,0 +1,80 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.10](https://github.com/coopenomics/mono/compare/v2.2.9...v2.2.10) (2025-03-27)
**Note:** Version bump only for package @coopenomics/boot
## [2.2.9](https://github.com/coopenomics/mono/compare/v2.2.8...v2.2.9) (2025-03-12)
**Note:** Version bump only for package @coopenomics/boot
## [0.1.8](https://github.com/coopenomics/contracts/compare/boot@0.1.7...boot@0.1.8) (2024-12-24)
**Note:** Version bump only for package boot
## [0.1.7](https://github.com/coopenomics/contracts/compare/boot@0.1.7-alpha.0...boot@0.1.7) (2024-10-16)
**Note:** Version bump only for package boot
## [0.1.6](https://github.com/coopenomics/contracts/compare/boot@0.1.5...boot@0.1.6) (2024-10-13)
**Note:** Version bump only for package boot
## [0.1.5](https://github.com/coopenomics/contracts/compare/boot@0.1.5-alpha.0...boot@0.1.5) (2024-09-26)
**Note:** Version bump only for package boot
## [0.1.4](https://github.com/coopenomics/contracts/compare/boot@0.1.4-alpha.0...boot@0.1.4) (2024-08-13)
**Note:** Version bump only for package boot
## [0.1.3](https://github.com/coopenomics/contracts/compare/boot@0.1.3-alpha.0...boot@0.1.3) (2024-08-13)
**Note:** Version bump only for package boot
## [0.1.2](https://github.com/coopenomics/contracts/compare/boot@0.1.2-alpha.0...boot@0.1.2) (2024-08-10)
**Note:** Version bump only for package boot
## [0.1.1](https://github.com/coopenomics/contracts/compare/boot@0.1.1-alpha.1...boot@0.1.1) (2024-08-09)
**Note:** Version bump only for package boot
+100
View File
@@ -0,0 +1,100 @@
# 🚀 @coopenomics/boot
CLI-утилита для инициализации блокчейн-инфраструктуры кооператива. Создаёт системные аккаунты, устанавливает смарт-контракты, настраивает токены и разворачивает кооперативную среду. Включает интеграционные тесты для основных подсистем платформы.
## Основные возможности
- Полный цикл развёртывания блокчейна EOSIO — от запуска ноды до создания кооператива
- Установка системных и прикладных смарт-контрактов
- Инициализация токенов и создание тестовых данных
- Управление Docker-контейнерами блокчейн-ноды
- Интеграционные тесты: паевые взносы (capital), кошелёк (wallet), регистрация участников (registrator)
- Поддержка нескольких режимов загрузки: стандартный, чистый и расширенный
## Установка
Компонент является частью монорепозитория. Установка зависимостей из корня проекта:
```bash
pnpm install
```
Или только для этого компонента:
```bash
pnpm install --filter @coopenomics/boot
```
## Скрипты
| Скрипт | Команда | Описание |
|--------|---------|----------|
| `boot` | `pnpm run boot` | Запуск блокчейна и установка контрактов |
| `boot:clean` | `pnpm run boot:clean` | Чистый запуск с полной переустановкой |
| `boot:extra` | `pnpm run boot:extra` | Расширенная инициализация с дополнительными данными |
| `deploy` | `pnpm run deploy` | Развёртывание контрактов на запущенной ноде |
| `cli` | `pnpm run cli` | Интерактивный командный интерфейс (Commander.js) |
| `clear` | `pnpm run clear` | Очистка данных блокчейна |
| `start` | `pnpm run start` | Перезапуск ноды |
| `stop` | `pnpm run stop` | Остановка ноды |
| `test` | `pnpm run test` | Интеграционные тесты (capital) |
| `test:all` | `pnpm run test:all` | Все интеграционные тесты |
## Конфигурация
Скопируйте `.env-example` в `.env` и настройте переменные окружения:
- Endpoint блокчейн-ноды (API)
- Строка подключения к MongoDB
- Строка подключения к реляционной БД
- Приватные ключи для развёртывания контрактов
Подробное описание переменных — в файле `.env-example`.
## Тестирование
Перед запуском тестов необходима работающая инфраструктура (см. `docker-compose.yaml` в корне проекта):
```bash
pnpm --filter @coopenomics/boot run test
pnpm --filter @coopenomics/boot run test:all
```
Тесты используют **Vitest** с таймаутом 240 секунд — это связано с ожиданием подтверждения блокчейн-транзакций.
## Архитектура
```
src/
├── index.ts # Точка входа CLI (Commander.js)
├── init/ # Логика инициализации
│ ├── booter.ts # Загрузчик блокчейна
│ ├── cooperative.ts # Настройка кооператива
│ ├── infra.ts # Инфраструктурная инициализация
│ └── participant.ts # Создание участников
├── blockchain/ # Взаимодействие с EOSIO (eosjs)
├── docker/ # Управление Docker-контейнерами
├── configs/ # Конфигурация ноды и протокол-фичи
├── tests/ # Интеграционные тесты
│ ├── capital.test.ts # Тесты паевых взносов
│ ├── wallet.test.ts # Тесты кошелька
│ └── registrator.test.ts # Тесты регистрации
└── utils/ # Вспомогательные утилиты
scripts/
├── reboot.sh # Полный перезапуск
├── clean_reboot.sh # Чистый перезапуск
└── extra_reboot.sh # Расширенный перезапуск
```
## Ключевые зависимости
- **eosjs** — взаимодействие с блокчейном EOSIO
- **commander** — CLI-фреймворк
- **dockerode** — управление Docker-контейнерами из Node.js
- **mongoose** — работа с MongoDB
- **pg** — работа с реляционной БД
- **vitest** — тестирование
## Лицензия
[BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru)
+19
View File
@@ -0,0 +1,19 @@
// @ts-check
import antfu from '@antfu/eslint-config'
export default antfu(
{
ignores: [
// eslint ignore globs here
],
},
{
rules: {
'no-unused-vars': 'off',
'unused-imports/no-unused-vars': 'off',
'no-console': 'off',
'unicorn/prefer-number-properties': 'off',
// overrides
},
},
)
+63
View File
@@ -0,0 +1,63 @@
{
"name": "@coopenomics/boot",
"type": "module",
"version": "2026.4.6-4",
"private": true,
"packageManager": "pnpm@9.0.6",
"description": "CLI-утилита инициализации блокчейна и кооператива",
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
"license": "",
"keywords": [],
"scripts": {
"enter": "./scripts/enter.sh",
"cli": "esno src/index.ts",
"deploy": "esno src/index.ts deploy",
"cleos": "esno src/index.ts cleos",
"unlock": "esno src/index.ts unlock",
"boot": "esno src/index.ts boot",
"boot:clean": "esno src/index.ts boot:clean",
"boot:extra": "esno src/index.ts boot:extra",
"reboot:clean": "cd components/boot/scripts && bash clean_reboot.sh",
"reboot:extra": "cd components/boot/scripts && bash extra_reboot.sh",
"create-coop": "esno src/index.ts create-coop",
"clear": "./scripts/clear.sh",
"start": "./scripts/restart.sh",
"stop": "./scripts/stop.sh",
"test": "vitest run src/tests/capital.test.ts --testTimeout=240000",
"test:all": "vitest run --testTimeout=240000"
},
"dependencies": {
"@coopenomics/factory": "workspace:*",
"@types/pg": "^8.16.0",
"axios": "^1.13.5",
"chai": "^5.1.2",
"commander": "^12.1.0",
"cooptypes": "workspace:*",
"dockerode": "^4.0.2",
"dotenv": "^16.4.5",
"eosjs": "^22.1.0",
"eosjs-api": "^7.0.4",
"eosjs-ecc": "^4.0.7",
"execa": "^9.5.2",
"mocha": "^10.7.3",
"mongoose": "^9.2.2",
"pg": "^8.16.3"
},
"devDependencies": {
"@antfu/eslint-config": "^2.18.0",
"@antfu/ni": "^0.21.12",
"@antfu/utils": "^0.7.8",
"@types/dockerode": "^3.3.31",
"@types/node": "^20.12.12",
"bumpp": "^9.4.1",
"eslint": "^9.2.0",
"esno": "^4.7.0",
"pnpm": "^9.1.1",
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.4.5",
"unbuild": "^2.0.0",
"vite": "^5.2.11",
"vitest": "^1.6.0"
}
}
+80
View File
@@ -0,0 +1,80 @@
#!/bin/zsh
# Определение ассоциативных массивов
typeset -A contract_params_test contract_params_prod
contract_params_test=(
[ano]="ano"
[gateway]="gateway"
[draft]="draft"
[marketplace]="marketplace"
[soviet]="soviet"
[registrator]="registrator"
[system]="eosio"
[fund]="fund"
)
contract_params_prod=(
[ano]="ano"
[gateway]="gateway"
[draft]="draft"
[marketplace]="marketplace"
[soviet]="soviet"
[registrator]="registrator"
[system]="eosio"
[fund]="fund"
[template]="template"
)
# Выбор массива параметров в зависимости от входящего аргумента
if [ "$2" = "prod" ]; then
contract_params=("${(@kv)contract_params_prod}")
elif [ "$2" = "test" ]; then
contract_params=("${(@kv)contract_params_test}")
else
contract_params=("${(@kv)contract_params_test}")
fi
# Форматирование строки для переменной окружения
contract_params_str=""
for key value in "${(@kv)contract_params}"; do
contract_params_str+="${key}=${value},"
done
# Проверяем, задан ли аргумент для контрактов
if [ -n "$1" ]; then
contracts=$1
else
contracts="*"
fi
# Запуск контейнера Docker
docker run --rm --name cdt_v4.1.0 \
--volume $(pwd):/project \
-w /project \
--env CONTRACT_PARAMS="$contract_params_str" \
dicoop/cdt_v4.1.0 /bin/bash -c "
cd ../cpp
# Перебираем все папки в директории
for folder in $contracts ; do
# Убираем слэш из названия папки, чтобы получить имя контракта
contract=\${folder%/}
contract=\${contract##*/}
echo \"Сборка контракта \$contract в \$folder...\"
# Переходим в директорию контракта
cd \$folder
# Извлекаем параметр для -contract из переменной окружения
contract_param=\$(echo \$CONTRACT_PARAMS | grep -o \"\$contract=[^,]*\" | cut -d'=' -f2)
# Компилируем с использованием параметра -contract
cdt-cpp -abigen -I include -R include -contract \$contract_param -o \$contract.wasm \$contract.cpp
# Возвращаемся назад
cd ..
echo \"Сборка контракта \$contract завершена!\"
done
"
+1
View File
@@ -0,0 +1 @@
cd ../system/build && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -Dcdt_DIR="/cdt/build/lib/cmake/cdt" -Dleap_DIR="/blockchain/build/lib/cmake/leap" .. && make -j 2
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
# Запускаем boot процесс
echo "Запускаем boot процесс..."
pnpm run boot:clean
# Запускаем parser
echo "Запускаем parser..."
docker compose up -d cooparser
echo "Запускаем контроллер..."
docker compose restart coopback || true
echo "Перезапуск завершен!"
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# Передача аргументов cleos
docker exec -it node /usr/local/bin/cleos "$@"
+108
View File
@@ -0,0 +1,108 @@
#!/bin/bash
# Импорт параметров сетей
source ./networks.sh
# Устанавливаем значения по умолчанию
network_param=$local
account_name_param=""
network=""
# Получаем первый аргумент, который должен быть именем контракта
contract_name=$1
rm /Users/darksun/testnet/wallet/keosd.sock
./kill.sh
# Проверяем наличие параметра -n и устанавливаем соответствующую сеть
while [[ $# -gt 0 ]]; do
case $1 in
-n)
network=$2
shift
shift
;;
-t)
account_name_param=$2
shift
shift
;;
*)
shift
;;
esac
done
# Устанавливаем значение сети из networks.sh
case $network in
prod)
network_param=$prod
;;
test)
network_param=$test
;;
local)
network_param=$local
;;
esac
# Разблокировка кошелька
./unlock.sh
# Функция для установки контракта
deploy_contract() {
local contract=$1
local dir=$2
local account_name=$3
echo "Устанавливаем контракт $contract на аккаунт $account_name..."
if [ -z "$account_name_param" ]; then
echo ./cleos.sh $network_param set contract $account_name $dir -p $account_name
./cleos.sh $network_param set contract $account_name $dir -p $account_name
else
echo ./cleos.sh $network_param set contract $account_name_param $dir -p $account_name_param
./cleos.sh $network_param set contract $account_name_param $dir -p $account_name_param
fi
}
# Если контракт указан, деплоим только его
if [ ! -z "$contract_name" ]; then
ls ./
echo "Проверка наличия папки для контракта $contract_name..."
if [ -d "./$contract_name" ]; then
echo "Папка контракта найдена. Содержимое папки:"
deploy_contract $contract_name "/contracts/cpp/$contract_name" $contract_name
elif [[ "$contract_name" == "eosio.msig" || "$contract_name" == "eosio.token" || "$contract_name" == "eosio.wrap" ]]; then
echo "Папка системного контракта найдена. Содержимое папки:"
ls "/contracts/cpp/system/build/contracts/$contract_name"
deploy_contract $contract_name "/contracts/cpp/system/build/contracts/$contract_name" $contract_name
elif [ "$contract_name" == "eosio.system" ]; then
echo "Папка системного контракта найдена. Содержимое папки:"
ls "/contracts/cpp/system/build/contracts/$contract_name"
deploy_contract $contract_name "/contracts/cpp/system/build/contracts/$contract_name" "eosio"
elif [ "$contract_name" == "eosio.boot" ]; then
echo "Папка системного контракта найдена. Содержимое папки:"
ls "/contracts/cpp/system/build/contracts/$contract_name"
deploy_contract $contract_name "/contracts/cpp/system/build/contracts/$contract_name" "eosio"
else
echo "Контракт $contract_name не найден"
fi
exit 0
fi
# Перебираем все папки с контрактами на верхнем уровне, исключая 'system'
for dir in $(find /contracts/cpp/* -maxdepth 0 -type d ! -name 'system'); do
contract=$(basename $dir)
echo "Найдена папка $dir для контракта $contract. Содержимое папки:"
ls $dir
deploy_contract $contract $dir $contract
done
# Исключение для контрактов в папке system
special_system_contracts=("eosio.msig" "eosio.token" "eosio.wrap")
for contract in "${special_system_contracts[@]}"; do
echo "Найдена папка системного контракта для $contract. Содержимое папки:"
ls "/contracts/cpp/system/build/contracts/$contract"
deploy_contract $contract "/contracts/cpp/system/build/contracts/$contract" $contract
done
deploy_contract "eosio.system" "/contracts/cpp/system/build/contracts/eosio.system" "eosio"
+4
View File
@@ -0,0 +1,4 @@
docker run --rm -it --name cdt \
--volume $(pwd)/:/project \
-w /project \
dicoop/blockchain_v5.1.1:dev /bin/bash

Some files were not shown because too many files have changed in this diff Show More