diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 18c9ab6..4ab5e52 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -56,7 +56,7 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` @@ -66,7 +66,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -95,6 +95,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0 with: category: "/language:${{matrix.language}}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33d3b45..5c75629 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,26 +1,33 @@ # Contributing +Language: **EN** | [RU](CONTRIBUTING.ru.md) + ----
Environment Setup -> The application requires `Node.js v24.18.0` and the pnpm version pinned in `package.json`; -> other versions are not supported. +> The application requires the Node.js and pnpm versions specified in `package.json`. +> Other versions are not supported. -> The git hooks additionally require [`jq`](https://jqlang.github.io/jq/download/) -> and [`gitleaks`](https://github.com/gitleaks/gitleaks#installing) to be available -> in `PATH`; a commit is rejected when either of them is missing. +> Git hooks require [`jq`](https://jqlang.github.io/jq/download/) +> and [`gitleaks`](https://github.com/gitleaks/gitleaks#installing). +> A commit is rejected if either tool is missing. > Pre-commit also runs the complete unit and integration suites. Docker must be > running and accessible to the current user. Testcontainers starts isolated > PostgreSQL, Redis, Kafka and Schema Registry containers; the application stack > and a local `.env` are not required. The first run downloads container images. +Project commands: [README](README.md). + [VSCode](https://code.visualstudio.com/) is the recommended editor for development. #### VSCode Setup -Install the following extensions for proper [ESLint](https://eslint.org/docs/latest/), [Prettier](https://prettier.io/docs/en/), and [EditorConfig](https://editorconfig.org/) integration: + +Install the following extensions for proper [ESLint](https://eslint.org/docs/latest/), +[Prettier](https://prettier.io/docs/en/), and [EditorConfig](https://editorconfig.org/) integration: + 1. `dbaeumer.vscode-eslint` 2. `rvest.vs-code-prettier-eslint` 3. `esbenp.prettier-vscode` @@ -33,68 +40,147 @@ Install the following extensions for proper [ESLint](https://eslint.org/docs/lat
Git Hooks -*Enable git hooks:* +Enable Git hooks: ```sh chmod +x .githooks/* git config core.hooksPath .githooks ``` -#### Checks executed on commit: +#### Pre-commit Checks + `pre-commit` runs the steps below in order and stops at the first failure: - 1. `Secret scan` — Gitleaks on staged changes. - 2. `Environment` — environment configuration consistency. - 3. `Translations` — dictionary consistency, see `scripts/intl.check.sh`. - 4. `Type check` — TypeScript check of the whole project. - 5. `Unused code` — unused exports, files, and dependencies. - 6. `Lint` — ESLint on the whole project, using the same script as CI. - 7. `Unit tests` — the complete unit suite in Jest CI mode. - 8. `Integration tests` — the complete integration suite in Jest CI mode. + +1. `Secret scan` — Gitleaks on staged changes. +2. `Environment` — environment configuration consistency. +3. `Translations` — dictionary consistency, see `scripts/intl.check.sh`. +4. `Type check` — TypeScript check of the whole project. +5. `Unused code` — unused exports, files, and dependencies. +6. `Lint` — ESLint on the whole project, using the same script as CI. +7. `Unit tests` — the complete unit suite in Jest CI mode. +8. `Integration tests` — the complete integration suite in Jest CI mode. The hook rejects the commit if Docker is unavailable or a test fails. Checks run against the working tree, including unstaged changes. CI checks PR to `dev`. -#### Allowed prefixes for commits and branches: +#### Allowed Commit and Branch Prefixes + `feat`, `fix`, `docs`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. -#### Commit message format: +#### Commit Message Format + ```text ()?[!]: ``` -*Where:* - - `` — one of the allowed prefixes above. - - `` — optional scope in parentheses, alphanumeric/`-`. - - `!` — optional breaking change marker. - - `: ` — colon + space are required. - - `` — short description. -> Total commit message length is limited to 70 characters. - -*Valid commit examples:* - - `feat: add filters to user search` - - `fix(auth): handle invalid jwt token` - - `refactor(user-service)!: drop legacy ws implementation` - - `docs: update api usage section` - - `chore(ci): tweak lint step` - -#### Branch naming format: +- `` — one of the allowed prefixes above. +- `` — optional scope in parentheses, alphanumeric/`-`. +- `!` — optional breaking change marker. +- `: ` — colon + space are required. +- `` — short description. + +> The commit subject line is limited to 70 characters. + +Examples: + +- `feat: add filters to user search` +- `fix(auth): handle invalid jwt token` +- `refactor(user-service)!: drop legacy ws implementation` +- `docs: update api usage section` +- `chore(ci): tweak lint step` + +#### Branch Naming Format + ```text / // ``` -*Where:* - - `` — one of the prefixes listed above. - - `/` — optional part, alphanumeric/`-`. - - `/` — short description, alphanumeric/`_`/`-`. +- `` — one of the prefixes listed above. +- `/` — optional part, alphanumeric/`-`. +- `/` — short description, alphanumeric/`_`/`-`. + +Examples: -*Valid branch examples:* - - `feat/add-user-search-filters` - - `fix/schedule-update-ws-event` - - `refactor/core-user-cleanup` - - `feat/GOTEC-404/add-notifications-for-users` +- `feat/add-user-search-filters` +- `fix/schedule-update-ws-event` +- `refactor/core-user-cleanup` +- `feat/GOTEC-404/add-notifications-for-users` > Exceptions: `dev`, `main`, `master`.
---- + +
+Releasing a new version + +1. Create a branch and update `version` in `package.json`. The example below uses `1.0.0`: +```bash +git switch dev +git pull --ff-only +git switch -c chore/release-v1.0.0 +# Update the version in package.json before committing. +git add package.json +git commit -m "chore: release v1.0.0" +git push -u origin chore/release-v1.0.0 +``` + +2. Open a PR into `dev`, pass CI, merge and wait for **Publish service image** to succeed. + +3. Tag the published commit; the tag must match `package.json`: +```bash +git switch dev +git pull --ff-only +git tag v1.0.0 +git push origin v1.0.0 +``` + +
+ +---- + +> The following tools are experimental and optional. They help LLM agents +> navigate code and component relationships in a local checkout. +> They are not required to develop or run the service. + +---- + +
+Local Code Graph (Graphify) + +Install once with [uv](https://docs.astral.sh/uv/), then open a new terminal: + +```sh +uv tool install graphifyy==0.9.65 +uv tool update-shell +``` + +Run `make graphify` from the repository root to create or update the local code +graph and report. No API key is required. Outputs in `graphify-out/` are ignored +by Git. Agent guidance: [AGENTS.md](AGENTS.md). + +
+ +---- + +
+Local TypeScript Navigation (LSP-MCP) + +[lsmcp](https://github.com/mizchi/lsmcp) and TypeScript Language Server are pinned +dev dependencies. Run `make lsp` from the repository root to start the MCP server +over stdio; an MCP client is required to send queries. + +For an MCP client, use command `node`, arguments +`["node_modules/@mizchi/lsmcp/dist/lsmcp.js", "--config", ".lsmcp/config.json"]`, +and set its working directory to this repository. Desktop clients may need an +absolute Node executable path. Configuration: [.lsmcp/config.json](.lsmcp/config.json). +Local indexes and caches are ignored by Git. Agent guidance: [AGENTS.md](AGENTS.md). + +Known limitation in lsmcp 0.10.0: definition previews can be empty for encoded +pnpm dependency paths. Hover still works; `resolve_symbol` locates the installed +package entry point. See [AGENTS.md](AGENTS.md) for the fallback. + +
+ +---- diff --git a/CONTRIBUTING.ru.md b/CONTRIBUTING.ru.md new file mode 100644 index 0000000..1e86323 --- /dev/null +++ b/CONTRIBUTING.ru.md @@ -0,0 +1,186 @@ +# Участие в разработке + +Language: [EN](CONTRIBUTING.md) | **RU** + +---- + +
+Подготовка окружения + +> Для приложения нужны Node.js и pnpm, версии указанной в `package.json`. +> Другие версии не поддерживаются. + +> Для Git-хуков установите [`jq`](https://jqlang.github.io/jq/download/) +> и [`gitleaks`](https://github.com/gitleaks/gitleaks#installing). +> Без них коммит не пройдёт проверку. + +> Pre-commit запускает все модульные и интеграционные тесты. Docker должен быть +> запущен и доступен текущему пользователю. Testcontainers создаёт отдельные +> контейнеры PostgreSQL, Redis, Kafka и Schema Registry. Запускать инфраструктуру +> приложения и создавать локальный `.env` не нужно. При первом запуске скачиваются образы. + +Команды проекта: [README](README.ru.md). + +Рекомендуемый редактор — [VSCode](https://code.visualstudio.com/). + +#### Настройка VSCode + +Для интеграции [ESLint](https://eslint.org/docs/latest/), [Prettier](https://prettier.io/docs/en/) +и [EditorConfig](https://editorconfig.org/) установите расширения: + +1. `dbaeumer.vscode-eslint` +2. `rvest.vs-code-prettier-eslint` +3. `esbenp.prettier-vscode` +4. `EditorConfig.EditorConfig` + +
+ +---- + +
+Git-хуки + +Подключите хуки: +```sh +chmod +x .githooks/* +git config core.hooksPath .githooks +``` + +#### Проверки перед коммитом + +`pre-commit` выполняет проверки по порядку и останавливается при первой ошибке: + +1. `Secret scan` — поиск секретов через Gitleaks в подготовленных к коммиту изменениях. +2. `Environment` — согласованность конфигурации окружения. +3. `Translations` — согласованность словарей; см. `scripts/intl.check.sh`. +4. `Type check` — проверка типов TypeScript во всём проекте. +5. `Unused code` — поиск неиспользуемых экспортов, файлов и зависимостей. +6. `Lint` — ESLint во всём проекте, тот же скрипт, что и в CI. +7. `Unit tests` — все модульные тесты в режиме CI Jest. +8. `Integration tests` — все интеграционные тесты в режиме CI Jest. + +Если Docker недоступен или тесты падают, коммит отклоняется. Проверки выполняются +на рабочей копии, включая изменения, не добавленные в коммит. CI проверяет PR в `dev`. + +#### Допустимые префиксы коммитов и веток + +`feat`, `fix`, `docs`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. + +#### Формат сообщения коммита + +```text +()?[!]: +``` + +- `` — один из допустимых префиксов. +- `` — необязательная область изменений в скобках: латинские буквы, цифры и `-`. +- `!` — необязательная отметка несовместимого изменения. +- `: ` — обязательные двоеточие и пробел. +- `` — краткое описание изменения. + +> Первая строка сообщения коммита — не более 70 символов. + +Примеры: + +- `feat: add filters to user search` +- `fix(auth): handle invalid jwt token` +- `refactor(user-service)!: drop legacy ws implementation` +- `docs: update api usage section` +- `chore(ci): tweak lint step` + +#### Формат имени ветки + +```text +/ +// +``` + +- `` — один из допустимых префиксов. +- `/` — необязательная область или номер задачи: латинские буквы, цифры и `-`. +- `/` — краткое описание: латинские буквы, цифры, `_` и `-`. + +Примеры: + +- `feat/add-user-search-filters` +- `fix/schedule-update-ws-event` +- `refactor/core-user-cleanup` +- `feat/GOTEC-404/add-notifications-for-users` + +> Исключения: `dev`, `main`, `master`. + +
+ +---- + +
+Выпуск новой версии + +1. Создайте ветку и обновите `version` в `package.json`. Ниже пример для `1.0.0`: +```bash +git switch dev +git pull --ff-only +git switch -c chore/release-v1.0.0 +# Обновите версию в package.json перед коммитом. +git add package.json +git commit -m "chore: release v1.0.0" +git push -u origin chore/release-v1.0.0 +``` + +2. Откройте PR в `dev`, дождитесь успешного CI, слейте PR и дождитесь завершения **Publish service image**. + +3. Создайте тег на коммите с опубликованным образом; тег должен соответствовать `package.json`: +```bash +git switch dev +git pull --ff-only +git tag v1.0.0 +git push origin v1.0.0 +``` + +
+ +---- + +> Далее экспериментальные и необязательные инструменты для работы с LLM-агентами +> в локальном проекте: помогают им находить код и связи между компонентами. +> Для разработки и запуска сервиса не требуются. + +---- + +
+Локальный граф кода (Graphify) + +Установите один раз через [uv](https://docs.astral.sh/uv/), затем откройте новый терминал: + +```sh +uv tool install graphifyy==0.9.65 +uv tool update-shell +``` + +Команда `make graphify` из корня репозитория создаёт или обновляет локальный граф +кода и отчёт. API-ключ не требуется. Результаты в `graphify-out/` исключены из Git. +Инструкции для агентов: [AGENTS.md](AGENTS.md). + +
+ +---- + +
+Локальная навигация по TypeScript (LSP-MCP) + +[lsmcp](https://github.com/mizchi/lsmcp) и TypeScript Language Server включены +в зависимости разработки с фиксированными версиями. Команда `make lsp` из корня +репозитория запускает MCP-сервер через stdio, для запросов к нему нужен MCP-клиент. + +В MCP-клиенте укажите команду `node`, аргументы +`["node_modules/@mizchi/lsmcp/dist/lsmcp.js", "--config", ".lsmcp/config.json"]` +и корень репозитория как рабочую директорию. Десктопным клиентам может потребоваться +абсолютный путь к Node. Конфигурация: [.lsmcp/config.json](.lsmcp/config.json). +Локальные индексы и кеши исключены из Git. Инструкции для агентов: [AGENTS.md](AGENTS.md). + +Ограничение lsmcp 0.10.0: предпросмотр определений иногда не отображается из-за кодирования +путей зависимостей pnpm. Подсказки типов (hover) работают; `resolve_symbol` находит точку входа +установленного пакета. Альтернативный способ поиска описан в [AGENTS.md](AGENTS.md). + +
+ +---- diff --git a/README.md b/README.md index 240e624..6503e89 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # @monadiam/template-service +Language: **EN** | [RU](README.ru.md) + Universal NestJS service boilerplate for MonadIAM — authn/permission guards wired against identity-service and access-control-service, audit/change-log capture with outbox-based ClickHouse archival, and BullMQ retention cleanup. ---- @@ -28,69 +30,27 @@ empty. ---- -
-Local Code Graph (Graphify) - -Install once with [uv](https://docs.astral.sh/uv/), then open a new terminal: - -```sh -uv tool install graphifyy==0.9.65 -uv tool update-shell -``` - -Run `make graphify` from the repository root to create or update the local code -graph and report. No API key is required. Outputs in `graphify-out/` are ignored -by Git. Agent guidance: [AGENTS.md](AGENTS.md). - -
- ----- - -
-Local TypeScript Navigation (LSP-MCP) - -[lsmcp](https://github.com/mizchi/lsmcp) and TypeScript Language Server are pinned -dev dependencies. Install with `pnpm install` using the Node version from -`package.json`. Run `make lsp` from the repository root to start the stdio MCP -server; an MCP client must connect to it to issue queries. - -For an MCP client, use command `node`, arguments -`["node_modules/@mizchi/lsmcp/dist/lsmcp.js", "--config", ".lsmcp/config.json"]`, -and set its working directory to this repository. Desktop clients may need an -absolute Node executable path. Configuration: [.lsmcp/config.json](.lsmcp/config.json). -Local indexes and caches are ignored by Git. Agent guidance: [AGENTS.md](AGENTS.md). - -Known limitation in lsmcp 0.10.0: definition previews can be empty for encoded -pnpm dependency paths. Hover still works; `resolve_symbol` locates the installed -package entry point. See [AGENTS.md](AGENTS.md) for the fallback. - -
- ----- -
Local Deployment -1. Start the [infra](https://github.com/MonadIAM/infra) repository first. +1. Start the [infrastructure](https://github.com/MonadIAM/infrastructure/tree/local) repository first, using its `local` branch. 2. Start [identity-service](https://github.com/MonadIAM/identity-service) and [access-control-service](https://github.com/MonadIAM/access-control-service) -when authenticated/authorized flows are required. - -3. Create the local env file: +when authenticated and authorized flows are required. +3. Create the local environment file: ```sh make env ``` 4. Build and start the service: - ```sh make build make up ``` -The local service container depends on: +Dependency addresses inside the Docker network: | Dependency | Address inside Docker network | | :------------------ | :--------------------------------- | @@ -101,34 +61,13 @@ The local service container depends on: | Identity JWKS | `identity-service-app:4002` | | Access Control gRPC | `access-control-service-app:50051` | -Shared Docker networks are created by the infra repository: +Shared Docker networks are created by the infrastructure repository: `postgres-net`, `redis-net`, `kafka-net`, and `vault-net`.
---- -
-Stand Configuration - -Runtime settings: [.env.stand.example](.env.stand.example). - -| Connection | Stand mode | Mounted files / credentials | -| :--------- | :------------------------------- | :------------------------------------------------------------- | -| PostgreSQL | CNPG rw / ro, TLS, password | /tls/postgresql/ca.crt; POSTGRES_USER, POSTGRES_PASSWORD | -| Redis | Sentinel, master discovery, mTLS | /tls/redis/{ca.crt,tls.crt,tls.key}; REDIS_PASSWORD | -| Kafka | TLS + SCRAM-SHA-512 | /tls/kafka/ca.crt; /secrets/kafka/password | -| Vault | Agent Unix socket | /vault/proxy/agent.sock | - -```sh -kubectl exec -n monadiam-apps -c service -- make migrate -kubectl exec -n monadiam-apps -c service -- make seed -``` - -
- ----- -
Local Ports @@ -145,47 +84,69 @@ kubectl exec -n monadiam-apps -c service -- make seed
Project Commands -| Makefile | Description | -| :-------------------------------- | :-------------------------------------------------------------------- | -| **Infrastructure** | | -| `make env` | Create `.env` from `.env.local.example`. | -| `make build` | Build the application image. | -| `make up` | Start service, Vault bootstrap, and Vault Agent. | -| `make down` | Stop and remove service containers. | -| `make restart` | Run `make down` and `make up`. | -| **Development** | | -| `make lint` | Run ESLint code checks. | -| `make lint-fix` | Fix only Prettier violations and print changed file paths. | -| `make tsc` | Check TypeScript with tsc --noEmit. | -| `make knip` | Detect unused exports, files, and dependencies. | -| `make secrets-check` | Run the hook secret scan (staged changes). | -| `make env-check` | Validate environment DTO, templates and injected keys. | -| `make intl-types` | Generate TypeScript types from the translation dictionaries. | -| `make intl-check` | Run all six dictionary checks used by the Git hook. | -| `make lsp` | Start the local TypeScript MCP server over stdio. | -| `make graphify` | Create or update the local code graph and Markdown report. | -| `make graphify-rebuild` | Rescan all code files and regenerate the report. | -| `make graphify-html` | Update the graph and report, then export HTML visualization. | -| `make swagger` | Generate the OpenAPI Swagger JSON file. | -| `make postman` | Generate and patch the Postman collection JSON file. | -| `make docs` | Generate Swagger and Postman documentation artifacts. | -| **Database** | | -| `make migrate` | Apply pending MikroORM migrations in the running service container. | -| `make migration name="..."` | Generate a MikroORM migration in the running service container. | -| `make empty-migration name="..."` | Generate a blank MikroORM migration. | -| `make seed` | Run the PostgreSQL seeder in the running service container. | -| **Container / GHCR** | | -| `make image` | Build the production image; accepts `IMAGE`, `BASE_IMAGE`, `VCS_REF`. | -| `make image-publish` | Publish the commit SHA image to GHCR (CI). | -| `make image-release` | Tag the published SHA image with the package version (CI). | -| `make image-cleanup` | Retain 10 SHA-only image versions (CI). | -| **Test** | | -| `make test` | Run unit and integration tests (Docker required). | -| `make utest` | Run the complete unit suite. | -| `make itest` | Run integration tests (Docker required). | -| `make coverage-unit` | Run unit tests with coverage in `coverage/unit`. | -| `make coverage-integration` | Run integration tests with coverage in `coverage/integration`. | -| `make coverage` | Run both suites with combined coverage in `coverage/all`. | +| Makefile | Description | +| :-------------------------------- | :----------------------------------------------------------------------------------------- | +| **Infrastructure** | | +| `make env` | Create `.env` from `.env.local.example`. | +| `make build` | Build the local Compose image. | +| `make up` | Start service, Vault bootstrap, and Vault Agent. | +| `make down` | Stop and remove service containers. | +| `make restart` | Run `make down` and `make up`. | +| **Development** | | +| `make lint` | Run ESLint code checks. | +| `make lint-fix` | Fix only Prettier violations and print changed file paths. | +| `make tsc` | Check TypeScript types without building (`tsc --noEmit`). | +| `make knip` | Detect unused exports, files, and dependencies. | +| `make secrets-check` | Scan staged changes for secrets with Gitleaks. | +| `make env-check` | Check consistency between the environment DTO, env templates, and configuration variables. | +| `make intl-types` | Generate TypeScript types from the translation dictionaries. | +| `make intl-check` | Run all six dictionary checks used by the Git hook. | +| **Database** | | +| `make migrate` | Apply pending MikroORM migrations in the running service container. | +| `make migration name="..."` | Generate a MikroORM migration in the running service container. | +| `make empty-migration name="..."` | Generate a blank MikroORM migration. | +| `make seed` | Run the PostgreSQL seeder in the running service container. | +| **Test** | | +| `make test` | Run unit and integration tests (Docker required). | +| `make utest` | Run unit tests. | +| `make itest` | Run integration tests (Docker required). | +| `make coverage-unit` | Run unit tests with coverage in `coverage/unit`. | +| `make coverage-integration` | Run integration tests with coverage in `coverage/integration`. | +| `make coverage` | Run both suites with combined coverage in `coverage/all`. | +| **Container / GHCR (internal)** | | +| `make image` | Build the production image; also available locally with `IMAGE`, `BASE_IMAGE`, `VCS_REF`. | +| `make image-publish` | Publish the commit SHA image (GitHub Actions). | +| `make image-release` | Tag the published SHA image with the package version (GitHub Actions). | +| `make image-cleanup` | Keep the last 10 intermediate SHA images; preserve releases (GitHub Actions). | +| **LLM Tools (experimental)** | | +| `make lsp` | Start the local TypeScript MCP server over stdio. | +| `make graphify` | Create or update the local code graph and Markdown report. | +| `make graphify-rebuild` | Rebuild the code graph and report with a full project scan. | +| `make graphify-html` | Update the graph and report, then export HTML visualization. | + +
+ +---- + +
+Stand Configuration + +Runtime settings: [.env.stand.example](.env.stand.example). Kubernetes supplies environment +variables and mounted Secrets; Compose settings are not used on the stand. + +| Connection | Stand mode | Mounted files / credentials | +| :--------- | :------------------------------------------ | :--------------------------------------------------------------- | +| PostgreSQL | CNPG `rw` / `ro`, TLS, password | `/tls/postgresql/ca.crt`; `POSTGRES_USER`, `POSTGRES_PASSWORD` | +| Redis | Sentinel, master discovery, mTLS | `/tls/redis/{ca.crt,tls.crt,tls.key}`; `REDIS_PASSWORD` | +| Kafka | TLS + SCRAM-SHA-512 | `/tls/kafka/ca.crt`; `/secrets/kafka/password` | +| Vault | Transit through the Vault Agent Unix socket | `/vault/proxy/agent.sock`, shared with the Vault Agent container | + +**Migrations and seeding** + +```sh +kubectl exec -n monadiam-apps -c service -- make migrate +kubectl exec -n monadiam-apps -c service -- make seed +```
@@ -198,7 +159,7 @@ kubectl exec -n monadiam-apps -c service -- make seed | Event | Action | | :------------ | :------------------------------------------------------------------------------------------------------------- | -| PR to `dev` | Existing checks and production image build; no publication | +| PR to `dev` | Checks and service image build; no publication | | Push to `dev` | Build merged commit and publish `sha-` | | Tag `vX.Y.Z` | Require matching `package.json` version and a commit in `dev`; tag its SHA image as `X.Y.Z` without rebuilding | | Cleanup | Retain 10 SHA-only versions; preserve release tags | @@ -207,35 +168,16 @@ kubectl exec -n monadiam-apps -c service -- make seed [CI](.github/workflows/ci.yml), [publication](.github/workflows/publish.yml), [release](.github/workflows/release.yml), [package operations](.github/scripts/packages.sh). -[Dockerfile](docker/app/Dockerfile) targets: `development` for Compose and migration tooling; -`runner` for GHCR, with production dependencies, compiled application and TypeScript sources -for CLI commands. Both targets include `make` and cached `pnpm`; startup runs the service only. - -
+[Dockerfile](docker/app/Dockerfile) targets: ----- +| Target | Purpose | +| :------------ | :--------------------------------------------------------------------------------------------------- | +| `development` | Local Compose deployment and migration tooling | +| `runner` | GHCR image: production dependencies, compiled code, TypeScript for CLI commands, and a non-root user | -
-Releasing a new version - -1. Prepare a branch and set `version` in `package.json` (`1.0.0` for the first release): -```bash -git switch dev -git pull --ff-only -git switch -c chore/release-v1.0.0 -# Update package.json before committing. -git add package.json -git commit -m "chore: release v1.0.0" -git push -u origin chore/release-v1.0.0 -``` -2. Open a PR into `dev`, pass CI, merge and wait for **Publish service image** to succeed. -3. Tag the published commit; the tag must match `package.json`: -```bash -git switch dev -git pull --ff-only -git tag v1.0.0 -git push origin v1.0.0 -``` +Both targets include `make`, `pnpm`, and `tsx`. The pinned `pnpm` version is cached +during the build. Database commands run inside the container. The host Makefile +invokes them through Compose. Container startup runs only the service.
diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..4a3238b --- /dev/null +++ b/README.ru.md @@ -0,0 +1,184 @@ +# @monadiam/template-service + +Language: [EN](README.md) | **RU** + +Универсальный шаблон сервиса на NestJS для MonadIAM — проверки аутентификации и разрешений через identity-service и access-control-service, журналы аудита и изменений с архивацией в ClickHouse через outbox, очистка устаревших записей через BullMQ. + +---- + +
+Состав шаблона + +| Компонент | Файлы | +| :-------------------------------------------------- | :----------------------------------------------- | +| Проверки аутентификации и разрешений | `src/context/infrastructure/guards` | +| Кеш прав доступа и его инвалидация | `src/context/infrastructure/queues/access-cache` | +| Повторные попытки и dead-letter для Kafka | `src/context/infrastructure/queues/kafka-retry` | +| Клиент Vault Transit | `src/common/services/vault-transit.service.ts` | +| Менеджер транзакций для журналов аудита и изменений | `src/common/transaction-manager` | +| gRPC-клиент AccessControl | `src/infrastructure/grpc` | +| Конфигурация PostgreSQL и миграции | `src/infrastructure/database` | +| Конфигурация Redis | `src/infrastructure/redis` | +| Наблюдаемость | `src/observability` | +| Локальное окружение Docker | `docker`, `docker-compose.yml` | + +Общие экспорты, которые понадобятся будущему доменному коду, можно помечать +`/** @public */`. Тогда `knip` не будет считать их неиспользуемыми, пока шаблон +не наполнен доменной логикой. + +
+ +---- + +
+Локальный запуск + +1. Сначала запустите инфраструктуру из репозитория [infrastructure](https://github.com/MonadIAM/infrastructure/tree/local), ветка `local`. + +2. Запустите [identity-service](https://github.com/MonadIAM/identity-service) и +[access-control-service](https://github.com/MonadIAM/access-control-service), +если требуются аутентификация и проверка разрешений. + +3. Создайте локальный файл окружения: +```sh +make env +``` + +4. Соберите и запустите сервис: +```sh +make build +make up +``` + +Адреса зависимостей внутри сети Docker: + +| Зависимость | Адрес внутри сети Docker | +| :------------------ | :--------------------------------- | +| PostgreSQL | `template-postgresql:5432` | +| Redis | `template-redis:6379` | +| Kafka | `kafka:29092` | +| Vault | `vault:8200` | +| Identity JWKS | `identity-service-app:4002` | +| Access Control gRPC | `access-control-service-app:50051` | + +Общие сети Docker создаются инфраструктурой: +`postgres-net`, `redis-net`, `kafka-net` и `vault-net`. + +
+ +---- + +
+Локальные порты + +| Ресурс | Порт | +| :--------- | :------------------------------------ | +| HTTP API | `4000` | +| PostgreSQL | `6000` на хосте, `5432` внутри Docker | +| Redis | `7000` на хосте, `6379` внутри Docker | + +
+ +---- + +
+Команды проекта + +| Makefile | Описание | +| :-------------------------------------- | :----------------------------------------------------------------------------- | +| **Инфраструктура** | | +| `make env` | Создать `.env` из `.env.local.example`. | +| `make build` | Собрать локальный образ Compose. | +| `make up` | Запустить сервис, инициализацию Vault и Vault Agent. | +| `make down` | Остановить и удалить контейнеры сервиса. | +| `make restart` | Выполнить `make down` и `make up`. | +| **Разработка** | | +| `make lint` | Проверить код через ESLint. | +| `make lint-fix` | Исправить только нарушения Prettier и вывести пути изменённых файлов. | +| `make tsc` | Проверить типы TypeScript без сборки (`tsc --noEmit`). | +| `make knip` | Найти неиспользуемые экспорты, файлы и зависимости. | +| `make secrets-check` | Проверить подготовленные к коммиту изменения на секреты через Gitleaks. | +| `make env-check` | Проверить согласованность DTO, env-шаблонов и переменных конфигурации. | +| `make intl-types` | Сгенерировать типы TypeScript из словарей переводов. | +| `make intl-check` | Выполнить все шесть проверок словарей из Git-хука. | +| **База данных** | | +| `make migrate` | Применить новые миграции MikroORM в работающем контейнере сервиса. | +| `make migration name="..."` | Создать миграцию MikroORM в работающем контейнере сервиса. | +| `make empty-migration name="..."` | Создать пустую миграцию MikroORM. | +| `make seed` | Запустить сидирование PostgreSQL в работающем контейнере сервиса. | +| **Тестирование** | | +| `make test` | Запустить модульные и интеграционные тесты (требуется Docker). | +| `make utest` | Запустить модульные тесты. | +| `make itest` | Запустить интеграционные тесты (требуется Docker). | +| `make coverage-unit` | Запустить модульные тесты с покрытием в `coverage/unit`. | +| `make coverage-integration` | Запустить интеграционные тесты с покрытием в `coverage/integration`. | +| `make coverage` | Запустить оба набора тестов с общим покрытием в `coverage/all`. | +| **Контейнер / GHCR (системные)** | | +| `make image` | Собрать образ сервиса; доступно вручную с `IMAGE`, `BASE_IMAGE`, `VCS_REF`. | +| `make image-publish` | Опубликовать образ с SHA коммита (GitHub Actions). | +| `make image-release` | Добавить опубликованному SHA-образу тег версии пакета (GitHub Actions). | +| `make image-cleanup` | Оставить последние 10 промежуточных SHA-образов и все релизы (GitHub Actions). | +| **LLM-инструменты (экспериментальные)** | | +| `make lsp` | Запустить MCP-сервер для навигации по TypeScript через stdio. | +| `make graphify` | Создать или обновить локальный граф кода и отчёт Markdown. | +| `make graphify-rebuild` | Пересоздать граф кода и отчёт с полным сканированием проекта. | +| `make graphify-html` | Обновить граф и отчёт, затем экспортировать визуализацию HTML. | + +
+ +---- + +
+Конфигурация стенда + +Настройки приложения: [.env.stand.example](.env.stand.example). На стенде Kubernetes передаёт +переменные окружения и монтирует Secrets; параметры Compose не используются. + +| Подключение | Режим на стенде | Монтируемые файлы / учётные данные | +| :---------- | :------------------------------------- | :------------------------------------------------------------- | +| PostgreSQL | CNPG `rw` / `ro`, TLS, пароль | `/tls/postgresql/ca.crt`; `POSTGRES_USER`, `POSTGRES_PASSWORD` | +| Redis | Sentinel, поиск текущего мастера, mTLS | `/tls/redis/{ca.crt,tls.crt,tls.key}`; `REDIS_PASSWORD` | +| Kafka | TLS + SCRAM-SHA-512 | `/tls/kafka/ca.crt`; `/secrets/kafka/password` | +| Vault | Transit через Unix-сокет Vault Agent | `/vault/proxy/agent.sock`, общий с контейнером Vault Agent | + +**Миграции и сидирование** + +```sh +kubectl exec -n monadiam-apps -c service -- make migrate +kubectl exec -n monadiam-apps -c service -- make seed +``` + +
+ +---- + +
+Пакет контейнера (GHCR) + +`ghcr.io/monadiam/template-service` + +| Событие | Действие | +| :------------ | :------------------------------------------------------------------------------------------------------------------ | +| PR в `dev` | Проверки и сборка образа сервиса без публикации | +| Push в `dev` | Сборка коммита после слияния PR и публикация `sha-` | +| Тег `vX.Y.Z` | Проверка версии в `package.json` и наличия коммита в `dev`; добавление тега `X.Y.Z` к его SHA-образу без пересборки | +| Очистка | Хранение 10 промежуточных SHA-версий и всех релизов | +| Стенд | Фиксация digest релизного образа | + +[CI](.github/workflows/ci.yml), [публикация](.github/workflows/publish.yml), +[релиз](.github/workflows/release.yml), [операции над пакетом](.github/scripts/packages.sh). + +Стадии [Dockerfile](docker/app/Dockerfile): + +| Стадия | Назначение | +| :------------ | :----------------------------------------------------------------------------------------------------- | +| `development` | Локальный запуск через Compose и работа с миграциями | +| `runner` | Образ для GHCR: production-зависимости, собранный код, TypeScript для CLI и пользователь без прав root | + +В обеих стадиях доступны `make`, `pnpm` и `tsx`. Версия `pnpm` закреплена и кешируется +при сборке. Команды БД запускаются внутри контейнера. Makefile на хосте вызывает их +через Compose. При старте контейнера запускается только сервис. + +
+ +---- diff --git a/package.json b/package.json index fc2c874..7850fbe 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Universal NestJS service boilerplate for MonadIAM — authn/permission guards wired against identity-service and access-control-service, audit/change-log capture with outbox-based ClickHouse archival, and BullMQ retention cleanup.", "author": "Daniil Egorov (https://github.com/Lambdius)", "license": "SEE LICENSE IN LICENSE", - "version": "1.0.0", + "version": "1.0.1", "main": "dist/index.js", "engines": { "node": "24.18.0" @@ -67,7 +67,7 @@ "@mikro-orm/nestjs": "7.0.2", "@mikro-orm/postgresql": "7.1.13", "@mikro-orm/seeder": "7.1.13", - "@monadiam/shared": "github:MonadIAM/shared#v3.0.1", + "@monadiam/shared": "github:MonadIAM/shared#v3.0.3", "@nest-lab/throttler-storage-redis": "1.2.0", "@nestjs/bullmq": "11.0.5", "@nestjs/common": "11.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f50c6fc..71ccb00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: 7.1.13 version: 7.1.13(@mikro-orm/core@7.1.13) '@monadiam/shared': - specifier: github:MonadIAM/shared#v3.0.1 - version: https://codeload.github.com/MonadIAM/shared/tar.gz/a7bbf40aa06a07617dbdad4356a5fb2b4db482ad + specifier: github:MonadIAM/shared#v3.0.3 + version: https://codeload.github.com/MonadIAM/shared/tar.gz/1cdf197fcf30fe43631bf6948437052d1a9548e0 '@nest-lab/throttler-storage-redis': specifier: 1.2.0 version: 1.2.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.1)(@nestjs/throttler@6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2))(ioredis@6.0.0(supports-color@8.1.1))(reflect-metadata@0.2.2) @@ -1106,9 +1106,9 @@ packages: engines: {node: '>=22.0.0'} hasBin: true - '@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/a7bbf40aa06a07617dbdad4356a5fb2b4db482ad': - resolution: {gitHosted: true, tarball: https://codeload.github.com/MonadIAM/shared/tar.gz/a7bbf40aa06a07617dbdad4356a5fb2b4db482ad} - version: 3.0.1 + '@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/1cdf197fcf30fe43631bf6948437052d1a9548e0': + resolution: {gitHosted: true, tarball: https://codeload.github.com/MonadIAM/shared/tar.gz/1cdf197fcf30fe43631bf6948437052d1a9548e0} + version: 3.0.3 engines: {node: 24.18.0} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': @@ -6088,7 +6088,7 @@ snapshots: uuid: 11.1.1 zod: 3.25.76 - '@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/a7bbf40aa06a07617dbdad4356a5fb2b4db482ad': {} + '@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/1cdf197fcf30fe43631bf6948437052d1a9548e0': {} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index df6e1b6..03d34e2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,7 +6,7 @@ color: true loglevel: info savePrefix: "" allowBuilds: - "@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/a7bbf40aa06a07617dbdad4356a5fb2b4db482ad": true + "@monadiam/shared@https://codeload.github.com/MonadIAM/shared/tar.gz/1cdf197fcf30fe43631bf6948437052d1a9548e0": true "@launchql/protobufjs": true "msgpackr-extract": true "unrs-resolver": true diff --git a/src/bootstrap/reference.bootstrap.ts b/src/bootstrap/reference.bootstrap.ts index 617edd6..23262f2 100644 --- a/src/bootstrap/reference.bootstrap.ts +++ b/src/bootstrap/reference.bootstrap.ts @@ -73,15 +73,11 @@ export abstract class BootstrapReference { } private static sources(document: object): Bootstrap.Reference.Source[] { - return [ - { slug: "access-control", title: "AccessControl", origin: process.env.ACCESS_CONTROL_SERVICE_URL }, - { slug: "notification", title: "Notification", origin: process.env.NOTIFICATION_SERVICE_URL }, - { slug: "identity", title: "Identity", origin: process.env.IDENTITY_SERVICE_URL }, - { slug: "template", title: "Template", origin: process.env.TEMPLATE_SERVICE_URL }, - ].map(({ slug, title, origin }) => - slug === DOCUMENT_SLUG - ? { content: document, title, slug, default: true } - : { url: `${origin}${REFERENCE_ROUTE}${DOCUMENT_ROUTE}`, title, slug }, + return [{ slug: "template", title: "Template", origin: process.env.TEMPLATE_SERVICE_URL }].map( + ({ slug, title, origin }) => + slug === DOCUMENT_SLUG + ? { content: document, title, slug, default: true } + : { url: `${origin}${REFERENCE_ROUTE}${DOCUMENT_ROUTE}`, title, slug }, ); } diff --git a/src/bootstrap/security.bootstrap.ts b/src/bootstrap/security.bootstrap.ts index 3685ba4..2a867e5 100644 --- a/src/bootstrap/security.bootstrap.ts +++ b/src/bootstrap/security.bootstrap.ts @@ -128,7 +128,10 @@ export abstract class BootstrapSecurity { .getInstance() .addHook("onRoute", (route) => { if (route.url === "/docs" || route.url.startsWith("/docs/")) { - route.helmet = { contentSecurityPolicy: { directives: referenceDirectives } }; + route.helmet = { + contentSecurityPolicy: { directives: referenceDirectives }, + crossOriginOpenerPolicy: { policy: "unsafe-none" }, + }; } });