Gitea secrets для PORTAINER_STACK_ID_BOOKVA_* + GITEA_TOKEN, создание
Portainer stacks для bookva-{api,web,scheduler,task-runner,ozon-mcp,
ops-mcp,db,mongo,es,ntfy}, volume create+copy (Mongo/ES/MinIO/configs),
bookva-db login-gate password-replace, DNS bookseller.kzntsv.site,
PWA redirect env в books-web, books-web embedded-api env (pending
step из embed-api handoff).
Dev-side done в victor/books master:
- 5e28fd1 embed-api + zod^4 (root cause: zod 3.x в web shadowed
root zod 4.x, @nuxt/ui v4.5 silent broke without zod 4 API)
- 4a9cafc ES endpoint per-tenant via custom-environment-variables.json
- c1e58cf PWA redirect banner (user.id=1 modal)
- 88df172 deploy.yml per-tenant (tenant input + per-tenant stack IDs)
Overlay-repos обновлены: bookva-overlay 58d4295 (+ ES env + ntfy
full config), slovo-overlay bca6bbc (+ ES env + ntfy full config).
24 KiB
Admin Task Board
Updated: 2026-05-26 — заведена bookva-tenant-cutover-prep ⚪ (7-step prep на books VDS перед поднятием Bookva стека). Code-side done в victor/books master: embed-api с zod^4 fix, ES endpoint per-tenant, PWA redirect banner, deploy.yml per-tenant, overlay-репо finalize, ntfy per-tenant. Осталась только Portainer/Gitea-secrets/VDS работа.
Updated: 2026-05-25 (iis-migration-to-ruvds 🟢 closed Phase 1 per user decision — 9/24 hostnames live на RUVDS, source IIS оставлен running. migrate-elasticsearch-to-books-vds 🟢 closed ранее сегодня. 16 IIS hostnames + LE renewal pipeline + decommission — descoped в Closure note, не отдельные tracker tasks.)
🟢 [migrate-elasticsearch-to-books-vds] — closed 2026-05-25 — ES indices с Windows (elasticold.kzntsv.site, 7.10.1) → books VDS (elasticsearch.kzntsv.site, 7.10.0). 3 indices, 928616 docs. Reindex-from-remote через reindex.remote.whitelist env, добавленный в Portainer stack 33. Pre-migration snapshot в kreknin repo как rollback. Consumer configs sed'd, 3 containers restarted, smoke green. Source НЕ выключен.
См. migrate-elasticsearch-to-books-vds.md § Closure note.
🟢 [books-vds-stacks-to-portainer] — closed 2026-05-25 — 6 SSH-compose стеков на books VDS (89.253.255.133) мигрированы в Portainer-managed (endpoint 1, https://portainer.kzntsv.site). Skip traefik + portainer (management plane). Adapter scripts/books-vds-portainer-migration/migrate.sh. Wiki: portainer-stack-management-books-vds.md. Backup pipeline bind paths preserved (verified by inspection, full run pending next 06:00 MSK).
🟢 [books-vds-backup-daily-kreknin] — closed 2026-05-25 — daily backup books VDS (89.253.255.133, host4g.ru, CentOS 7) → kreknin в 06:00 MSK. DB dumps (mariadb/mongo×2) + ES snapshot via REST → rsync 4.87GB → ntfy/email (phone+inbox ✓). ES path.repo bootstrap + snapshot repo kreknin registered. См. books-vds-backup-daily-kreknin.md.
🟢 [unify-backup-notifications] — closed 2026-05-25 — единый формат push + email для VDS/RUVDS/windows-host (3 scripts), VDS run.sh импортирован в repo. См. unify-backup-notifications.md § Closed. windows-host self-deploy остаётся на user'е (elevated PS).
⚪ [bookva-tenant-cutover-prep] — 7-step prep на books VDS перед поднятием Bookva стека
Gitea secrets + Portainer bookva stacks + volume create/copy (Mongo/ES/MinIO/configs — beyond MariaDB из stateful-split-volume-copy) + bookva-db login-gate + DNS bookseller.kzntsv.site + PWA redirect env в books-web + books-web embedded-api env (pending step из embed-api). После — отдельной задачей bookva-tenant-cutover (compose up).
Контекст dev-source: victor/books .wiki/concepts/tenant-split.md rev v3. Code-side closed master: 5e28fd1 embed-api+zod^4, 4a9cafc ES endpoint, c1e58cf PWA redirect, 88df172 deploy.yml per-tenant. Overlay-repos: bookva-overlay/main 58d4295, slovo-overlay/main bca6bbc.
Acceptance: все 7 шагов выполнены (см. task-файл), но compose up bookva-* ещё не запущен — это отдельная задача.
Status: ready Where I stopped: (not started — все dev-side prep closed) Next action: Step 1 в Gitea UI — добавить bookva secrets (placeholder values). Шаги 2-7 по порядку. Blocker: — Branch: main
⚪ [stateful-split-volume-copy] — поднять bookva-db + slovo-db на VDS как копии books-db через cp -a volume
Ops-таска для Фазы 1 дизайна tenant-split из victor/books. Scope сужен 2026-05-25 (user: «просто поднимем 2 БД»): только MariaDB volume copy + up 2 контейнеров. Mongo / MinIO / DELETE / app-стеки — отдельными ops-тасками потом.
Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Фаза 1». Compose-файлы готовы в bookva-overlay / slovo-overlay (commit d0eb210 в books).
Acceptance: docker ps показывает живые bookva-db + slovo-db, оба отвечают SELECT 1. Текущий books-db стек снова в строю после maintenance window.
Status: ready Where I stopped: (not started — lean playbook готов в stateful-split-volume-copy.md) Next action: под maintenance window 5-10 мин на VDS — execute 5 команд из task-файла. Backup → stop books-db → cp -a в 2 volume'а → up 2 новых контейнера через Portainer → start books-db. Blocker: — Branch: master
🟢 [iis-migration-to-ruvds] — closed 2026-05-25 per user decision — Phase 1 done: RUVDS infra setup + 8.66GB scp + IIS recreate + 25 HTTPS SNI bindings (LE R13 expire 2026-07-22) + 9 hostnames (kupimknigi.spb.ru, emspb±www, pilorama98±www, labtools.pro±www, rimiz±www) live на 80.64.31.36 with correct per-tenant content. 16 hostnames остаются на windows source per user pace (incl. tandemmebel scope-exception). Source IIS:8089 + traefik routes ALIVE для rollback. Decommission + LE renewal + cleanup descoped в Closure note. См. iis-migration-to-ruvds.md § Closure note.
Status: closed 2026-05-25
Where I stopped: 2026-05-24 — kupimknigi.spb.ru + emspb.ru (+ www.emspb.ru) DNS A flipped на 80.64.31.36, authoritative ns1.reg.ru правильный, public resolver caches expire'ятся (8.8.8.8=~6h, 1.1.1.1=~24h max). RUVDS state: snolla site (8.66 GB / 44725 files) transferred + IIS recreated + 25 HTTPS SNI bindings c LE certs (R13, valid до 2026-07-22), 7 prod hostnames live-smoke через VDS (третья сеть) → 200 OK / correct content. Source IIS:8089 + traefik routes ALIVE — rollback ready. 2026-05-25 close-time DNS probe: ещё 5 пар hostnames swap'нуты user'ом silently — итого 9 на RUVDS, 16 на source.
Findings зафиксированы в iis-migration-to-ruvds.md Decisions log: (1) outbound 445 блокирует home ISP, не RUVDS-FW → SSH/scp = canonical transfer-метод; (2) home network HTTP-middlebox mangles Host header for direct external HTTP — real end-users не пострадают, тестировать через VDS; (3) traefik acme.json → IIS PFX recipe работает (extract + openssl pkcs12 -export + Import-PfxCertificate + AddSslCertificate by thumbprint); (4) IIS 10 HTTP/2 default; (5) maljarka.tandemmebel.ru + 3 rimiz hostnames → 502/404 — pre-existing CMS-tenant config gap, не migration defect.
Scope exception (2026-05-24 user decision): tandemmebel.ru + www.tandemmebel.ru ОСТАЮТСЯ на windows-IIS на неопределённый срок (отдельное решение user'а — site не готов к cutover ровно сейчас). DNS НЕ свапать. Cert на RUVDS уже импортирован, binding existing — может оставаться idle, traffic не пойдёт.
Next action:
- Снизить DNS TTL в reg.ru на оставшиеся 22 hostnames до 300s —
pilorama98.ru,labtools.{ru,pro},snolla.com+ 11 snolla subdomains +rimiz.{ru,snolla.com}+maljarka.tandemmebel.ru(но неtandemmebel.ru— см. Scope exception). Сократит cache-tail с 24h до 5 мин. - 24h soak kupimknigi + emspb — verify через cache-clean resolver (потом curl без
--resolve). - Bulk DNS swap оставшихся 22 hostnames на 80.64.31.36 (single sitting). tandemmebel.ru / www.tandemmebel.ru — пропустить.
- 1-week prod soak с RUVDS как live source для migrated hostnames.
- Decommission source IIS:8089 только для migrated hostnames (snolla catch-all site нельзя decommission'ить пока tandemmebel.ru на нём же). Решение defer до tandemmebel migration.
- LE renewal pipeline — win-acme + HTTP-01 на RUVDS после full cutover (LE certs expire 2026-07-22, soak window до ~07-15).
- Cleanup migration-temp:
Remove-NetFirewallRule 'smb-from-source','ssh-from-source'на RUVDS, удалить~/.ssh/ruvds-iis-migration*на source, очиститьC:\ProgramData\ssh\administrators_authorized_keysна RUVDS.
Полный план + Completed + Open questions + Remaining steps — в iis-migration-to-ruvds.md. Branch: master
Branch: master
⚪ [infra-inventory] — two-tier инвентаризация: public-карта в global wiki + admin-detailed runbook локально
Status: ready
Where I stopped: (not started — создана 2026-05-22 из инцидента gitea. vs git. + перепутанный source/dest IP в traefik access-логах; scope расширена в тот же день — single-page → two-tier per user «у все общее представление, у админа полное»)
Next action: см. полный план в infra-inventory.md. Шаги: (1) probing-фаза одна для обоих — nslookup известных subdomain'ов + vds-ops/synology-ops ops_docker_ps/inspect + knowledge_search по существующим bootstrap-concept'ам + rg "kzntsv.site" по ~/projects/. (2) Write A — light public в projects-wiki/concepts/infrastructure-inventory.md (машины, canonical hostnames, anti-aliases) → knowledge_ingest. (3) Write B — heavy admin в .admin/.wiki/concepts/infrastructure-stack.md (per-stack: версии, internal IPs, volumes, depends-on, backup paths, DR pointers) → commit + push. (4) Cross-refs A↔B + pointer в .admin/.wiki/CLAUDE.md Domain conventions.
Branch: n/a
🔵 [books-vds-bookva-bootstrap] — Ops-таска для Фазы 4 дизайна tenant-split из victor/books (см. mcp__projects-meta__knowledge_get slug=concepts/tenant-split). Поднять новый VDS для Bookva — billing на юрлицо Bookva (учредители развелись, каждое юрлицо платит инфру напрямую провайдеру).
Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md; brainstorm trace ~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md.
Direction: Slovo остаётся на текущем VDS, Bookva переезжает на новый.
Acceptance:
- Новый VDS у провайдера (Rusonyx или другой по предпочтению Bookva), billing-account оформлен на юрлицо Bookva.
- Docker + traefik + certbot развёрнуты.
- DNS
*.bookva.<tld>готов резолвиться на новый IP (фактический switch — вbooks-dns-cutover-bookva). - SSH-ключи: только инженерные (core-разраб + админ Bookva, если есть). Аналитики Bookva — НЕ имеют SSH к новому VDS.
Status: blocked
Where I stopped: (not started)
Next action: Pre-step: прочитать concepts/tenant-split.md § «Фаза 4» в victor/books (через mcp__projects-meta__knowledge_get slug=concepts/tenant-split или git clone victor/books → .wiki/concepts/tenant-split.md).
- Согласовать с учредителем Bookva: provider, configuration (CPU/RAM/disk), регистрация billing'а на юрлицо Bookva.
- Provision VDS, базовая настройка (firewall, fail2ban, SSH ключи).
- Установить Docker + docker-compose.
- Развернуть traefik + certbot из overlay-репо
victor/books-bookva/deploy/traefik.compose.yml. - Подготовить DNS-зону
bookva.<tld>(создать A-records, TTL=300 для возможности быстрого switch'а). - Smoke:
curl https://placeholder.bookva.<tld>→ 200 от nginx-placeholder или из traefik. - Документировать в
victor/books-bookva/README.md: hostname, IP, как ssh, runbook smoke-теста. - Закрыть с note: «VDS поднят, IP=<...>, готов к stack deploy».
Blocker:
OpeItcLoc03/admin books-stateful-split-execution(Фаза 1 — physical split на shared VDS, bookva-* контейнеры живут) +victor/books per-tenant-backup-and-observability(Фаза 2 — per-tenant backups настроены до физического переезда) Branch: n/a
🔵 [books-dns-cutover-bookva] — Ops-таска для Фазы 4, шаг 5 дизайна tenant-split из victor/books. DNS switch *.bookva.<tld> → новый VDS IP, с parallel-run старого bookva-стека на shared VDS 7 дней без публичного hostname.
Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Фаза 4».
Acceptance:
- DNS
*.bookva.<tld>(или конкретный hostname Bookva — определяется учредителем) указывает на новый VDS IP. - Старый bookva-стек на shared VDS остаётся запущенным, без публичного hostname (traefik labels убраны или disabled), доступен только по docker-сети для возможного отката.
- Smoke с двух разных сетей (mobile + офисный wifi) —
https://<bookva-hostname>отвечает с нового VDS. - Per-VDS backup'ы на новом VDS подтверждены — есть snapshot спустя 24ч после DNS-switch'а.
- Через 7 дней parallel-run без откатов — decommission старого bookva-стека (см. Фаза 4, шаг 7 в spec).
Status: blocked
Where I stopped: (not started)
Next action: Pre-step: прочитать concepts/tenant-split.md § «Фаза 4, шаги 4–7» в victor/books.
- Maintenance window 1–2ч (объявить за 7 дней).
- Финальная синхронизация:
mysqldump mariadb-bookvaна текущем VDS →mysqlна новом VDS (если schema/data разошлись с момента Фазы 2). - Smoke на новом VDS до DNS-switch'а: hosts-файл override → проверить login + base flow.
- DNS-switch: меняем A-record(s) для bookva-hostname на new IP. TTL=300 уже стоял (Phase 4 step 1).
- Через 1ч (TTL expiry + propagation): smoke с двух сетей.
- На старом VDS — отключить traefik labels для bookva-стека (контейнеры продолжают работать, но не доступны снаружи).
- Parallel-run 7 дней. Каждый день — smoke. Per-VDS backup на новом VDS — проверить хотя бы 1 успешный snapshot.
- Если за 7 дней проблем нет → закрыть с note «cutover stable». Если есть — DNS rollback на старый IP, follow-up debug task в victor/books.
- Decommission старого bookva-стека на shared VDS (отдельный шаг, не в этой таске — финальный backup в архив +
docker compose down+ volume drop). Blocker:books-vds-bookva-bootstrap+ victor/bookspwa-redirect-handoff(SW bump выкачен в shared API за 1–2 недели до этой таски). Branch: n/a
⚪ [books-bookva-user-whitelist-gathering] — Процессная ops-таска для Фазы 3, шаг 1 дизайна tenant-split из victor/books. Получить от учредителя Bookva письменный список пользователей, которые допущены к Bookva-инсталляции. Без этого списка cutover не возможен — иначе утечка доступа: либо лишние юзеры получат доступ к Bookva (через копирование «всех users»), либо легитимные юзеры заблокированы (через пустой whitelist).
Direction: Bookva — новая инсталляция, начинается с whitelist'а (закрытый список). Slovo — остаётся на текущем стеке, получает копию всех текущих users (статус-кво).
Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Двухуровневая изоляция пользователей → App-level» и § «Фаза 3, шаг 1».
Не блокируется ничем — можно начинать gathering сразу, лаг до cutover'а может быть значительным.
Acceptance:
- Письменный список от учредителя Bookva (email или подписанный документ): ФИО + role (если знают).
- Список зафиксирован в
OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.mdили в отдельной таблице. - Для каждой строки списка сверка с текущей
users-таблицей shared БД: user существует / роль соответствует / email актуален. - Любые расхождения (запрошенный user не существует в shared / роль другая) — обработаны: создать новый user в Bookva-БД post-cutover ИЛИ скорректировать список с учредителем.
Status: ready
Where I stopped: (not started)
Next action: 1. Отправить учредителю Bookva формальный запрос (email/мессенджер): «Для tenant-split нужен список пользователей, которые останутся в Bookva после разделения. Формат: ФИО + роль + email/login. Список будет применён на cutover'е как whitelist».
2. Дождаться ответа (это может занять дни/недели — нормально).
3. Получив список — сверить с users-таблицей shared БД:
SELECT id, login, email, role FROM users WHERE email IN (<list>) AND deleted_at IS NULL;
- Разобрать расхождения с учредителем (missing / role mismatch / inactive).
- Зафиксировать финальный список в
OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md(или в local-only encrypted file если NDA-чувствительно). - Финальный список становится input'ом для Фазы 3, шаг 1: при настройке Bookva-БД в неё попадают только эти users.
- Закрыть с note: «whitelist (N users) подтверждён учредителем, готов к применению на cutover'е». Branch: n/a
🔵 [books-stateful-split-execution] — Ops-таска для Фазы 1 дизайна tenant-split из victor/books. Выполнить playbook на shared VDS в maintenance window — docker volume copy для MariaDB + Mongo + MinIO bucket rename + DELETE cleanup. Identical bit-for-bit копии через cp -a alpine helper. Никаких mysqldump/mongodump/mc cp.
Playbook (dev-source, читать целиком до начала): victor/books/.tasks/stateful-split-volume-copy.md.
Контекст дизайна: mcp__projects-meta__knowledge_get slug=concepts/tenant-split § «Фаза 1 — Physical stateful split + overlay-repos». Brainstorm trace: ~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md.
Container naming: drop books- prefix → bookva-<svc> / slovo-<svc>.
TDD-режим: [skip-tdd: one-off migration on maintenance window] — verify через post-cleanup audit.
Acceptance:
bookva-db+slovo-dbконтейнеры живые, auditSELECT DISTINCT idSeller={1}/{2}соответственно.bookva-mongo+slovo-mongoконтейнеры живые, agenda-история preserved, jobs чужого tenant'а cleanup'нуты.- MinIO (один shared) имеет bucket'ы
bookva+slovoвместоbooks; объекты чужого tenant'а удалены. - Shared catalog tables (
books,editions,epz,epz_pages,warehouses,products) — полные копии в обеих БД. - Smoke с двух сетей: login через
bookva.<tld>→ Bookva-данные; текущий URL → Slovo. - Через 7-14 дней parallel-run без откатов → cleanup старых
books-*контейнеров и volumes (отдельный заход).
Status: blocked
Where I stopped: (not started)
Next action: Pre-step: прочитать victor/books/.tasks/stateful-split-volume-copy.md целиком. Убедиться что 7 open questions resolved (закрыто dev-prep таской victor/books stateful-split-volume-copy): FK CASCADE, units/receipts strategy, MinIO key structure, users whitelist, task_runs handling, chrome/traefik shared.
- Подтвердить overlay-репо
victor/bookva-overlay+victor/slovo-overlayготовы — compose-файлы дляbookva-*/slovo-*стеков на месте. - Подтвердить per-tenant backups настроены (
victor/books per-tenant-backup-and-observability) — иначе нет точки rollback. - Объявить maintenance window 1-2ч (без записи).
- Execute Steps 0-11 из playbook на shared VDS.
- Post-execution audit:
SELECT DISTINCT idSeller FROM <each>,db.agendaJobs.distinct('data.idSeller'),mc lson MinIO. - Smoke с двух сетей.
- Через 7-14 дней parallel-run без откатов → cleanup старых
books-*контейнеров+volumes (отдельный заход). - Closing note: «Physical split done, bookva-* + slovo-* контейнеры живут на shared VDS, audit clean, parallel-run start ».
Blocker:
victor/books stateful-split-volume-copy(dev-prep — 7 open questions resolved + dev-DB test passed) +victor/books per-tenant-backup-and-observability(Фаза 2 — per-tenant backups настроены) Branch: n/a