chore: каркас моно-репо и скрипт первичной подготовки dev-ВМ

Содержимое первого коммита:
- Структура моно-репо: cmd/{lk-gateway,m2m-core,nsd-adapter,lk-emulator,notify}/,
  internal/{m2m,nsdxml,fansystore,notify}/, services/crypto-service/,
  web/admin-ui/, deploy/docker-compose/, migrations/, docs/.
- Заглушки main.go во всех cmd/ — make build проходит из коробки.
- Makefile с целями build/test/lint/fmt/vet/tidy/ci/compose-up/compose-down.
- .golangci.yml, .gitignore, README.md (на русском).
- .claude/settings.json — общие ограничения Claude Code для команды
  (запрет sudo, rm -rf, доступа к /etc/cryptopro, /var/cryptopro).
- README в каждом каталоге — назначение и стадия (M1..M5).
- docs/architecture/overview.md — выжимка из плана проекта.
- docs/fansy-contract/v1/, docs/lk-contract/v1/ — точки сборки контрактов
  с командами Fansy и ЛК клиента.
- deploy/docker-compose/docker-compose.yml — dev-стек (PostgreSQL, MinIO).
- scripts/setup-dev-vm.sh — первичная подготовка dev-ВМ под РЕД ОС 7.x
  и Ubuntu 22.04+ (для компаний без бюджета на лицензии); ставит Go 1.23,
  Liberica JDK 21, Node.js 20 LTS, Podman, podman-compose, Claude Code CLI;
  создаёт пользователя dev, /srv/dev, аудит-history. Идемпотентен.
- scripts/README.md — описание скрипта и ограничений.

Что НЕ коммитим:
- Секреты, ключи, сертификаты — закрыто в .gitignore.
- Локальные настройки Claude Code (settings.local.json) и сессионные
  каталоги (.claude/projects/, .claude/worktrees/, .claude/logs/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-05-05 14:42:18 +03:00
parent 6222612af2
commit d5b5597c67
25 changed files with 1037 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(go *)",
"Bash(make *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git branch *)",
"Bash(git fetch *)",
"Bash(git pull *)",
"Bash(git remote -v)",
"Bash(podman *)",
"Bash(podman-compose *)",
"Bash(docker *)",
"Bash(docker-compose *)",
"Bash(xmlstarlet *)",
"Bash(jq *)",
"Bash(ls *)",
"Bash(cat *)",
"Bash(grep *)",
"Bash(find *)",
"Bash(npm run *)",
"Bash(npx *)",
"Bash(./scripts/*)"
],
"deny": [
"Bash(rm -rf /*)",
"Bash(rm -rf ~)",
"Bash(rm -rf $HOME)",
"Bash(sudo *)",
"Bash(dd *)",
"Bash(mkfs *)",
"Bash(curl * | sh)",
"Bash(curl * | bash)",
"Bash(wget * | sh)",
"Bash(wget * | bash)",
"Read(/etc/cryptopro/**)",
"Read(/var/cryptopro/**)",
"Read(/etc/ipsec.d/**)",
"Read(/root/**)",
"Read(/home/admin/**)",
"Write(/etc/**)",
"Write(/var/**)",
"Write(/root/**)",
"Write(/home/admin/**)"
]
}
}
+60
View File
@@ -0,0 +1,60 @@
# Сборки
/bin/
/dist/
*.exe
*.test
*.out
# Go
vendor/
go.sum.lock
# Java / crypto-service
services/crypto-service/build/
services/crypto-service/.gradle/
services/crypto-service/target/
*.jar
*.class
# Node / admin-ui
web/admin-ui/node_modules/
web/admin-ui/dist/
# Локальные конфиги и секреты
.env
.env.local
*.local.yaml
*.local.yml
secrets/
certs/private/
# Контейнеры ключей и сертификаты — никогда не коммитим
*.p12
*.pfx
*.pem
*.key
*.cer.priv
# IDE и временные файлы
.idea/
.vscode/
*.swp
.DS_Store
# Логи
*.log
logs/
# Тестовые артефакты
coverage.out
coverage.html
test-results/
# Claude Code сессионные логи и локальные настройки
.claude/logs/
.claude/settings.local.json
.claude/projects/
.claude/worktrees/
# macOS
.DS_Store
+44
View File
@@ -0,0 +1,44 @@
run:
timeout: 3m
go: "1.23"
linters:
disable-all: true
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- revive
- gocritic
- bodyclose
- errorlint
- nilerr
- prealloc
- unconvert
- unparam
- whitespace
linters-settings:
goimports:
local-prefixes: git.zetit.ru/zuevav/Bridge-and-Join-s
misspell:
locale: US
revive:
rules:
- name: var-naming
disabled: false
- name: package-comments
disabled: true
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
- unparam
+52
View File
@@ -0,0 +1,52 @@
.PHONY: help build test lint fmt vet clean tidy ci compose-up compose-down
GO ?= go
GOLANGCI_LINT ?= golangci-lint
COMPOSE ?= podman-compose
help:
@echo "Цели:"
@echo " make build - сборка всех бинарников Go"
@echo " make test - юнит-тесты"
@echo " make lint - golangci-lint"
@echo " make fmt - gofmt + goimports"
@echo " make vet - go vet"
@echo " make tidy - go mod tidy"
@echo " make ci - все проверки CI локально"
@echo " make compose-up - поднять локальный стек (PostgreSQL, MinIO, заглушки)"
@echo " make compose-down - остановить локальный стек"
@echo " make clean - удалить артефакты"
build:
@mkdir -p bin
$(GO) build -o bin/lk-gateway ./cmd/lk-gateway
$(GO) build -o bin/m2m-core ./cmd/m2m-core
$(GO) build -o bin/nsd-adapter ./cmd/nsd-adapter
$(GO) build -o bin/lk-emulator ./cmd/lk-emulator
$(GO) build -o bin/notify ./cmd/notify
test:
$(GO) test ./... -race -count=1
lint:
$(GOLANGCI_LINT) run ./...
fmt:
$(GO) fmt ./...
vet:
$(GO) vet ./...
tidy:
$(GO) mod tidy
ci: tidy fmt vet lint test build
compose-up:
$(COMPOSE) -f deploy/docker-compose/docker-compose.yml up -d
compose-down:
$(COMPOSE) -f deploy/docker-compose/docker-compose.yml down
clean:
rm -rf bin/ dist/ coverage.out coverage.html
+61
View File
@@ -0,0 +1,61 @@
# Bridge and Join's — сервис M2M-перевода ценных бумаг через НРД
Внутренний модуль брокера для обмена с НРД сообщениями о переводе ценных бумаг
(M2M, ЭДО НРД, сервис MOEX MOST). Реализация — российское ПО, готовится к
включению в Реестр (ПП РФ № 1236).
## Назначение
Связующий модуль между:
- личным кабинетом клиента на платформе ESIA Finance (источник заявлений инвестора);
- учётной системой `Fansy` (источник реквизитов и остатков, выгрузка в нашу
принимающую БД делает отдельная команда);
- НРД через Интеграционный шлюз / Web-сервис ONYX (формат XML по схемам
`M2MTransferRequest/Decision/Response/Handbook/ParticipantForm`).
Подробное описание архитектуры, потока, регуляторных требований, SLA и
дорожной карты — см. `docs/architecture/overview.md` и план в репозитории
команды.
## Стек
- Go 1.23+ — ядро (`cmd/`, `internal/`).
- Java 21 (Liberica JDK) + КриптоПро JCP — `services/crypto-service` (XMLDSig
по ГОСТ Р 34.10-2012, серверная подпись действий оператора).
- React + Vite — `web/admin-ui` (журнал, ручное согласование, сертификаты).
- PostgreSQL Pro Certified — основная БД (`m2m-core`, `fansy-store`, аудит).
- MinIO — эталоны подписанных XML.
- Развёртывание — одна ВМ внутри контура брокера, `podman-compose`.
## Документация
- `DOC/` — оригинальная документация НРД (схемы XSD, инструкции, эталоны).
- `docs/architecture/overview.md` — обзор архитектуры.
- `docs/fansy-contract/v1/` — DDL и контракт данных для команды Fansy.
- `docs/lk-contract/v1/` — OpenAPI контракта с ЛК клиента (ESIA Finance).
- `scripts/setup-dev-vm.sh` — скрипт первичной подготовки dev-ВМ.
## Быстрый старт (на dev-ВМ)
```bash
# под admin (один раз на ВМ)
sudo bash scripts/setup-dev-vm.sh
# под dev
git clone https://git.zetit.ru/zuevav/Bridge-and-Join-s.git
cd Bridge-and-Join-s
make build
make test
```
## Ветви
- `main` — стабильная.
- `feature/<имя>` — рабочие ветви, в основную через merge request.
- `claude/<...>` — служебные ветви автоматизированных правок.
## Лицензия
Внутренняя разработка. Лицензионная модель будет определена при подаче
в Реестр российского ПО.
+20
View File
@@ -0,0 +1,20 @@
// Package main — сервис lk-emulator. Эмулятор ЛК клиента (ESIA Finance API V1)
// на время, пока реальный ЛК не готов. Позволяет «как будто загрузить»
// заявление через веб-форму и запустить полный путь обработки документа.
//
// Когда реальный ЛК подключится — эмулятор остаётся как тестовый инструмент
// в QA-окружении.
//
// На этапе M1 — заглушка.
package main
import (
"fmt"
"os"
)
const serviceName = "lk-emulator"
func main() {
fmt.Fprintf(os.Stdout, "%s: запуск (заглушка M1)\n", serviceName)
}
+17
View File
@@ -0,0 +1,17 @@
// Package main — сервис lk-gateway. Принимает заявления от ЛК клиента
// (платформа ESIA Finance, /api/v1/back_office/...), валидирует их подпись,
// передаёт в m2m-core, отдаёт callback-статусы обратно в ЛК.
//
// На этапе M1 — заглушка. Реализация контракта — M2.
package main
import (
"fmt"
"os"
)
const serviceName = "lk-gateway"
func main() {
fmt.Fprintf(os.Stdout, "%s: запуск (заглушка M1)\n", serviceName)
}
+17
View File
@@ -0,0 +1,17 @@
// Package main — сервис m2m-core. Бизнес-логика и FSM сделки M2M-перевода:
// идемпотентность по GUID, валидация по XSD, метрики SLA, ветка ручного
// согласования и таймаут-отказа MOST.
//
// На этапе M1 — заглушка.
package main
import (
"fmt"
"os"
)
const serviceName = "m2m-core"
func main() {
fmt.Fprintf(os.Stdout, "%s: запуск (заглушка M1)\n", serviceName)
}
+19
View File
@@ -0,0 +1,19 @@
// Package main — сервис notify. Отправка уведомлений по нескольким каналам:
// e-mail (SMTP), Yandex Messenger (Yandex 360), WebSocket-push в admin-ui,
// плюс расширяемая модель провайдеров-плагинов (smtp, yandex360, telegram,
// mattermost, webhook) под единый интерфейс Notifier — для тиражирования
// продукта другим компаниям.
//
// На этапе M1 — заглушка.
package main
import (
"fmt"
"os"
)
const serviceName = "notify"
func main() {
fmt.Fprintf(os.Stdout, "%s: запуск (заглушка M1)\n", serviceName)
}
+22
View File
@@ -0,0 +1,22 @@
// Package main — сервис nsd-adapter. Транспорт к НРД:
// - Интеграционный шлюз через REST API (основной канал, ИШ сам подписывает);
// - Web-сервис ONYX напрямую (резерв);
// - Файловый шлюз / обменные папки ИШ (fallback).
//
// Сериализация и парсинг XML по схемам M2MSchemas в windows-1251,
// маршрутизация по типам пакетов (#M2MTR / #M2MTD / #M2MER / SUBBR / SUBER /
// SUB16 / Справки / квитанции ЭДО).
//
// На этапе M1 — заглушка.
package main
import (
"fmt"
"os"
)
const serviceName = "nsd-adapter"
func main() {
fmt.Fprintf(os.Stdout, "%s: запуск (заглушка M1)\n", serviceName)
}
+37
View File
@@ -0,0 +1,37 @@
version: "3.9"
# Локальный стек разработки и тестирования.
# В прод-среде сервисы поднимаются через systemd-юниты или podman-compose
# с production-overlay. Здесь — dev-конфиг с минимальными настройками
# и без секретов.
services:
postgres:
image: postgres:16
# В проде заменить на postgrespro/std-16 или registry.postgrespro.ru/pgpro/...
container_name: bj-postgres
environment:
POSTGRES_USER: bj
POSTGRES_PASSWORD: bj_dev
POSTGRES_DB: bj
ports:
- "127.0.0.1:5432:5432"
volumes:
- bj-postgres-data:/var/lib/postgresql/data
minio:
image: minio/minio:latest
container_name: bj-minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: bj
MINIO_ROOT_PASSWORD: bj_dev_minio
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
volumes:
- bj-minio-data:/data
volumes:
bj-postgres-data:
bj-minio-data:
+52
View File
@@ -0,0 +1,52 @@
# Архитектура — обзор
Краткая выжимка из плана проекта. Полный план хранится у разработки в
`/.claude/plans/precious-percolating-axolotl.md` (внешний документ).
## Что строим
Внутренний модуль брокера для M2M-перевода ценных бумаг через НРД
(сервис MOEX MOST). Сейчас разрабатывается «для себя», но архитектура
изначально закладывается под тиражирование другим компаниям.
## Состав
- `lk-gateway` — интеграция с ЛК клиента (ESIA Finance API V1).
- `lk-emulator` — эмулятор ЛК на время, пока реальный ЛК не готов.
- `m2m-core` — бизнес-логика и FSM сделки.
- `nsd-adapter` — три транспорта к НРД (ИШ REST — основной, WS ONYX —
резерв, ФШ — fallback) и маршрутизация по типам пакетов
(`#M2MTR/#M2MTD/#M2MER/SUBBR/SUBER/SUB16/Справки/квитанции ЭДО`).
- `crypto-service` — Java + КриптоПро JCP, КС1.
- `notify` — уведомления (e-mail, Yandex Messenger 360, WS-push,
webhook), архитектура плагинов.
- `fansy-store` — принимающая БД для выгрузки от команды Fansy.
- `admin-ui` — веб-интерфейс администратора и сотрудника депозитария.
## Стек
- Go 1.23, Java 21 (Liberica JDK), React + Vite + TypeScript.
- PostgreSQL Pro Certified (на проде), MinIO.
- Развёртывание — одна ВМ, podman-compose.
- Класс СКЗИ — КС1 (КриптоПро CSP/JCP).
## Ключевые ограничения
- SLA: 5 мин/этап на старте, 2 мин/этап в проде. Регуляторный таймаут
отсутствия ответа от Брокера 2 — 10 → 5 мин.
- Дедлайн прод-релиза — **01.09.2026** (вступление 124-ФЗ + Указания ЦБ).
- Реестр российского ПО (ПП-1236) — заявка на M5; стек и архитектура
соответствуют.
- Кодировка XML — windows-1251; формат `NSDDateTime` — только МСК-зона.
## Документация интеграций
- `docs/fansy-contract/v1/` — DDL и контракт данных для команды Fansy
(мы передаём, ETL делают они).
- `docs/lk-contract/v1/` — OpenAPI контракта с ЛК клиента.
## Развёртывание
- `deploy/docker-compose/docker-compose.yml` — dev-стек (PostgreSQL +
MinIO).
- `scripts/setup-dev-vm.sh` — подготовка dev-ВМ под РЕД ОС или Ubuntu.
+33
View File
@@ -0,0 +1,33 @@
# docs/fansy-contract/v1 — контракт данных с командой Fansy
ETL Fansy → принимающая БД (`fansy-store`) реализует **другая команда
разработки**. С нашей стороны:
1. Спроектировать таблицы по требованиям документации НРД к данным M2M.
2. Передать команде Fansy DDL и контракт данных.
3. Согласовать тип load (UPSERT в staging), окна обновления, SLA на
свежесть данных.
4. Не давать ETL-роли DDL-прав в принимающей схеме.
Состав каталога (создаём в M1, отправляем в начале M2):
- `ddl/``*.sql` миграции PostgreSQL для всех таблиц.
- `data-dictionary.md` — семантика каждого поля (источник в Fansy,
nullable, единицы, примеры).
- `etl-requirements.md` — требования к процессу выгрузки: тип load,
расписание, способ записи, окна простоя, обработка ошибок,
конфиденциальность.
- `examples/` — пример заявки M2M «end-to-end», 5–10 тестовых клиентов
и заявок для совместного приёмочного теста.
Минимальный набор таблиц (см. план):
- Депоненты / клиенты.
- Документы инвестора (`IdentityDocumentCodeEnum`).
- ИИС-договоры (`IIAContractTypeEnum ∈ {T12, T03}`).
- Депо-счета и разделы (`AccountId`, `SectionId`, `DeponentCode`).
- Реквизиты расчётов (ИНН депозитария).
- Портфели и остатки (Whole / Fractional, `IsolationStatus = SGDN`).
- Справочник ЦБ (`SecurityCode`, `ISIN`, `Classification`, `Category`).
- Контрагенты-участники сервиса MOST (Справочник пользователей).
- Audit / staging-таблицы для каждой основной.
+18
View File
@@ -0,0 +1,18 @@
# docs/lk-contract/v1 — контракт с ЛК клиента (ESIA Finance)
ЛК клиента работает на платформе **ESIA Finance**, контракт описан
в `DOC/API ЛК ЕСИА.pdf` (`/api/v1/back_office/...`, Basic HTTP, JSON,
UTF-8).
На этапе M1 в `lk-emulator` мы воспроизводим этот контракт для запуска
сквозного потока. Реальный ЛК подключится по тому же контракту, без
правок на нашей стороне.
В этом каталоге будут:
- `openapi.yaml` — наш OpenAPI-контракт `lk-gateway`, согласованный
с командой ЛК.
- `examples/` — примеры заявлений и ответов.
- `changelog.md` — версионирование контракта.
Реализация — задача M1.
+3
View File
@@ -0,0 +1,3 @@
module git.zetit.ru/zuevav/Bridge-and-Join-s
go 1.23
+8
View File
@@ -0,0 +1,8 @@
# internal/fansystore — Go-репозиторий чтения нашей принимающей БД
ETL Fansy → принимающая БД делает **отдельная команда**. Здесь только
типизированный Go-доступ на чтение/обогащение из `m2m-core`.
DDL и контракт данных — `docs/fansy-contract/v1/`.
Реализация — задача M2 (см. план).
View File
+24
View File
@@ -0,0 +1,24 @@
# internal/m2m — доменные модели сообщений M2M
Go-модели, генерируемые/выровненные по XSD из `DOC/M2MSchemas_260408/`
(namespace `http://nsd.ru/schemas/m2m/...`, version `2026-04-08`).
Состав:
- `M2MTransferRequest` — запрос на перевод.
- `M2MTransferDecision` — решение принимающей стороны.
- `M2MTransferResponse` — тех. ответ НРД (`StatusCode ∈ {INFO, ERROR}`).
- `M2MTransferHandbook(+Request)` — справочник участников.
- `M2MTransferParticipantForm` — карточка участника.
Точные ограничения (валидаторы):
- `ReferenceId` — длина 16, pattern `M2M[A-Z0-9]{13}`.
- `DeponentCode` — до 12 символов, `[A-Z0-9]*`.
- `ISIN` — длина 12, `[A-Z]{2}[A-Z0-9]{9}[0-9]`.
- `OrganizationINN` — ровно 10 цифр.
- `IIAContractType``T12 | T03`.
- `SecurityClassification``BOND | SHAR | MFUN`.
- `IsolationStatus` — единственное значение `SGDN`.
Реализация — задача M1 (см. план).
+15
View File
@@ -0,0 +1,15 @@
# internal/notify — провайдеры уведомлений
Архитектура — плагины под единый интерфейс `Notifier`. Реализации в
конфиге включаются/выключаются без изменений в коде:
- `smtp` — внутренняя корпоративная почта.
- `yandex360` — Yandex Messenger через Yandex 360 для бизнеса
(используем готового бота заказчика).
- `webhook` — универсальный POST по URL, для лёгкого подключения
других систем при тиражировании.
- `wspush` — push-уведомления в `admin-ui` через WebSocket.
- (опционально) `telegram`, `mattermost`, `rocketchat` — заглушки с
примером расширения.
Реализация — задача M4 (см. план).
+14
View File
@@ -0,0 +1,14 @@
# internal/nsdxml — сериализация и парсинг XML по правилам НРД
Особенности, которые этот пакет обязан учитывать:
- **Кодировка `windows-1251`** на чтение и запись (XML-объявление и тело).
Для конвертации — `golang.org/x/text/encoding/charmap.Windows1251`.
- **Тип `NSDDateTime`** — формат `YYYY-MM-DDThh:mm:ss(МСК[+/-N])`,
pattern из XSD:
`[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\(МСК([+-][0-9]{1,2})?\)`.
Только зона МСК, опциональный сдвиг.
- **Каноникализация `xml-exc-c14n`** для проверки совпадения с эталоном.
- **Round-trip**: `Marshal(Unmarshal(x)) == x` после канонизации.
Реализация — задача M1 (см. план).
View File
+56
View File
@@ -0,0 +1,56 @@
# scripts — служебные скрипты
## setup-dev-vm.sh
Первичная подготовка dev-ВМ.
Поддерживаемые ОС:
- **РЕД ОС 7.x** — целевая прод-ОС.
- **Ubuntu 22.04+** / **Debian 12+** — бюджетная альтернатива для
разработки и для компаний без лицензий на сертифицированные ОС.
- **Astra Linux** (deb-семейство) — поддерживается базовый сценарий.
- RHEL-совместимые (Rocky, Alma) — для разработки.
Что ставит:
- базовый dev-стек (git, make, jq, xmlstarlet и т. п.),
- Podman + podman-compose,
- Go 1.23,
- Liberica JDK 21 (BellSoft),
- Node.js 20 LTS,
- Claude Code CLI (`@anthropic-ai/claude-code`).
Что готовит:
- пользователя `dev` с домашней директорией, без sudo по умолчанию,
- рабочий каталог `/srv/dev`,
- bash-историю с timestamp для аудита,
- `/etc/profile.d/go.sh` для PATH к Go.
Запуск:
```bash
sudo bash scripts/setup-dev-vm.sh
```
Тонкая настройка через переменные окружения — см. шапку скрипта.
Идемпотентен — повторный запуск ничего не ломает.
### Что НЕ делает скрипт
Эти шаги — вручную, по мере получения артефактов:
1. Установка КриптоПро CSP и КриптоПро JCP под целевую ОС.
2. Установка дистрибутива Интеграционного шлюза НРД.
3. Импорт тестовых сертификатов GUEST/TEST3 (ГОСТ + RSA).
4. Настройка исходящего файрвола / прокси (whitelist для НРД,
git.zetit.ru, npmjs, golang, anthropic).
### Прод-ограничения
Скрипт удобен для разработки на любой из перечисленных ОС, но для
прод-стенда в финансовом секторе требуется **сертифицированная РЕД ОС
или Astra SE** + лицензированный **КриптоПро КС1**. Ubuntu/Debian/Rocky
для прода финсектора не подходят.
+373
View File
@@ -0,0 +1,373 @@
#!/usr/bin/env bash
# Скрипт первичной подготовки dev-ВМ для проекта Bridge and Join's
# (сервис M2M-перевода ЦБ через НРД).
#
# Поддерживаемые ОС:
# - РЕД ОС 7.x — целевая прод-ОС (вариант с бюджетом на лицензии)
# - Ubuntu 22.04+ — бюджетная альтернатива для разработки и для
# компаний, у которых ещё нет лицензий на РЕД ОС
# (для прод-сертификации в финсекторе всё равно
# потребуется РЕД ОС / Astra SE с сертификатом ФСТЭК)
#
# Что делает:
# - определяет ОС и пакетный менеджер;
# - устанавливает базовый dev-стек (Git, Make, jq, xmlstarlet, ...);
# - устанавливает Go 1.23, Liberica JDK 21, Node.js 20 LTS, Podman;
# - устанавливает Claude Code CLI;
# - создаёт пользователя dev и каталог /srv/dev;
# - готовит безопасный shell-history с timestamp.
#
# Запуск (под root или через sudo):
# sudo bash scripts/setup-dev-vm.sh
#
# Переменные окружения для тонкой настройки:
# GO_VERSION — версия Go (по умолчанию 1.23.4)
# JDK_VERSION — версия Liberica JDK (по умолчанию 21)
# NODE_VERSION — версия Node (по умолчанию 20)
# DEV_USER — имя пользователя для разработки (по умолчанию dev)
# DEV_HOME — домашняя директория dev (по умолчанию /home/dev)
# WORKSPACE_ROOT — корень рабочих репо (по умолчанию /srv/dev)
# SKIP_USER — 1, чтобы не создавать пользователя
# SKIP_CLAUDE — 1, чтобы не ставить Claude Code CLI
#
# Скрипт идемпотентен — повторный запуск не ломает то, что уже сделано.
set -euo pipefail
# --------------------------------------------------------------------- #
# Параметры
# --------------------------------------------------------------------- #
GO_VERSION="${GO_VERSION:-1.23.4}"
JDK_VERSION="${JDK_VERSION:-21}"
NODE_VERSION="${NODE_VERSION:-20}"
DEV_USER="${DEV_USER:-dev}"
DEV_HOME="${DEV_HOME:-/home/${DEV_USER}}"
WORKSPACE_ROOT="${WORKSPACE_ROOT:-/srv/dev}"
SKIP_USER="${SKIP_USER:-0}"
SKIP_CLAUDE="${SKIP_CLAUDE:-0}"
REPO_URL="https://git.zetit.ru/zuevav/Bridge-and-Join-s.git"
# --------------------------------------------------------------------- #
# Хелперы
# --------------------------------------------------------------------- #
log() { printf '[%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$*"; }
die() { printf '[ОШИБКА] %s\n' "$*" >&2; exit 1; }
need_root() { [[ "$EUID" -eq 0 ]] || die "Нужны права root. Запусти через sudo."; }
# --------------------------------------------------------------------- #
# 0. Предусловия — определение ОС и менеджера пакетов
# --------------------------------------------------------------------- #
need_root
if [[ ! -f /etc/os-release ]]; then
die "Файл /etc/os-release не найден. Проверь, что ОС поддерживает FHS."
fi
# shellcheck disable=SC1091
. /etc/os-release
OS_FAMILY="" # rpm | deb
OS_HUMAN=""
case "${ID:-}" in
redos)
OS_FAMILY="rpm"
OS_HUMAN="РЕД ОС ${VERSION_ID:-?}"
;;
rhel|centos|rocky|almalinux|fedora)
OS_FAMILY="rpm"
OS_HUMAN="${PRETTY_NAME:-RHEL-совместимая}"
log "ВНИМАНИЕ: ОС ${OS_HUMAN} — для разработки подходит, для прод-сертификации в финсекторе нужна РЕД ОС / Astra SE."
;;
ubuntu|debian)
OS_FAMILY="deb"
OS_HUMAN="${PRETTY_NAME:-${ID} ${VERSION_ID:-?}}"
log "ОС ${OS_HUMAN} — бюджетный вариант для разработки. Для прода в финсекторе нужна РЕД ОС / Astra SE с сертификатом ФСТЭК."
;;
astra)
OS_FAMILY="deb"
OS_HUMAN="Astra Linux ${VERSION_ID:-?}"
;;
*)
log "ВНИМАНИЕ: ОС определена как ${ID:-неизвестно} ${VERSION_ID:-?}."
if command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
OS_FAMILY="rpm"
elif command -v apt-get >/dev/null 2>&1; then
OS_FAMILY="deb"
else
die "Не удалось определить пакетный менеджер. Поддерживаются dnf/yum (РЕД ОС, RHEL) и apt (Ubuntu, Debian, Astra)."
fi
log "Пробуем как ${OS_FAMILY}-совместимую."
;;
esac
log "ОС: ${OS_HUMAN}, семейство: ${OS_FAMILY}"
PKG_INSTALL=""
PKG_UPDATE=""
case "${OS_FAMILY}" in
rpm)
if command -v dnf >/dev/null 2>&1; then PKG_INSTALL="dnf install -y"; PKG_UPDATE="dnf -y makecache"
elif command -v yum >/dev/null 2>&1; then PKG_INSTALL="yum install -y"; PKG_UPDATE="yum -y makecache"
else die "Не найден dnf/yum"
fi
;;
deb)
export DEBIAN_FRONTEND=noninteractive
PKG_INSTALL="apt-get install -y --no-install-recommends"
PKG_UPDATE="apt-get update -y"
;;
esac
log "Обновление кэша пакетов"
${PKG_UPDATE}
# --------------------------------------------------------------------- #
# 1. Базовый системный стек
# --------------------------------------------------------------------- #
log "Шаг 1/8: установка базовых пакетов"
case "${OS_FAMILY}" in
rpm)
${PKG_INSTALL} \
git make curl wget tar gzip unzip ca-certificates \
jq xmlstarlet libxml2 \
gcc gcc-c++ binutils \
python3 python3-pip \
postgresql-libs \
openssl-libs
;;
deb)
${PKG_INSTALL} \
git make curl wget tar gzip unzip ca-certificates \
jq xmlstarlet libxml2-utils \
build-essential \
python3 python3-pip python3-venv \
libpq5 \
libssl3 \
gnupg lsb-release software-properties-common
;;
esac
# --------------------------------------------------------------------- #
# 2. Podman + podman-compose
# --------------------------------------------------------------------- #
log "Шаг 2/8: установка Podman и podman-compose"
case "${OS_FAMILY}" in
rpm)
${PKG_INSTALL} podman podman-docker || true
;;
deb)
${PKG_INSTALL} podman || true
# podman-docker может отсутствовать — не критично
apt-get install -y --no-install-recommends podman-docker 2>/dev/null || true
;;
esac
if ! command -v podman-compose >/dev/null 2>&1; then
pip3 install --quiet podman-compose || \
log "ВНИМАНИЕ: не удалось установить podman-compose через pip3. Поставь вручную."
fi
# Включаем сокет podman, чтобы клиенты, ожидающие docker.sock, работали без правок
systemctl enable --now podman.socket 2>/dev/null || true
# --------------------------------------------------------------------- #
# 3. Go
# --------------------------------------------------------------------- #
log "Шаг 3/8: установка Go ${GO_VERSION}"
GO_TARBALL="go${GO_VERSION}.linux-amd64.tar.gz"
GO_URL="https://go.dev/dl/${GO_TARBALL}"
if [[ -d /usr/local/go && -x /usr/local/go/bin/go ]]; then
CURRENT_GO_VER="$(/usr/local/go/bin/go version | awk '{print $3}' | sed 's/go//')"
if [[ "${CURRENT_GO_VER}" == "${GO_VERSION}" ]]; then
log "Go ${GO_VERSION} уже установлен"
else
log "Найден Go ${CURRENT_GO_VER}, переустанавливаем на ${GO_VERSION}"
rm -rf /usr/local/go
fi
fi
if [[ ! -d /usr/local/go ]]; then
TMP_TAR="/tmp/${GO_TARBALL}"
wget -q --show-progress -O "${TMP_TAR}" "${GO_URL}"
tar -C /usr/local -xzf "${TMP_TAR}"
rm -f "${TMP_TAR}"
fi
cat > /etc/profile.d/go.sh <<'EOF'
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
EOF
chmod 0644 /etc/profile.d/go.sh
# --------------------------------------------------------------------- #
# 4. Liberica JDK
# --------------------------------------------------------------------- #
log "Шаг 4/8: установка Liberica JDK ${JDK_VERSION}"
case "${OS_FAMILY}" in
rpm)
if [[ ! -f /etc/yum.repos.d/bellsoft.repo ]]; then
cat > /etc/yum.repos.d/bellsoft.repo <<'EOF'
[bellsoft]
name=BellSoft Repository
baseurl=https://yum.bell-sw.com/
enabled=1
gpgcheck=1
gpgkey=https://download.bell-sw.com/pki/GPG-KEY-bellsoft
EOF
fi
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}" || \
log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
;;
deb)
if [[ ! -f /etc/apt/sources.list.d/bellsoft.list ]]; then
curl -fsSL https://download.bell-sw.com/pki/GPG-KEY-bellsoft \
| gpg --dearmor -o /usr/share/keyrings/bellsoft.gpg
echo "deb [signed-by=/usr/share/keyrings/bellsoft.gpg] https://apt.bell-sw.com/ stable main" \
> /etc/apt/sources.list.d/bellsoft.list
apt-get update -y
fi
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}-full" || \
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}" || \
log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
;;
esac
# --------------------------------------------------------------------- #
# 5. Node.js LTS (для admin-ui и Claude Code CLI)
# --------------------------------------------------------------------- #
log "Шаг 5/8: установка Node.js ${NODE_VERSION} LTS"
need_install_node=1
if command -v node >/dev/null 2>&1; then
CUR_NODE_MAJOR="$(node --version | sed 's/v//;s/\..*//')"
if [[ "${CUR_NODE_MAJOR}" == "${NODE_VERSION}" ]]; then
log "Node.js ${NODE_VERSION} уже установлен"
need_install_node=0
fi
fi
if [[ "${need_install_node}" == "1" ]]; then
case "${OS_FAMILY}" in
rpm)
curl -fsSL "https://rpm.nodesource.com/setup_${NODE_VERSION}.x" | bash -
${PKG_INSTALL} nodejs
;;
deb)
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
${PKG_INSTALL} nodejs
;;
esac
fi
# --------------------------------------------------------------------- #
# 6. Claude Code CLI
# --------------------------------------------------------------------- #
if [[ "${SKIP_CLAUDE}" != "1" ]]; then
log "Шаг 6/8: установка Claude Code CLI"
npm install -g @anthropic-ai/claude-code
else
log "Шаг 6/8: пропуск установки Claude Code CLI (SKIP_CLAUDE=1)"
fi
# --------------------------------------------------------------------- #
# 7. Пользователь dev и рабочая директория
# --------------------------------------------------------------------- #
if [[ "${SKIP_USER}" != "1" ]]; then
log "Шаг 7/8: подготовка пользователя ${DEV_USER}"
if ! id -u "${DEV_USER}" >/dev/null 2>&1; then
useradd -m -s /bin/bash -d "${DEV_HOME}" "${DEV_USER}"
log "Создан пользователь ${DEV_USER}"
else
log "Пользователь ${DEV_USER} уже существует"
fi
mkdir -p "${WORKSPACE_ROOT}"
chown "${DEV_USER}:${DEV_USER}" "${WORKSPACE_ROOT}"
HIST_SNIPPET="${DEV_HOME}/.bashrc.d-history"
cat > "${HIST_SNIPPET}" <<'EOF'
# История с timestamp и общая для всех сессий — для аудита.
export HISTTIMEFORMAT="%F %T "
export HISTSIZE=10000
export HISTFILESIZE=20000
shopt -s histappend
PROMPT_COMMAND="history -a; ${PROMPT_COMMAND:-}"
EOF
chown "${DEV_USER}:${DEV_USER}" "${HIST_SNIPPET}"
if ! grep -q "bashrc.d-history" "${DEV_HOME}/.bashrc" 2>/dev/null; then
echo "[ -f ${HIST_SNIPPET} ] && . ${HIST_SNIPPET}" >> "${DEV_HOME}/.bashrc"
fi
if ! grep -q "/usr/local/go/bin" "${DEV_HOME}/.bashrc" 2>/dev/null; then
cat >> "${DEV_HOME}/.bashrc" <<'EOF'
[ -f /etc/profile.d/go.sh ] && . /etc/profile.d/go.sh
EOF
fi
else
log "Шаг 7/8: пропуск подготовки пользователя (SKIP_USER=1)"
fi
# --------------------------------------------------------------------- #
# 8. Финальные проверки
# --------------------------------------------------------------------- #
log "Шаг 8/8: проверка установленных версий"
{
printf 'git: '; git --version
printf 'make: '; make --version | head -n1
printf 'jq: '; jq --version
printf 'xmlstarlet: '; xmlstarlet --version | head -n1
printf 'podman: '; podman --version 2>/dev/null || echo "(не установлен)"
printf 'podman-compose:'; podman-compose --version 2>/dev/null || echo "(не установлен)"
printf 'go: '; /usr/local/go/bin/go version
printf 'java: '; java -version 2>&1 | head -n1
printf 'node: '; node --version 2>/dev/null || echo "(не установлен)"
printf 'npm: '; npm --version 2>/dev/null || echo "(не установлен)"
if [[ "${SKIP_CLAUDE}" != "1" ]]; then
printf 'claude: '; claude --version 2>/dev/null || echo "(не установлен или требует первого запуска)"
fi
} || true
cat <<EOF
──────────────────────────────────────────────────────────────────────
Подготовка dev-ВМ завершена.
ОС: ${OS_HUMAN}
Дальше — под пользователем ${DEV_USER}:
su - ${DEV_USER}
cd ${WORKSPACE_ROOT}
# Положи свой публичный SSH-ключ в ~/.ssh/id_ed25519.pub
# и добавь его в git.zetit.ru → Settings → SSH Keys
git clone ${REPO_URL}
cd Bridge-and-Join-s
make ci # локальный прогон линтера, тестов и сборки
make compose-up # PostgreSQL + MinIO для разработки
# Запуск Claude Code из корня репо:
claude
Дополнительные шаги, которые НЕ автоматизированы (делаются вручную при
получении соответствующих артефактов от заказчика):
1. Установка КриптоПро CSP и КриптоПро JCP под целевую ОС.
2. Установка дистрибутива Интеграционного шлюза НРД.
3. Импорт тестовых сертификатов GUEST/TEST3 (ГОСТ + RSA).
4. Конфигурирование исходящего прокси/whitelist для git.zetit.ru,
gost-*.nsd.ru, rsa-*.nsd.ru, registry.npmjs.org, proxy.golang.org,
api.anthropic.com.
ВАЖНО про прод:
Эта ВМ годится для разработки и тестирования. Для прод-стенда в
финсекторе обязательны РЕД ОС / Astra SE с сертификатом ФСТЭК и
лицензированный КриптоПро класса КС1 — Ubuntu/Debian для прода
финсектора не подходят.
──────────────────────────────────────────────────────────────────────
EOF
+24
View File
@@ -0,0 +1,24 @@
# services/crypto-service — Java + КриптоПро JCP
Изолированный сервис для криптографических операций по ГОСТ:
- проверка подписи входящих квитанций ЭДО, ответов НРД и сообщений
от брокеров;
- серверная подпись действий оператора в `admin-ui` (ради соблюдения
SLA 2 мин — клиентский КриптоПро на АРМ исключён);
- резервный канал отправки в НРД через WS ONYX напрямую (когда ИШ не
задействован — упаковка и подпись на нашей стороне);
- криптографические проверки целостности эталонов в MinIO.
Стек: Java 21 (Liberica JDK), Apache Santuario с ГОСТ-патчем, КриптоПро
JCP. КриптоПро CSP ставится на ВМ. Класс СКЗИ — **КС1** (программная
защита, без HSM).
Канал общения с Go-ядром — gRPC по Unix Domain Socket (без сетевого хопа,
минимальная задержка).
Архитектурно сохранена `Provider`-абстракция (Валидата JCP / VipNet и
прочее) — для тиражирования другим компаниям. На M1 реализован только
КриптоПро.
Реализация — задача M1-M2 (заглушка gRPC) и M3-M4 (полный функционал).
+18
View File
@@ -0,0 +1,18 @@
# web/admin-ui — веб-интерфейс администратора и сотрудника депозитария
Стек: React + Vite + TypeScript. Реализация — M3.
Разделы:
- Журнал входящих и исходящих сообщений (фильтры по GUID / инвестору /
статусу / периоду / типу пакета).
- Ручное согласование (manual-approval) сделок с подписью оператора
серверной ЭП через `crypto-service`.
- Раздел «Сертификаты» — обновление сертификатов для подписи (закрытые
ключи через UI не выгружаются), сроки, алерты, аудит.
- Раздел «Уведомления» — включение и настройка провайдеров плагинов.
- SLA-дашборд по этапам.
- Переключатель канала отправки (ИШ / ONYX / ФШ) для тестов.
Роли: Администратор, Администратор СКЗИ, Сотрудник депозитария,
Оператор, Аудитор.