diff --git a/.tasks/NEXT_SESSION.md b/.tasks/NEXT_SESSION.md index 453b0fa..94bf731 100644 --- a/.tasks/NEXT_SESSION.md +++ b/.tasks/NEXT_SESSION.md @@ -1,37 +1,43 @@ --- -_last_updated_: 2026-06-29T20:05:00Z -session_id: 2026-06-29-pilonuxt-deploys +_last_updated_: 2026-07-01T13:45:00Z +session_id: 2026-07-01-labtools-vds-staging --- # Next session handoff ## Recent commits -- `685f2735` deploy(pilonuxt): cf2bba2 LIVE — forms-api + full @snollajs/snolla drop (ADR-0010) -- `dca1bf77` deploy(pilonuxt): 40bb383 LIVE — staticPages + robots.txt from DB (ADR-0009) -- `c3e84583` wiki(books-vds): document public bookva-db endpoint :33306 - -## Что произошло (сжато) -Два последовательных деплоя pilonuxt на прод (Portainer стек 16, vds-kzntsv 89.253.255.94), оба по наряду от victor/pilorama98.ru через inbox: -1. **40bb383** — DB-backed staticPages + robots.txt (ADR-0009). Smoke 6/6. -2. **cf2bba2** — forms на `@snollajs/forms-api`, полный дроп `@snollajs/snolla`, core@0.1.1 (Buffer вместо btoa). Tree-check `.output` 4/4 (0× snolla, 0× btoa, 1× data@0.9.1) + smoke зелёный (SSR 200, robots/yandex из БД, форма 422/404). - -Оба наряда 🟢 closed, notify отправлены. **Текущий прод pilonuxt = `cf2bba2`.** Откат: стек 16 → `40bb383` (образ на VDS цел). +- `deploy(labtools)`: staging собран+развёрнут на vds-kzntsv, smoke parity GREEN, рунбук + стек-черновик (этой сессии). +- (до этого: pilonuxt sitemap taxonomy deploys — не связано) ## Open треки | Трек | Готовность | Entry-point | |---|---|---| -| (нет активных 🔴) | — | борд чист по pilonuxt | -| backlog/blocked без изменений | ⚪/🔵/🟡 | STATUS.md: harden-books-vds-exposed-ports ⚪, kreknin-self-backup ⚪, books-* bootstrap/cutover 🔵, fix-nl-vds-reality 🟡 (ждёт client-side от vitya) | +| `[labtools-web-vds-deploy]` | STAGING GREEN, ждём DNS-cutover | `.tasks/STATUS.md` + `.wiki/concepts/labtools-vds-deploy-runbook.md` | -## Спроси / подтверди -- (нет на этом раунде) — обе задачи закрыты, ничего mid-task не висит. +**Состояние labtools:** образ `registry.kzntsv.site/labtools:43e28ba` собран на VDS + push; Portainer stack `labtools` +(Id 17, endpoint 1) на `labtools.vds.kzntsv.site`, контейнер healthy; 8 секрет-env в stack-env. Smoke vs бой +`www.labtools.ru` — GREEN (меню 200/200, редиректы 301 1:1, тема+`/assets` из MinIO байт-в-байт, Cache-Control 86400). +Бой на RUVDS IIS (`80.64.31.36`) ЖИВОЙ, не тронут. Env-контракт (8 секретов, источники) — в рунбуке. + +## Спроси user'а +- **DNS хозяин перенастраивает A-записи `labtools.ru`/`www` → `89.253.255.94` ЗАВТРА (2026-07-02).** Как оператор + подтвердит «DNS флипнут / флипаю сейчас» — выполнить cutover ops-шаги (ниже). +- `labtools.pro` — отдельная таска (оператор решил 2026-07-01, не в scope этой). Ждёт заведения. + +## Cutover ops-шаги (по сигналу оператора о флипе — НЕ раньше) +1. В стеке `labtools` (Portainer Id 17) добавить в traefik-rule: `Host(labtools.ru) || Host(www.labtools.ru)` — + **синхронно с флипом** (не заранее — см. guard ниже). Правка source: `admin/host-stacks/vds-kzntsv/labtools.compose.yml` → PUT в Portainer. +2. Проверить LE cert выпущен на боевом хосте + прогнать тот же смоук-свип уже на `https://www.labtools.ru`. +3. Отчитаться оператору + workshop (from admin). ## Не делать (preemptive guards) -- **Notify victor/pilorama98.ru → ТОЛЬКО `~/projects/pilorama98.ru/.claude-inbox/`** (монореп, живой, метётся в `.read/`). НЕ dotted `~/projects/.pilorama98.ru/` (мёртвый, сообщения копятся непрочитанными). Промахнулся раз на 40bb383-notify. См. memory `pilorama98-inbox-is-monorepo-not-dotted`. -- **Автопуш был выдан на ЭТУ сессию** — на старте следующей сбросить в ask-mode (project-discipline Rule 4). -- **pilonuxt build-канал:** push с дома гарантированно 499 на `.output`-слое → сразу `docker save | ssh -i ~/.ssh/id_ed25519 vitya@89.253.255.94 'sudo docker load'` + push с VDS (registry локален). Не тратить время на home-push. -- **forms-api smoke:** 422 только с `Accept: application/json` (иначе `prefersJson` false → 404, не баг). -- **Деплой pilonuxt всегда требует ФОРС-регенерации клиента:** `rm -rf apps/web/src/generated` + `yarn workspace nuxt-app schema-gen` (egress в MSSQL) перед docker build — иначе ensure-schema skip соберёт stale-клиент. +- **НЕ добавлять `Host(labtools.ru)` в traefik-rule ДО флипа DNS** — traefik проактивно на reload тянет LE-cert (HTTP-01), + challenge упадёт на RUVDS (DNS ещё там), сожжём LE rate-limit → задержка cert на реальном cutover'е. +- **НЕ выводить RUVDS IIS labtools из эксплуатации** (директива оператора 2026-07-01) — это rollback-путь. +- **НЕ флипать DNS самим** — это делает хозяин домена. +- **Автопуш в ЭТОЙ сессии НЕ выдавался** — коммиты локальные, не запушены; на старте следующей — ask-mode (project-discipline Rule 4). +- Build-канал labtools: собирать НА VDS из чистого `git archive` (без `config/default.json`), push с VDS (registry локален). +- `labtools.pro` — не трогать в рамках этой таски. ## Memory updates за сессию -- Создана `pilorama98-inbox-is-monorepo-not-dotted.md` (reference) + строка в MEMORY.md — куда слать notify pilorama98. +- (нет на этом раунде — всё в STATUS.md + рунбуке `labtools-vds-deploy-runbook.md`) diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 6924cb9..6dad484 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -989,7 +989,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api --- -## ⚪ [labtools-web-vds-deploy] — Деплой `labtools.ru` snolla-приложения (`victor/labtools.ru/apps/web`) в docker-контейнере на Rusonyx VDS за traefik. Приложение parity-подтверждено (ре-ревью №2 PASS: HTML+визуал byte-parity с боем `www.labtools.ru`, ассеты из MinIO, ноль локальных хаков, `@snollajs/snolla@0.28.2`). +## 🟡 [labtools-web-vds-deploy] — Деплой `labtools.ru` snolla-приложения (`victor/labtools.ru/apps/web`) в docker-контейнере на Rusonyx VDS за traefik. Приложение parity-подтверждено (ре-ревью №2 PASS: HTML+визуал byte-parity с боем `www.labtools.ru`, ассеты из MinIO, ноль локальных хаков, `@snollajs/snolla@0.28.2`). ## Кто/где собирает образ (решение workshop) **Образ собирает АДМИН на VDS** из `Dockerfile`, который пишет имплементер. build нужен docker + доступ к verdaccio (`verdaccio.kzntsv.site`) + `VERDACCIO_TOKEN` — эта инфра у админа/на VDS; имплементер владеет корректностью Dockerfile, не реестром. Кросс-машинный трансфер образа не нужен. @@ -1019,9 +1019,9 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api **notify:** OpeItcLoc03/workshop Dev-source: `victor/labtools.ru/apps/web` (parity-док/буфер — в `.workshop/.brainstorm/snolla-sites-migration.md`). Blast: cutover затронет живой прод labtools.ru — staging-first, cutover gated. -**Status:** ready -**Where I stopped:** (not started) — labtools apps/web parity-подтверждён (ре-ревью №2 PASS: HTML+визуал byte-parity, ассеты из MinIO, 0 хаков, snolla 0.28.2). -**Next action:** Дождаться Dockerfile+env-листа от имплементера (labtools) → собрать образ на VDS из его Dockerfile → traefik+staging-host → runtime-секреты env → smoke parity vs бой → (cutover labtools.ru DNS = отдельный gated шаг с оператором) → вики-рунбук. +**Status:** in_progress +**Where I stopped:** STAGING ЗЕЛЁНЫЙ (2026-07-01). Образ `registry.kzntsv.site/labtools:43e28ba` собран на VDS + push; Portainer stack `labtools` (Id 17) на `labtools.vds.kzntsv.site`, контейнер healthy (MSSQL+S3 подключены); 8 секрет-env в stack-env. Smoke parity vs бой GREEN: все страницы меню 200/200, редиректы `.php`/trailing-slash/lowercase 301 1:1, `theme.css` из MinIO байт-в-байт (Cache-Control 86400), `/assets/*.webp` байт-в-байт. Рунбук: `.wiki/concepts/labtools-vds-deploy-runbook.md`. +**Next action:** Cutover live DNS `labtools.ru` — GATED, ждём добро оператора (бой на RUVDS 80.64.31.36 живой, НЕ выводить). По go: добавить `Host(labtools.ru)||Host(www.labtools.ru)` в traefik-rule стека → оператор флипает A-записи reg.ru → 89.253.255.94 → LE cert → smoke на боевом хосте. labtools.pro — ОТДЕЛЬНАЯ таска (deferred, решение оператора 2026-07-01), не в scope. **Branch:** master **Notify:** OpeItcLoc03/workshop diff --git a/.wiki/concepts/labtools-vds-deploy-runbook.md b/.wiki/concepts/labtools-vds-deploy-runbook.md new file mode 100644 index 0000000..9c0a3ec --- /dev/null +++ b/.wiki/concepts/labtools-vds-deploy-runbook.md @@ -0,0 +1,70 @@ +--- +title: labtools.ru snolla-app — VDS deploy runbook (stack / traefik / env / cutover / rollback) +type: concept +tags: [labtools, snolla, vds, deploy, docker, portainer, traefik, runbook, migration] +related: [[../entities/vds-kzntsv]], [[../entities/ruvds-iis-host]], [[portainer-stack-management-vds]], [[minio-imgproxy-on-vds]] +updated: 2026-07-01 +--- + +# labtools.ru → VDS deploy runbook + +Вынос `labtools.ru` (snolla-приложение, `@snollajs/snolla`, server-side Liquid) с [[../entities/ruvds-iis-host]] +(catch-all CMS) в отдельный docker-контейнер на [[../entities/vds-kzntsv]] (89.253.255.94), за traefik. +Модель — snolla-app (НЕ pilonuxt/Nuxt-contentApi): читает БД-контент MoreThenCms (`mssql.kzntsv.site`) ++ ассеты из MinIO (`minio.kzntsv.site`) через imgproxy. + +## Артефакты +- **Код:** `victor/labtools.ru` @ `43e28ba` (apps/web, parity ре-ревью №2 PASS). `deploy/Dockerfile` (multi-stage node:22-slim). +- **Образ:** `registry.kzntsv.site/labtools:43e28ba` (+`:latest`). Собран НА VDS (обход traefik-499), 114 MB content. +- **Стек Portainer:** `labtools` (Id 17, endpoint 1). Source-of-truth compose: `admin/host-stacks/vds-kzntsv/labtools.compose.yml`. + +## Сборка образа (на VDS) +```bash +# чистый git-archive (только tracked → без config/default.json и node_modules) на VDS +git -C ~/projects/labtools.ru archive --format=tar 43e28ba \ + | ssh vitya@89.253.255.94 'rm -rf ~/build/labtools && mkdir -p ~/build/labtools && tar -x -C ~/build/labtools' +# build с build-arg VERDACCIO_TOKEN (pass vds-kzntsv/full-env VERDACCIO_CI_TOKEN), push из VDS +ssh vitya@89.253.255.94 'cd ~/build/labtools && docker build -f deploy/Dockerfile \ + --build-arg VERDACCIO_TOKEN= -t registry.kzntsv.site/labtools:43e28ba -t registry.kzntsv.site/labtools:latest . \ + && docker push registry.kzntsv.site/labtools:43e28ba && docker push registry.kzntsv.site/labtools:latest' +``` +- VERDACCIO_TOKEN — build-time only (discarded build-стейдж; в финальный образ не попадает; docker linter warn-only). +- `config/default.json` (dev-секреты) исключён `.dockerignore` — в образе только `production.json` + `custom-environment-variables.json`. + +## Runtime env-контракт (8 секретов — Portainer stack-env, НЕ в образ/git) +Маппинг env→config: `apps/web/config/custom-environment-variables.json`. Non-secret (host/siteId/endpoints) — в `production.json`. + +| ENV | Значение | Источник | +|---|---|---| +| `DB_USER` | `snolla` | web.config боя MoreThenCmsEntities | +| `DB_PASSWORD` | ⟨секрет⟩ | web.config боя MoreThenCmsEntities (User Id=snolla) | +| `IMGPROXY_KEY` | ⟨128hex⟩ | `pass minio-vds/full-env` (== сервер imgproxy books-vds — HMAC должен совпадать) | +| `IMGPROXY_SALT` | ⟨128hex⟩ | `pass minio-vds/full-env` | +| `S3_ACCESS_KEY_ID` | ⟨секрет⟩ | `pass minio-vds/full-env` (MINIO_ROOT_USER) | +| `S3_SECRET_ACCESS_KEY` | ⟨секрет⟩ | `pass minio-vds/full-env` (MINIO_ROOT_PASSWORD) | +| `SMTP_USER` | `noreply@snolla.com` | `pass snolla-smtp/full-env` | +| `SMTP_PASSWORD` | ⟨секрет⟩ | `pass snolla-smtp/full-env` (SMTP_PASS; default.json имел dev-значение — НЕ брать оттуда) | + +PORT — не переопределять (образ дефолтит 5000; EXPOSE 5000; healthcheck `/robots.txt` на $PORT). traefik service-port = 5000. + +## Runtime egress (наружу из proxy-сети) +`mssql.kzntsv.site:1433` (БД-контент, обязателен) · `minio.kzntsv.site:443` (ассеты) · `imgproxy.kzntsv.site:443` · `smtp.yandex.ru:465` (формы). + +## Staging smoke (2026-07-01, GREEN) — `labtools.vds.kzntsv.site` vs бой `www.labtools.ru` +- Все страницы меню (home/about/clients/contacts/cookie/privacy/6×products) — 200/200, дельта размера стабильна (длина хоста в canonical/og). +- Редиректы: `.php`→301→`/`, `/about/`→301 (trailing-slash), `/Products/Press-Forms`→301→lowercase — 1:1 с боем. +- `theme.css` из MinIO — **байт-в-байт** (12248), `Cache-Control: max-age=86400, public, must-revalidate`. +- `/assets//*.webp` из MinIO — **байт-в-байт** с боем (44444, 24354); content-type `application/octet-stream` — как на бою. +- Контейнер healthy (MSSQL+S3 подключены). + +## Cutover (live DNS) — GATED, действие ОПЕРАТОРА +Бой на [[../entities/ruvds-iis-host]] (`80.64.31.36`) **остаётся живым** до подтверждения. Порядок: +1. **(ops)** В стеке `labtools` добавить в traefik-rule: `Host(\`labtools.ru\`) || Host(\`www.labtools.ru\`)` (LE HTTP-01 выпустит cert на первый хит после DNS-flip). +2. **(оператор)** reg.ru: A-записи `labtools.ru` + `www.labtools.ru` → `89.253.255.94` (с RUVDS `80.64.31.36`). Понизить TTL заранее. +3. Проверить LE cert выпущен + smoke на боевом хосте. +4. `labtools.pro` — открытый вопрос (бой IIS обслуживал и его; scope таски = labtools.ru). Решить отдельно. + +## Rollback +- **DNS:** вернуть A-записи `labtools.ru`/`www` → `80.64.31.36` (RUVDS IIS живой, нетронут). Откат = смена DNS. +- **Стек:** Portainer → stack `labtools` → remove (или откат тега образа). Образ в registry остаётся. +- RUVDS IIS labtools **не выводить из эксплуатации** до явного решения оператора (директива 2026-07-01). diff --git a/host-stacks/vds-kzntsv/labtools.compose.yml b/host-stacks/vds-kzntsv/labtools.compose.yml new file mode 100644 index 0000000..bd26d61 --- /dev/null +++ b/host-stacks/vds-kzntsv/labtools.compose.yml @@ -0,0 +1,54 @@ +# labtools — prod-фронт labtools.ru (snolla-приложение, server-side Liquid, node) на vds-kzntsv (89.253.255.94). +# +# ЧЕРНОВИК / STAGING-FIRST. Source-of-truth этого compose — admin-репо; применяется через Portainer +# API/UI (VDS-rule: стеки через Portainer, не ssh+compose). +# +# Модель: НЕ pilonuxt/Nuxt-contentApi. Это монолит @snollajs/snolla — читает БД-контент MoreThenCms +# (mssql.kzntsv.site) + server-side Liquid render. Ассеты из MinIO (minio.kzntsv.site) через imgproxy +# (imgproxy.kzntsv.site). node-config: NODE_ENV=production → default(dev,gitignored)+production.json+ENV-override. +# +# Образ собирается АДМИНОМ на VDS из victor/labtools.ru/deploy/Dockerfile (build-arg VERDACCIO_TOKEN, +# @snollajs/* из verdaccio.kzntsv.site). Тег = git-sha монорепы labtools.ru. [ТЕГ TBD — ждём Dockerfile имплементера] +# +# Домен: STAGING — labtools.vds.kzntsv.site (под wildcard *.vds.kzntsv.site, cert LE HTTP-01 авто). +# Cutover live www.labtools.ru — ОТДЕЛЬНЫЙ gated шаг: бой живой на RUVDS IIS (80.64.31.36), НЕ выводить. +# DNS переключает ОПЕРАТОР по сигналу «staging green». План отката = revert DNS на RUVDS. +# +# Runtime egress (наружу из proxy-сети): mssql.kzntsv.site:1433, minio.kzntsv.site:443, +# imgproxy.kzntsv.site:443, smtp.yandex.ru:465. +# +# Секреты — env на VDS (Portainer stack env), НЕ в образ, НЕ в git. Значения ${...} инжектит Portainer +# из stack-переменных. Контракт (custom-environment-variables.json): +# DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD + +services: + labtools: + container_name: labtools + image: registry.kzntsv.site/labtools:43e28ba # git-sha victor/labtools.ru master + restart: unless-stopped + environment: + NODE_ENV: production + TZ: Europe/Moscow + # PORT не переопределяем — образ дефолтит 5000 (EXPOSE 5000, healthcheck на $PORT) + # --- runtime-секреты (значения из Portainer stack env; НЕ хранить тут) --- + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + IMGPROXY_KEY: ${IMGPROXY_KEY} + IMGPROXY_SALT: ${IMGPROXY_SALT} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + SMTP_USER: ${SMTP_USER} + SMTP_PASSWORD: ${SMTP_PASSWORD} + networks: + - proxy + labels: + traefik.enable: "true" + traefik.http.routers.labtools.entrypoints: websecure + # STAGING-хост. Боевой www.labtools.ru || labtools.ru добавить ТОЛЬКО на cutover-шаге. + traefik.http.routers.labtools.rule: Host(`labtools.vds.kzntsv.site`) + traefik.http.routers.labtools.tls.certresolver: letsEncrypt + traefik.http.services.labtools.loadbalancer.server.port: "5000" + +networks: + proxy: + external: true