From cd76ce8f3f6eb24883061a27d5dafdb55e8ce41d Mon Sep 17 00:00:00 2001 From: vitya Date: Tue, 4 Aug 2026 18:33:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(admin-api):=20=D0=B4=D0=B5=D0=BF=D0=BB?= =?UTF-8?q?=D0=BE=D0=B9=20admin-api=20=D0=BD=D0=B0=20vds=20(=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=2025)=20=E2=80=94=20admin-api.vds.kzntsv.site?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Образ registry.kzntsv.site/admin-api:ca0dfa3 (ESM-фикс). Stack 25, proxy-сеть, env 8/8 (DATABASE_URL/JWT_SECRET/S3), mem_limit 512m, traefik + LE. Live GREEN: /health ok, ключ pilorama98 проходит, LE-серт выдан. Coord-таска обновлена (Number resolved, staging, deploy). Co-Authored-By: Claude --- .tasks/coord-loader-admin-api-client.md | 59 +++++++++++++++++++- host-stacks/vds-kzntsv/admin-api.compose.yml | 53 ++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 host-stacks/vds-kzntsv/admin-api.compose.yml diff --git a/.tasks/coord-loader-admin-api-client.md b/.tasks/coord-loader-admin-api-client.md index 136cf82..71e6968 100644 --- a/.tasks/coord-loader-admin-api-client.md +++ b/.tasks/coord-loader-admin-api-client.md @@ -16,16 +16,69 @@ S2 `@snolla/admin-api-client`, S3 `@snolla/admin-schema-gen`. Канал: snolla шлёт ревью-запрос в мой inbox (commit/hash + что смотреть), я ревьюю и возвращаю финайдинги. Ревью S2 — **до публикации в Verdaccio** (pilorama98 завязан на клиент). +- **Ревьюер загрузчика pilorama98** (назначен vitya 2026-08-04): код миграции — + `ApiCatalogStore`, contract-tests, identity-тест. Канал: pilorama98 шлёт commit'ы + что + смотреть; фокус — identity (API vs DB одинаковое состояние) и byte-for-byte `Content`. ## Ревью-чекпойнты (snolla) +- [x] S2 + S3 — **ревью DONE, F1 CONFIRMED 2026-08-04T14:48Z, publish разрешён.** + Фиксы `f9714f2`+`ddf47a1`: F1a server-guard `Fields.length>0`, F1b typed-save без Fields при + пустых defs (Content byte-for-byte), F3 джанкции в `$transaction` (tx прокинут), F5 pascal-сан, + F6 no-clobber. Пере-ревью + тесты: admin-api **13/13**, schema-gen **19/19**, client 19/19+tsc0, + остатков `[TEST]` в проде 0. Нюанс F2: variants `?sku=` ✅, Sku в products search-OR не добавлен + (не блокер; loader матчит продукты списком/ProductId). Жду: publish 0.2.0/0.1.0 + API Key. - [ ] S1 auth-плагин (PAT+cookie-JWT) + keys-эндпоинты — после завершения -- [ ] S2 `@snolla/admin-api-client` — **до publish** -- [ ] S3 `@snolla/admin-schema-gen` — после завершения ## Текущий статус -- 🔵 **2026-08-04T14:29Z — зазор СНЯТ, скоуп расширен, контракт зафиксирован.** +- 🔵 **2026-08-04T15:26Z — деплой admin-api: стек 25 создан, блокер в Dockerfile.** + - Образ `admin-api:e1540de` собран+запушен. Стек 25 (`admin-api.vds.kzntsv.site`, env 8/8, + proxy-сеть, mem_limit 512m) создан. **Контейнер крашится**: ESM `ERR_MODULE_NOT_FOUND` + dist/plugins/prisma — 32 файла extensionless импортов + tsconfig `moduleResolution:bundler`. + Фикс маршрутизирован snolla (15-26-16Z): добавить `.js` + node16, или bundle esbuild. + Staging 4732 (tsx) работает — identity не блокируется. Жду sha фикса → пересборка + PUT. +- 🟢 **2026-08-04T15:18Z — staging admin-api перезапущен с Number-fix (911b9b7).** + Write-probe сам: Number 424242→424242 ✅, throwaway удалён. pilorama98 запускает + identity-прогон (bump 0.2.1 + Number в ApiCatalogStore, 104/104). +- 🟢 **2026-08-04T15:14Z — Number РЕШЁН, identity-тест разблокирован.** + - snolla: `Number` добавлен в products/save (`911b9b7`/`6fe1088`, client **0.2.1**): create — + присланный иначе findNextNumber, update — ставит если прислан. Тесты 23/23+19/19, чисто. + - pilorama98: F1 (apiKey→local.json gitignored)+F2 (TypeId из чтения) пофикшены (4372c59, 103/103). + Собирают identity-тест на staging (bump 0.2.1 + Number в ApiCatalogStore). + - **Ротация ключа** (был в истории git fb07849): рекомендация — принять риск (приватный Gitea), + решение за vitya. + - Деплой: жду Dockerfile от snolla. +- 🟢 **2026-08-04T15:12Z — ревью ApiCatalogStore DONE (тесты 102/102).** + - **F1 🔴** API-ключ `sk_6a83…` закоммичен в `apps/loader/config/default.json` (трекаемый) — + нарушение acceptance №4; фикс: `config/local.json` gitignored + ротация (по решению pilorama98). + - **F2 🟡** `updateProduct` хардкодит `TypeId` (расходится с DB-путем для др. типа); F3 🟡 get+save + на джанкции (перф). Content byte-for-byte/джанкции/пагинация/store-selection ✅. + - Number: зазор подтверждён, вопрос у snolla (15:06Z). +- 🟢 **2026-08-04T15:06Z — staging write-probe: ок, но зазор `Number`.** + - pilorama98: products/save Content byte-for-byte ✅, variants/save ✅. ⚠️ **`Number` + серверно-манится** (saveSchema без Number, create = findNextNumber max+1) → identity по + Number ломается (лоадер пишет явно). Вопрос маршрутизирован snolla (15-06-26Z), + рекомендация — добавить `Number` в save-schema. Если server-managed → решение vitya + (ослабить identity по Number / гибрид). + - **Деплой-план согласован с snolla** (14-56-38Z): Dockerfile — snolla коммитит, + оркестрация/деплой — я; хостнейм `admin-api.vds.kzntsv.site`; один общий инстанс; + env JWT_SECRET (реальный)/DATABASE_URL/S3; mem_limit 512m. + +## Хронология + - **Staging admin-api поднят** `127.0.0.1:4732` → `MoreThenCmsStaging` (свежая копия + MoreThenCms на vds mssql, restore из COPY_ONLY-бэкапа, snolla=db_owner, логи сжаты). + Ключ pilorama98 проходит (scope read-write). Письмо pilorama98 14-59-32Z (staging + + роль ревьюера). + - **Лог-чистка (вопрос vitya):** MoreThenCms 10ГБ→1ГБ (SIMPLE recovery + SHRINK, кап-риск + Express устранён), staging log 10ГБ→259МБ. Диск 35ГБ свободно. + - **Деплой-план согласован с snolla** (14-56-38Z): Dockerfile — snolla коммитит, + оркестрация/деплой — я; хостнейм `admin-api.vds.kzntsv.site`; один общий инстанс; + env JWT_SECRET (реальный)/DATABASE_URL/S3; mem_limit 512m. + - Инцидент snolla: cleanup-live-keys `[CLIENT-VERIFY]` — LIKE character class снёс ключ, + перевыпущен + скрипт починен (7eb1bb8). + +## Хронология - **Решение vitya: расширять snolla** (AskUserQuestion). snolla подтвердила независимо — variants/categories/tags + джанкции в текущем скоупе, реализует после ядра кодгена. - **Контракт от snolla relay'нут pilorama98** (14-29-11Z): query (GET list?scope&skip&take / diff --git a/host-stacks/vds-kzntsv/admin-api.compose.yml b/host-stacks/vds-kzntsv/admin-api.compose.yml new file mode 100644 index 0000000..835b104 --- /dev/null +++ b/host-stacks/vds-kzntsv/admin-api.compose.yml @@ -0,0 +1,53 @@ +# admin-api — shared management-plane API (Fastify, port 4731) на vds-kzntsv (89.253.255.94). +# +# STAGING-FIRST. Source-of-truth compose — admin-репо; применяется через Portainer API/UI +# (VDS-rule: стеки через Portainer, не ssh+compose). +# +# Модель: @snolla/admin-api (Fastify, Prisma→MSSQL) — resource-oriented CRUD + ContentTools +# (fields[]/Content) + keys-эндпоинты (Bearer PAT sk_…). Общий management-plane инстанс +# (один на все тенанты, по дизайну admin-typed-api-design). Читает/пишет MoreThenCms +# (mssql.kzntsv.site, user snolla). Первый потребитель — @pilorama98/loader (apps/loader). +# +# Образ: registry.kzntsv.site/admin-api:ca0dfa3 — собран АДМИНОМ на VDS из victor/snolla +# (packages/admin-api/Dockerfile, multi-stage node:22-slim, deps public npm — verdaccio не нужен). +# Тег = git-sha snolla master. ca0dfa3 = ESM-фикс (relative imports .js). Healthcheck GET /health (в Dockerfile). +# +# Домен: admin-api.vds.kzntsv.site (wildcard → VDS). LE cert на первый hit. +# +# Runtime egress: mssql.kzntsv.site:1433 (traefik TCP), minio.vds.kzntsv.site:443 (theme-files ops). +# +# Секреты — env на VDS (Portainer stack env), НЕ в образ, НЕ в git. Контракт стека: +# DATABASE_URL JWT_SECRET S3_ENDPOINT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY S3_REGION S3_FORCE_PATH_STYLE THEMES_BUCKET +# JWT_SECRET — реальный (не дефолт dev-secret-change-in-production). S3_* — для theme-storage-плагина +# (loader'у не нужен, но общий инстанс — заводим сразу). + +services: + admin-api: + container_name: admin-api + image: registry.kzntsv.site/admin-api:ca0dfa3 + restart: unless-stopped + mem_limit: 512m # guardrail: admin-api базало ~100-200M; OOM -> restart. + environment: + NODE_ENV: production + TZ: Europe/Moscow + # --- runtime-секреты (значения из Portainer stack env; НЕ хранить тут) --- + DATABASE_URL: ${DATABASE_URL} + JWT_SECRET: ${JWT_SECRET} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + S3_REGION: ${S3_REGION} + S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE} + THEMES_BUCKET: ${THEMES_BUCKET} + networks: + - proxy + labels: + traefik.enable: "true" + traefik.http.routers.admin-api.entrypoints: websecure + traefik.http.routers.admin-api.rule: Host(`admin-api.vds.kzntsv.site`) + traefik.http.routers.admin-api.tls.certresolver: letsEncrypt + traefik.http.services.admin-api.loadbalancer.server.port: "4731" + +networks: + proxy: + external: true