Compare commits

..

11 Commits

Author SHA1 Message Date
23b1a47c14 wiki(stostayer): ingest stostayer-admin-minio-config — :8090 admin on stostayer MinIO
Fix 2026-07-22: local .NET admin (C:\sites\stostayer\Web.config) S3 providers
re-pointed to stostayer MinIO. 3 bugs in original config: wrong creds
(AKIAJ2YJP72W6ZHCRE6Q = books-vds root, not stayer_minio), wrong endpoint
(minio.stostayer.ru is not S3 API port; prod uses minio-api.stostayer.ru),
wrong region (local -> AmazonS3Exception expecting us-west-1). Creds NOT in
pass — live in stostayer.new config/default.json. Admin now shares prod
storage; manual App_Data mirroring retired.

+ index.md, log.md. Also carries pending handoff (NEXT_SESSION) + runbook
update from 2026-07-21 stostayer-web 0.3.20 deploy session.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 10:45:13 +03:00
95168e301a meta(handoff): NEXT_SESSION — RUVDS DECOMM 2026-07-21, rimiz DNS flipped, 0 public domains on RUVDS
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 10:18:52 +03:00
ec3ac79272 meta(ruvds): rimiz.ru DNS flipped to VDS — zero public domains left on RUVDS
Authoritative ns1/ns2.reg.ru serve 89.253.255.94 for rimiz.ru + www.
Public resolvers still cache 80.64.31.36 (TTL 86400), clears in 24h.
RUVDS has no public domain pointing at it anymore.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 10:18:29 +03:00
535ed6170f meta(ruvds): DECOMM 2026-07-21 — powered off, forgotten
ruvds-iis-host (80.64.31.36) decommissioned at provider. Final offsite
snapshot verified on kreknin 2026-07-21 (8.8G, 7-day retention).
Only public domain still on RUVDS was dead rimiz.ru (404 since 2026-05-19);
all snolla-fleet migrated to VDS (stacks 17-22). Local .NET admin
independent of RUVDS. on-snolla runbook rollback-ref updated
(RUVDS no longer rollback-target — image-tag only).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 10:14:09 +03:00
vitya
50e263860d ops(on-snolla): rename site alias on->internal (admin at internal.snolla.com/admin)
- Sites.Alias+LoweredAlias on->internal (siteId B9ECDB50; PrimaryDomain on.snolla.com kept).
- Web.config primaryAlias on->internal (catch-all default-site; without it the whole
  catch-all NRE-500s on every request — primaryAlias references the renamed alias).
- hosts: on.snolla.com->internal.snolla.com under # snolla-local-admin marker.
- setup-local-snolla-admin.ps1 aliases array updated.
- runbook + NEXT_SESSION + memory updated.

VDS prod on.snolla.com unaffected (snolla-app uses config.siteId, not alias) — verified
byte-identical + smoke GREEN after rename. Incident: elevated hosts edit emptied hosts
to 0B (Get-Content -Raw returned null in RunAs context) — restored from Windows default
header + snolla-local-admin block. Memory snolla-catch-all-primaryalias-depends-on-alias.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 13:33:09 +03:00
vitya
bec7784ffa meta(handoff): write NEXT_SESSION — Task B (on-snolla) CUTOVER DONE 2026-07-20
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 13:18:47 +03:00
vitya
4c4d19382b feat(on-snolla): CUTOVER DONE — on.snolla.com live on VDS (stack 22, LE cert)
DNS reg.ru on.snolla.com A -> 89.253.255.94 (operator flip, authoritative NS verified).
Traefik rule swapped staging->Host(on.snolla.com), LE cert issued (CN=on.snolla.com,
YR1, until 2026-10-18). Live-smoke GREEN, homepage byte-identical prod-HTML, sitemap
3/3 parity. RUVDS IIS untouched (rollback = revert DNS).

- compose source-of-truth updated to LIVE rule.
- runbook .wiki/concepts/on-snolla-vds-deploy-runbook.md (cutover as-built + gotchas:
  snolla robotsTxt crash on undefined app.locals.domain, no Liquid form-tag, /c dead-link
  parity, Portainer JWT re-auth before PUT).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 13:17:59 +03:00
vitya
9705468ef4 feat(on-snolla): Portainer stack 22 source-of-truth (staging Host, mem_limit 512m, 8 env secrets)
on.snolla.com snolla-app live behind traefik on staging Host on-snolla.vds.kzntsv.site.
Image registry.kzntsv.site/on-snolla:473923e494db (snolla 0.42.1). Pending DNS cutover
(reg.ru on.snolla.com A 80.64.31.36 -> 89.253.255.94, then PUT rule -> Host(on.snolla.com)).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 13:17:59 +03:00
019f5bae26 meta(tasks): close [on-snolla-vds-migration] in OpeItcLoc03/admin 2026-07-20 10:17:10 +00:00
73ad7669b4 meta(tasks): close [vehicles-loader-redeploy-per-group-rate-diff] in OpeItcLoc03/admin 2026-07-20 08:00:55 +00:00
dcc84cec4b meta(handoff): lock Task B decisions — repo victor/on.snolla.com, admin creds in DB
Task B deferred to next session. Operator decisions locked:
- repo = victor/on.snolla.com
- admin creds in MoreThenCms.dbo.Accounts (read via snolla user / SA)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 10:31:08 +03:00
11 changed files with 332 additions and 56 deletions

View File

@@ -1,57 +1,50 @@
--- ---
_last_updated_: 2026-07-20T10:15:00Z _last_updated_: 2026-07-21T14:45:00Z
session_id: 2026-07-20-snolla-local-admin-restore-done session_id: 2026-07-21-stostayer-web-0320-deploy
prev_session_id: 2026-07-20-snolla-local-admin-on-snolla-design prev_session_id: 2026-07-21-ruvds-decomm
--- ---
# Next session handoff # Next session handoff
## Эта сессия — Task A DONE (local .NET admin restored) ## Эта сессия — деплой цен кондиционеров на stostayer.ru (legacy-web 0.3.20). ЗАВЕРШЁН ✅
🟢 **[snolla-local-admin-restore] ЗАКРЫТА.** Локальный catch-all IIS-сайт `snolla` поднят. `stostayer.new` прислал deploy-request (повышение цен в `packages/web/components/ConditionerPrice.vue`, commit `4bbdbbf`). Задеплоено на прод. По ходу вскрылся и разрешён давний ESM-блокер.
Спека: `.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md` (commit `b1b0092c`).
Tаска: `.tasks/snolla-local-admin-restore.md` (as-built). Коммиты: `b1b0092c` (design),
`d4278e79` (handoff), `c196964d` (Task A done + scripts).
### As-built (отличия от спеки) ### Что сделано
- **Selective copy ~100MB** (НЕ 8.76GB): tar-over-SSH исключил stale `App_Data/{assets,galleries,themes,uploadCache}` + `bin.old`. Команда в task-файле. - **ESM-блокер подтверждён + разрешён.** `packages/web` (Nuxt2/CJS) не собирался с master (ESM-миграция `c805e7e` data/api + ESM-переиздание snolla). Ретрофит 5 `require()`→dynamic `import()` (`nuxt.config.js`, `server/index.js`, 2 `serverMiddleware`→CJS). snolla-сессия починила bare-`get`-баг → опубликовала `@snollajs/snolla@0.7.6`. Образ `0.3.20` собран, build+runtime зелёные.
- **Repoint НЕ нужен** — RUVDS Web.config уже на `mssql.kzntsv.site,1433;Catalog=MoreThenCms;User Id=snolla;Password=fXkH4@8O%3pc` (тот же snolla SQL-user что stostayer.old) + MinIO S3 drop-in с **реальными ключами** (placeholder_count=0). - **Деплой на прод.** Push `0.3.20` в `docker.stostayer.ru`, redeploy стека 16 (`0.3.18``0.3.20`, Portainer PUT через SSH:20435 + внутренний IP portainer). Контейнер Up, `RestartCount=0`, обслуживает реальный трафик.
- Elevated setup: `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1` (ASCII-only — PS5.1 BOM-less ломается на Cyrillic/em-dash байтах, урок). Запуск: `Start-Process -Verb RunAs` (UAC-клик). - **Verify.** `/remont-kondicionerov/{zapravka,diagnostika}-kondicionera` → 200, компонент `ConditionerPrice` рендерит новые цены (4790/3290/6190/3990/7890/4840), старые (4400/2900/5800/7500/4450) ушли. Bare `/remont-kondicionerov` 301→`/remont/remont-kondicionerov` (CMS, без компонента) — verify по sub-страницам.
- **Smoke GREEN**: `tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on .snolla.com/admin/account/login` → 200, реальная MoreThenCms-логинформа (`<title>SNOLLA</title>`, `<form action="/admin/login">`).
- Catch-all routing: `appSettings primaryDomain=snolla.com primaryAlias=on` (default-site=on.snolla.com "Internal Site"), siteId из Host-header (нет siteId в appSettings).
- RUVDS SSH: plink `-hostkey SHA256:r/vSKU5WzH4B8T7RiXyXlg0D8XZ9hlBxzmdrPzuPWzE`, креды `pass show ruvds-iis/full-env` (password auth, RUVDS_IIS_PASS). PS-команды через `-EncodedCommand` (UTF-16LE base64) — stdin-`powershell -Command -` виснет.
- hosts: marker `# snolla-local-admin``127.0.0.1 <6 aliases>.snolla.com`. FW `block-inbound-80-snolla-local`.
### MinIO upload-acceptance (ручной follow-up оператора) ### Инцидент по ходу (разрешён)
Логин в админку → залить тест-ассет → проверить объект в `minio.kzntsv.site` bucket=assets. S3-провайдер был admin-self-test green на RUVDS, Web.config 1:1 → должно работать. Креды админа см. open Q ниже. Первый push 3.33GB с VPN → VPN-IP бан у провайдера хоста → `:443` к хосту TLS-fail с операторского IP (сайт для остальных работал). Остановил push, оператор убрал VPN, **resume-push** добил остаток малым egress → без повторного бана. **Урок: единичный large-push тоже триггерит бан** (не только retry-storm). Memory `stostayer-push-ban-single-large-push`.
## Recent commits ### ОСТАЁТСЯ (follow-up)
- c196964d feat(snolla-local-admin): RESTORE DONE — local .NET admin live (6 /admin URLs) - **🔴 ESM-ретрофит НЕ закоммичен/НЕ запушен в stostayer.new master.** stostayer.new-сессия сказала «коммить и пушь ты» (у неё изменений нет локально, ретрофит делался в working-tree .admin-сессии на этой воркстейшн). Commit-message одобрен stostayer.new. **Оператор НЕ дал «го» на пуш в эту сессию** (Rule 4 — пуш в git-master = outward). → Ретрофит лежит **незакоммиченным в `C:/Users/vitya/projects/stostayer.new` working-tree** (5 файлов + `package.json` snolla `^0.7.6` + `yarn.lock` + `.dockerignore`; `.yarnrc.yml` восстановлен в оригинал — чист; `.tasks/*` не трогать — ими stostayer.new управляет). **Следующая сессия: получить «го» оператора → `git add` ретрофит-файлов → commit (message ниже) → `git push origin master`.** НЕ делать `git checkout`/`stash`/`clean` в stostayer.new до коммита — потеряешь ретрофит. Если потерян — реконструируем из образа 0.3.20 (client registry) + ранбука + memory `stostayer-web-esm-retrofit-snolla-076`.
- d4278e79 meta(handoff): write NEXT_SESSION — snolla local admin + on-snolla design - Commit-message (одобрен stostayer.new): `fix(web): ESM-deps retrofit — require()→dynamic import(), pin snolla ^0.7.6` + body (см. письмо `stostayer.new/.claude-inbox/2026-07-21T14-28-00Z-admin.md`).
- b1b0092c design(snolla): local .NET admin restore + on.snolla.com VDS migration - После пуша stostayer.new тянет master + verify `packages/web` собирается.
- **✅ snolla yank 0.7.5 — DONE.** snolla-сессия сняла `@snollajs/snolla@0.7.5` с verdaccio (`npm unpublish --force`), verify: 0.7.x = только 0.7.6, `latest`=0.42.1 не сбит, exact-0.7.5 консьюмеров не было. Линия 0.7.x полностью закрыта.
- **Операторский IP 46.151.25.64** может ещё флапать по :443 к stostayer-хосту (бан egress-IP с инцидента) — для оператора; для посетителей сайта ограничений нет. Проходит само.
- **Побочная нота (не чинил):** vds-ops `ops.docker.logs` на контейнере `verdaccio` висит 300с (docker-socket-proxy + тяжёлый stdout) — snolla-сессия не смогла взять verdaccio-логи через MCP. Если понадобится独立-verify verdaccio-логов — чинить vds-ops (таймаут/стрим) или грепнуть лог-файл на хосте.
### Wiki / memory обновлено
- Ранбук `.wiki/concepts/stostayer-web-deploy-runbook.md`: БЛОКЕР→RESOLVED, push-секция (single-large-push gotcha), verify-секция (sub-страницы + smoke→prod-DB gotcha).
- Memory: `stostayer-web-esm-retrofit-snolla-076`, `stostayer-push-ban-single-large-push`, `stostayer-smoke-hits-prod-db`.
### Креды/доступы — не менялись
snolla-сессия: commit `63e5c60` (snolla repo, branch `fix/snolla-0.7.6-s3-get`), 0.7.6 в verdaccio. snolla-задача `snolla-074-get-bugfix-republish` закрыта (commit `46fe4040` в victor/snolla .tasks).
## Open треки ## Open треки
| Трек | Готовность | Entry-point | | Трек | Готовность | Entry-point |
|---|---|---| |---|---|---|
| **on-snolla-vds-migration (Task B)** | ⚪ ready, **unblocked** (Task A done) | `.tasks/on-snolla-vds-migration.md`; spec §Task B. Шаг 1: реконструкция Liquid из боевого `on.snolla.com` + локального админа. Нужны: repo-name, admin-креды. | | stostayer.new закоммитить ESM-ретрофит | ⚪ за stostayer.new | working-tree stostayer.new (5 файлов + lockfile + .dockerignore) |
| vehicles-loader 0.4.1 runtime verify | 🔴 active — просрочено с 07-14 | SSH `new.stostayer.ru:20435`, проверить importRun. Откат: tag 0.4.0. | | snolla yank 0.7.5 | ⚪ за snolla (го дано) | snolla выполнит `npm unpublish @snollajs/snolla@0.7.5 --force` |
| books-vds → vds-kzntsv консолидация | оценено, не лезет по RAM/disk | Решение за user. | | books-vds → vds-kzntsv консолидация | оценено, не лезет по RAM/disk | За user. |
| MinIO upload-acceptance on.snolla | ⚪ follow-up оператора | из prev handoff |
## Спроси user'а ## Спроси user'а
- **Push?** `c196964d` (Task A done) на origin/master? (Rule 4 — "пушни и продолжай" было разовое для design-коммита, не standing auto-push). - **«го» на коммит+пуш ESM-ретрофита в `victor/stostayer.new` master** (незакоммичен в working-tree, stostayer.new ждёт пуш чтобы подтянуть+verify). Без него следующий legacy-web деплой упрётся в ту же ESM-стену.
- **Task B repo name** — `victor/on-snolla`? `victor/on.snolla.com`?
- **Admin-креды** для локального админа (чтобыbrowse контент-дерево on.snolla.com через /admin) — есть в `pass`? Или в DB `MoreThenCms.dbo.Accounts`? (Логин-форма живёт на `/admin/account/login`.)
- **Task B стартовать сейчас** или pause? (Реконструкция = итеративная dev-работа: Liquid-шаблоны из HTML + DB-контент, build на VDS, cutover.)
## Не делать (preemptive guards) ## Не делать
- **НЕ пушить без добла** (Rule 4). - **НЕ пушить упавший push повторно с того же egress** (VPN-ban).
- **НЕ SSH-action на RUVDS без добла** (read-only verify уже сделан; дальше — только если нужно). - **НЕ коммитить в stostayer.new master** без координации с stostayer.new-сессией (их репо, их ESM-стратегия) — я оставил ретрофит в working-tree, они коммитят.
- **НЕ ставить URL Rewrite rule** локальному админу (нет traefik → `:port` leak). - snolla 0.7.5 yank — за snolla-сессией (го дано, но она исполняет).
- **НЕ деплоить vehicles-loader** без rateDiff; **НЕ retry-push** в docker.stostayer.ru; **НЕ netplan** на vds-kzntsv; **НЕ сжимать** books на .94.
- **hosts override активен** — `on.snolla.com` → 127.0.0.1 теперь (боевой on.snolla.com доступен только через `--resolve ...:443:80.64.31.36` или off-hosts).
## Memory updates за сессию
- (candidate) **PS5.1 BOM-less .ps1 ломается на UTF-8 Cyrillic/em-dash** — cp1251 0x94=`"` байт ломает string-терминаторы; BOM-less PS-скрипты = ASCII-only.
- (candidate) **SNOLLA admin addressing = `<alias>.snolla.com/admin`** catch-all IIS, siteId из dbo.Sites по alias (primaryAlias `on` = default). DB user `snolla` работает на MoreThenCms+stostayer catalogs.
- Обновить entity `windows-recovery-host.md`: IIS-сайт `snolla` восстановлен 2026-07-20 (локальный админ-хаб, не прод — прод на RUVDS).

View File

@@ -54,7 +54,17 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci
## 🟢 [snolla-local-admin-restore] — closed 2026-07-20 — локальный .NET-админ (catch-all IIS `snolla`) поднят с RUVDS, `/admin` для тиража+on.snolla.com. Per-task: `snolla-local-admin-restore.md`. Selective tar-copy ~100MB (НЕ 8.76GB, исключил stale App_Data assets/galleries/themes). Web.config уже на mssql.kzntsv.site+MinIO (repoint не нужен, ключи реальные). Elevated setup: `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1`. Smoke GREEN: 6 адресов `/admin/account/login` → 200 (tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on), реальная MoreThenCms-логинформа. unblocks `on-snolla-vds-migration`. ## 🟢 [snolla-local-admin-restore] — closed 2026-07-20 — локальный .NET-админ (catch-all IIS `snolla`) поднят с RUVDS, `/admin` для тиража+on.snolla.com. Per-task: `snolla-local-admin-restore.md`. Selective tar-copy ~100MB (НЕ 8.76GB, исключил stale App_Data assets/galleries/themes). Web.config уже на mssql.kzntsv.site+MinIO (repoint не нужен, ключи реальные). Elevated setup: `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1`. Smoke GREEN: 6 адресов `/admin/account/login` → 200 (tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on), реальная MoreThenCms-логинформа. unblocks `on-snolla-vds-migration`.
## [on-snolla-vds-migration] — on.snolla.com (siteId B9ECDB50…) с RUVDS IIS → VDS Node snolla-app 0.42.1, реконструкция шаблонов из боевого+админки. Per-task: `on-snolla-vds-migration.md`. Design: `../.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md` §Task B. ⚪ ready, blocked by `snolla-local-admin-restore`. ## 🟢 [on-snolla-vds-migration] — on.snolla.com (siteId B9ECDB50…) с RUVDS IIS → VDS Node snolla-app 0.42.1, реконструкция шаблонов из боевого+админки. Per-task: `on-snolla-vds-migration.md`. Design: `../.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md` §Task B. ⚪ ready, blocked by `snolla-local-admin-restore`.
## As-built (2026-07-20)
- Repo `victor/on.snolla.com` pushed (git.kzntsv.site). Structure-clone of tandemmebel.ru.
- Image `registry.kzntsv.site/on-snolla:473923e494db` (+:latest), built on VDS.
- Portainer stack Id 22, `mem_limit 512m`, 8 env secrets, rule `Host(on.snolla.com)`, LE cert.
- `on.snolla.com` DNS reg.ru A 80.64.31.36→89.253.255.94 (operator flip). RUVDS IIS untouched (rollback = revert DNS).
- Reconstruction: layout.liquid = bootstraptor landing byte-identical prod; form "Join us" baked (POST / → forms middleware); StaticPages /yandex + /4a052808276c; /c = 404 dead-link (parity).
- robotsTxt fix in index.js: on.snolla.com has no Domains row → snolla robotsTxt crashes on undefined app.locals.domain; synthesized domain drop from site.robotsTxt.
- Completeness-gate: sitemap 3/3 parity, homepage + /yandex byte-identical, 0 regressions.
<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-20T10:17:07.799Z / note: DONE 2026-07-20 — on.snolla.com live on VDS (stack 22, image on-snolla:473923e494db, snolla 0.42.1). LE cert issued (CN=on.snolla.com, YR1, until Oct 18). Homepage byte-identical prod-HTML. Sitemap 3/3 parity {/,/c,/yandex}. Live-smoke GREEN. -->
## 🟢 [kupimknigi-deploy-snolla-0-42-0] — VDS-staging образ kupimknigi.spb.ru на snolla@0.42.0 (одностраничник, финал тиража). Per-task: `kupimknigi-deploy-snolla-0-42-0.md`. ## 🟢 [kupimknigi-deploy-snolla-0-42-0] — VDS-staging образ kupimknigi.spb.ru на snolla@0.42.0 (одностраничник, финал тиража). Per-task: `kupimknigi-deploy-snolla-0-42-0.md`.
@@ -1582,13 +1592,14 @@ Cutover DNS уже сделан (07-02) — тут только смена об
--- ---
## 🔴 [vehicles-loader-redeploy-per-group-rate-diff] — Пересобрать и задеплоить @stostayer/vehicles-loader на прод клиента с новым кодом per-group rate diff. Код уже на origin/master (запушено 85ca936, 5 коммитов b0e7b58..85ca936). Изменение: rate-группы с неизменённым мультимножеством {standardHours,notes} теперь skip'аются вместо слепого DELETE+INSERT — объём записей в БД 51 203 → 1 963 rate-rows/день (96%), счётчик replaced=реальные записи, skipped=пропущенные группы. Read-consistency сохранена (изменившиеся группы — та же короткая транзакция). Write-примитив replaceGroup не менялся. Валидировано двумя dry-run против prod (0 ошибок, SG skipped=41089/replaced=1555, SM skipped=5131/replaced=408) + 36 unit-тестов. ## 🟢 [vehicles-loader-redeploy-per-group-rate-diff] — Пересобрать и задеплоить @stostayer/vehicles-loader на прод клиента с новым кодом per-group rate diff. Код уже на origin/master (запушено 85ca936, 5 коммитов b0e7b58..85ca936). Изменение: rate-группы с неизменённым мультимножеством {standardHours,notes} теперь skip'аются вместо слепого DELETE+INSERT — объём записей в БД 51 203 → 1 963 rate-rows/день (96%), счётчик replaced=реальные записи, skipped=пропущенные группы. Read-consistency сохранена (изменившиеся группы — та же короткая транзакция). Write-примитив replaceGroup не менялся. Валидировано двумя dry-run против prod (0 ошибок, SG skipped=41089/replaced=1555, SM skipped=5131/replaced=408) + 36 unit-тестов.
**Status:** active **Status:** done
**Where I stopped:** DEPLOYED + image-verified. Built docker.stostayer.ru/vehicles-loader:0.4.1 from master@b86ed40 (incl 85ca936 rate-diff). Pushed (one shot, digest sha256:228f2b24). Host: pulled + tagged :latest (image c3c4a83b15bd). New code confirmed in image: rateDiff.js(rateSetsEqual×2), replaceGroup.js(replaceRatesForGroups×2). Old tags 0.4.0/0.3.0 retained for rollback. Timer armed, next run Tue 2026-07-14 06:21:38 MSK. PENDING: runtime verify at that run. **Where I stopped:** runtime verify GREEN per operator 2026-07-20 — 0.4.1 rate-diff confirmed on prod.
**Next action:** After 07-14 06:21 MSK run: check new importRun status=ok + counts.serviceGenerationRate={replaced:<сотни>,skipped:<~41k>}, serviceModificationRate={replaced:<сотни>,skipped:<~5k>}, duration < ~93 мин. If replaced still ~44k/6.7k → wrong image (but code-verify already GREEN). Rollback: docker tag 0.4.0 vehicles-loader:latest. **Next action:** (none — kept until merged)
**Branch:** n/a **Branch:** n/a
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-07-13T14:08:16.228Z --> <!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-07-13T14:08:16.228Z -->
<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-20T08:00:52.478Z / note: runtime verify GREEN per operator 2026-07-20 — 0.4.1 rate-diff confirmed on prod. -->
--- ---

View File

@@ -17,14 +17,18 @@
- [ ] **7. Cutover** — verify авторит. NS reg.ru→89.253.255.94 → traefik Host-rule → LE-серт → live-smoke. По `tandemmebel-vds-deploy-runbook`. - [ ] **7. Cutover** — verify авторит. NS reg.ru→89.253.255.94 → traefik Host-rule → LE-серт → live-smoke. По `tandemmebel-vds-deploy-runbook`.
## Status ## Status
⚪ ready (blocked by [[snolla-local-admin-restore]]). Branch: master. ⚪ ready (unblocked [[snolla-local-admin-restore]] DONE). **Отложен в след. сессию.** Branch: master.
## Depends on / blocks ## Depends on / blocks
- Blocked by: [[snolla-local-admin-restore]] (админ для контент-инспекции). - ~~Blocked by: [[snolla-local-admin-restore]]~~ — DONE (локальный админ жив, 6 `/admin` URL).
## Decisions (locked 2026-07-20)
- **Repo:** `victor/on.snolla.com` (на git.kzntsv.site), структура-калька `victor/tandemmebel.ru`.
- **Admin-креды:** в БД `MoreThenCms.dbo.Accounts` (читать SQL-user'ом `snolla` / SA `pass mssql-vds/sa-password`). Логин-форма `/admin/account/login`.
- **Контент-инспекция:** hosts→127.0.0.1 активен (Task A), `http://on.snolla.com/` рендерит локальный IIS из той же MoreThenCms DB → контент == прод; `/admin` — контент-дерево.
## Open Q ## Open Q
- Имя репо (`victor/on-snolla`? `victor/on.snolla.com`?). - Сложность посадочной (объём шаблонов) — узнается при реконструкции (Title="Internal Site", culture `en` — вероятно простой лендинг).
- Сложность посадочной (объём шаблонов) — узнается при реконструкции.
## Rollback ## Rollback
Образ предыдущего тега в registry / revert DNS reg.ru→80.64.31.36 (RUVDS IIS жив пока DNS не флипнут). Образ предыдущего тега в registry / revert DNS reg.ru→80.64.31.36 (RUVDS IIS жив пока DNS не флипнут).

View File

@@ -0,0 +1,96 @@
---
title: on.snolla.com snolla-app — VDS deploy runbook (cutover 2026-07-20)
type: concept
tags: [on-snolla, snolla, vds, deploy, docker, portainer, traefik, runbook, migration, landing]
related: [[../entities/vds-kzntsv], [../entities/ruvds-iis-host], [tandemmebel-vds-deploy-runbook], [snolla-live-prod-inplace-image-bump], [portainer-stack-management-vds], [snolla-local-admin-and-on-snolla-migration-design]]
updated: 2026-07-20
---
# on.snolla.com → VDS deploy runbook
Вынос посадочной `on.snolla.com` (snolla-app, `@snollajs/snolla` **0.42.1**, server-side Liquid) с
[[../entities/ruvds-iis-host]] (catch-all CMS, 80.64.31.36) в docker-контейнер на [[../entities/vds-kzntsv]]
(89.253.255.94), за traefik. Паттерн = [[tandemmebel-vds-deploy-runbook]]. Отличие: **лендинг** (1 Page `/`
+ 2 StaticPages), БЕЗ блога/каталога/e-commerce → reconstruction = layout.liquid byte-identical prod-HTML.
Culture `en`. Спека: [[snolla-local-admin-and-on-snolla-migration-design]] §Task B.
## Артефакты
- **Код:** `victor/on.snolla.com` @ `473923e494db` (apps/web, snolla 0.42.1). Структура-калька tandemmebel.ru.
`deploy/Dockerfile` (multi-stage node:22-slim, `yarn install --immutable`, non-root, healthcheck `/robots.txt`).
- **Образ:** `registry.kzntsv.site/on-snolla:473923e494db` (+`:latest`). Имя **`on-snolla`** (= стек/контейнер).
Собран НА VDS (обход traefik-499).
- **Стек Portainer:** `on-snolla` (**Id 22**, endpoint 1). Source-of-truth compose: `admin/host-stacks/vds-kzntsv/on-snolla.compose.yml`.
- **siteId:** `B9ECDB50-2B93-4CE5-B238-A9918B3F9CF7` (non-secret, в `production.json`).
- **siteUrl:** `https://on.snolla.com``production.json`).
- **Runtime env (8 секретов):** `DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD`
— Portainer stack env (переиспользованы verbatim из tandemmebel стека 20 — общая snolla-инфра). НЕ в образе, НЕ в git.
- **mem_limit:** `512m`.
## Site model (MoreThenCms DB, siteId B9ECDB50)
- 1 Page `/` (template `content_page`, `customDefaultLayout=layout`). **Content-поля пустые** → весь
bootstraptor-лендинг baked в `views/layout.liquid` (byte-identical prod). `{{ item.content }}` (пуст) wired.
- Form "Join us" (Path `/`, FormTemplate `join_us`: first_name/last_name/email/website/description/captcha)
baked в `layout.liquid`; POST `/` → snolla `forms` middleware → `form_ajax_response.liquid` (AJAX replace).
Legacy reCAPTCHA v1 в разметке мертва (Google shut down v1) — parity с продом, НЕ чиним.
- StaticPages: `/yandex_7a510ac1f8311fca.html` (verification, IncludeInSitemap=true), `/4a052808276c.html` (not in sitemap).
- `/c`**404** (stale dead-link в .NET sitemap; snolla-app sitemap тоже его несёт → parity `/`+`/c`+`/yandex`).
- Theme assets `/themes/c406a987ebe14244b0eefe7b8f959a6f/{css,js,images}/*` — из MinIO bucket `themes` (snolla
`themeFiles` middleware; пути literal в `layout.liquid`).
- `/admin`**публичного `on.snolla.com/admin` не существует** (никогда не было: RUVDS catch-all IIS не
маршрутизировал /admin на публичном on.snolla.com — cert на 80.64.31.36:443 SNI on.snolla.com = чужой
`CN=kupimknigi.spb.ru`, /admin → 404). Админка — только **локальный catch-all IIS `snolla`** (Task A,
воркстейшн). Site alias переименован `on``internal` (2026-07-20), доступ = `http://internal.snolla.com/admin`
через hosts override → 127.0.0.1 → 302→login. **Web.config `primaryAlias`** тоже обновлён `on``internal`
(default-site resolution; иначе catch-all NRE-500 на ВСЕХ запросах — primaryAlias ссылается на алиас,
переименование сайта без обновления primaryAlias ломает весь catch-all). НЕ часть snolla-app.
## Cutover 2026-07-20 (первый вынос на VDS — DNS-gated)
1. **Build на VDS** (archive `473923e494db``~/build/on-snolla` → `docker build -f deploy/Dockerfile
--build-arg VERDACCIO_TOKEN -t .../on-snolla:473923e494db -t .../on-snolla:latest . && push`). Guard:
`config/default.json` ABSENT в архиве (.dockerignore), пин snolla = 0.42.1.
2. **Throwaway-staging :5077** из собранного env (8 секретов inline, т.к. нового live-контейнера ещё нет —
секреты общие с тиражом). `docker run -d --name on-snolla-staging --env-file .staging.env -p 127.0.0.1:5077:5000`.
Healthy. robots/`/`/sitemap 200.
3. **Completeness-gate С VDS** — sitemapindex разворот. **NEW==PROD locs: 3 = 3 IDENTICAL** (`/`, `/c`, `/yandex_…`).
Self-consistency: `/`→200, `/yandex`→200, `/c`→404 (dead-link, parity с прод-оракулом — prod /c тоже 404).
**Byte-parity:** homepage + /yandex **byte-identical** staging vs prod (RUVDS, via `--resolve :443:80.64.31.36`).
GREEN = 0 регрессий.
4. **Portainer стек 22** создан со **staging-Host** `on-snolla.vds.kzntsv.site` (wildcard → VDS), LE=default cert.
Healthy за traefik. (`create-stack.mjs` node-скрипт, Portainer JWT auth, env array 8/8, `mem_limit 512m`.)
5. **DNS flip** — operator: reg.ru `on.snolla.com` A `80.64.31.36` → `89.253.255.94`.
6. **Verify авторит. NS** — `nslookup on.snolla.com ns1/ns2.reg.ru` = `89.253.255.94` (BEFORE traefik rule swap —
memory `operator-dns-flip-verify-domain-before-cutover`: wrong domain burns LE rate-limit). Public resolvers propagated.
7. **PUT стека 22** — rule `Host(on-snolla.vds.kzntsv.site)` → `Host(on.snolla.com)`, env-preserving (8/8),
`prune:false, pullImage:false`. node `cutover-put.mjs` (Portainer JWT; **re-auth right before PUT** —
jwt из GET-фазы успел протухнуть к моменту PUT, первый PUT дал 401). PUT 200.
8. **Poll** — контейнер healthy, **LE-серт issued on first hit**: CN=on.snolla.com, issuer YR1, until 2026-10-18.
9. **Live-smoke С VDS + external** — `https://on.snolla.com/{robots.txt,/,/sitemap.xml,/yandex_…}` = 200,
`/c` = 404 parity. TLS verify `ssl_verify_result=0` (trusted LE chain). Homepage **byte-identical** prod-HTML.
## Гочи (специфичные)
- **snolla `robotsTxt` middleware падает на undefined `app.locals.domain`** — on.snolla.com НЕ имеет row в
MoreThenCms `Domains` (served by primaryDomain без per-domain row). `robotsTxt.js` читает
`res.app.locals.domain.robotsTxt` без null-guard → GET /robots.txt (и Dockerfile healthcheck) крашится 500.
**Фикс в `apps/web/index.js`**: после `initApp`, если `!app.locals.domain`, synthesized `{robotsTxt: site.robotsTxt}`
(only robotsTxt.js reads locals.domain; theme resolution uses site.activeThemeId — safe). Без мутации shared DB.
**Альтернатива** (не применена): добавить Domains row (Name=on.snolla.com, SiteId=B9ECDB50, Public=1, ThemeId=null)
— но это мутация shared prod MoreThenCms; предпочтён app-code fallback.
- **Form "Join us" не имеет Liquid form-tag** — snolla не предоставляет form-rendering tag. Форма HTML baked в
`layout.liquid` (поля по FormTemplate `join_us`); POST обрабатывает `forms` middleware (мэтч по Path `/`).
`form_ajax_response.liquid` — AJAX-ответ (`$form.replaceWith(response)`).
- **`/c` в sitemap — dead-link** — snolla SitemapService эмбитит `/c` из sections-source, но страница 404.
Prod-оракул идентичен (404) → benign parity, не регрессия.
- **hosts override на воркстейшне** — Task A оставил `127.0.0.1 on.snolla.com` в hosts (локальный админ catch-all).
External verify прод-on.snolla.com с воркстейшна = только через `curl --resolve on.snolla.com:443:89.253.255.94`
(иначе попадёшь в локальный IIS). См. memory `workstation-lan-dns-serves-local-cms-copy`.
- **Portainer JWT short-lived** — GET stack + GET file + PUT в одном скрипте: jwt из GET-фазы может протухнуть
к PUT. Re-auth (`POST /api/auth`) прямо перед PUT. API-key `ptr_*` даёт 401 (см. [[portainer-stack-management-vds]]).
- **Образных rollback-тегов пока нет** — первый deploy, `473923e494db` = `:latest`. ~~Rollback = revert DNS (RUVDS жив).~~ RUVDS DECOMM 2026-07-21 — rollback только образ-тегом.
## Rollback
- ~~**DNS (предпочт, мгновенный):** revert reg.ru `on.snolla.com` A → `80.64.31.36` (RUVDS IIS жив, не тронут).~~ **НЕАКТУАЛЬНО с 2026-07-21** — [[../entities/ruvds-iis-host]] DECOMM (погашен у провайдера). RUVDS более не rollback-target.
- **Образный (рабочий):** PUT стека 22 назад на предыдущий тег (rollback-теги в registry). `473923e494db` = `:latest` — первый deploy, предыдущего тега нет; с этого дня ведём rollback-теги на каждый bump.
## Связанное
- on.snolla.com admin (`/admin`) остаётся на RUVDS .NET — catch-all IIS `snolla` (см. [[snolla-local-admin-and-on-snolla-migration-design]] §Task A).
- Тираж snolla на VDS: labtools.ru(17), emspb(18), labtools.pro(19), tandemmebel(20), kupimknigi(21), **on-snolla(22)**.

View File

@@ -0,0 +1,71 @@
---
title: stostayer .NET admin — MinIO S3 providers config (creds/endpoint/region)
type: concept
tags: [stostayer, morethencms, iis, admin, minio, s3, config, windows]
related: [[../entities/ruvds-iis-host]], [[galleries-storage-class-local-not-s3]], [[snolla-admin-appdata-acl-500-after-scp-migration]], [[minio-imgproxy-on-vds]], [[snolla-local-admin-and-on-snolla-migration-design]]
updated: 2026-07-22
---
# stostayer .NET admin — MinIO S3 providers
Локальная MoreThenCms-админка stostayer (`C:\sites\stostayer\Web.config`, IIS :8090) переведена с битых S3-настроек на рабочий stostayer MinIO. После правки админка и боевой `stostayer-web` (0.3.20, Node snolla-app) читают одно хранилище — ручное зеркалирование в `App_Data` больше не нужно.
## Контекст
- Боевой `stostayer-web:0.3.20` (legacy @stostayer/web, Nuxt2) читает ассеты/галереи из **stostayer MinIO**, не из локальной ФС. Поток отдачи: картинки галерей/asset → 302 на `imgproxy.stostayer.ru/imgproxy2` → imgproxy тянет из MinIO → webp; прочие ассеты — стримом из MinIO (см. [[../entities/ruvds-iis-host]] ранбук).
- Локальная админка = тот же движок MoreThenCms .NET 4.8, conn → `www.stostayer.ru,1433` (DB `stostayer`, user `stayer_site`). S3 drop-in `MoreThenCms.FileStorage.S3` (+ `AWSSDK.Core`/`AWSSDK.S3`) уже лежал в `bin/` — провайдеры в `Web.config` были вставлены, но не работали.
## Креды stostayer MinIO — НЕ в pass
Лежат в репо-конфиге `~/projects/stostayer.new/packages/web/config/default.json` → секция `s3`:
| Параметр | Значение |
|---|---|
| accessKeyId | `stayer_minio` (bucket-user) |
| secretAccessKey | `cXY>AVoWBv#_V-SZn6Vu6Tzx&!M[%UOod` |
| endpoint | `https://minio-api.stostayer.ru` |
| region | `us-west-1` (см. ниже — НЕ `local`) |
| pathStyle / ssl | `true` / `true` |
imgproxy: `https://imgproxy.stostayer.ru`, basePath `/imgproxy2`, key+salt — там же в `default.json``imgproxy`.
## Три бага в исходном Web.config (почему "смотрит на локальное")
1. **Чужие креды.** `accessKey=AKIAJ2YJP72W6ZHCRE6Q` + `secretKey=7o0Q4NjE5GL…` — это **books-vds/snolla MinIO root-ключ** (см. [[minio-imgproxy-on-vds]]). На stostayer MinIO не зарегистрирован → `The Access Key Id you provided does not exist in our records`. Видимо, конфиг копипастом из snolla catch-all admin ([[snolla-local-admin-and-on-snolla-migration-design]], тот юзает books-vds `minio.kzntsv.site`), без замены ключей на stostayer-свои.
2. **Не API-порт.** `serviceURL=https://minio.stostayer.ru` → MinIO отвечает `S3 API Requests must be made to API port`. Прод юзает `https://minio-api.stostayer.ru`. Оба хоста резолвятся в один IP `2a03:6f01:1:2::c66b`, но `minio.stostayer.ru` — не S3 API-эндпоинт.
3. **Регион.** `authenticationRegion="local"` → SigV4-подпись регионом не совпала → `AmazonS3Exception: The authorization header is malformed; the region is wrong; expecting 'us-west-1'` (HTTP 400) на `GalleriesStorage.EnsureKeyPrefixExists``S3DirectoryInfo.ExistsWithBucketCheck` (провайдер галерей падал на ините, в трассе `ToolsController.ImagePreview``CreatePreview`). stostayer MinIO настроен на регион `us-west-1`. Прод `default.json` пишет `region:"local"`, но то Node aws-sdk (galleries через imgproxy мимо app-S3; для .NET AWSSDK регион обязан быть `us-west-1`).
## Fix (2026-07-22)
9 S3-блоков в `fileStorageClients` (galleries/assets/images/scripts/stylesheets/imageCache/watermarks + inert-настройки contentCache/uploadCache) — 3 глобальные замены в `Web.config`:
```
accessKey: AKIAJ2YJP72W6ZHCRE6Q → stayer_minio
secretKey: 7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S → cXY&gt;AVoWBv#_V-SZn6Vu6Tzx&amp;!M[%UOod (XML-escape: > → &gt;, & → &amp;)
serviceURL: https://minio.stostayer.ru → https://minio-api.stostayer.ru
authenticationRegion: local → us-west-1
```
Бэкап: `C:\sites\stostayer\Web.config.bak-2026-07-22`. IIS авто-recycle по Web.config-изменению. `contentCache`/`uploadCache` остались `storageType="Local"` (inert S3-настройки поправились, storageType не трогал — кэши локально, по рецепту [[snolla-local-admin-and-on-snolla-migration-design]] шаг 4).
`bucketName` уже был корректен и не менялся: galleries→`galleries`, assets→`assets`, images/scripts/stylesheets/imageCache/watermarks→`themes`. МинIO-бакеты (mc ListBuckets): `assets, galleries, themes, contentcache, imagescache, maxmind, searchindexes, seo, sessions, uploadcache`. Бакет `assets/9cf0a8e52cf144619fd290606a146d35/` несёт реальные файлы (0001.png…); `ownerId` = siteId `9cf0a8e5-2cf1-4461-9fd2-90606a146d35` без дефисов lowercased.
## Verify
- `mc alias set stn_api https://minio-api.stostayer.ru stayer_minio "<secret>"``mc ls stn_api/` = 10 бакетов. ⚠ `MC_HOST_…` с URL-encoded секретом в mc-урле давал signature-mismatch — юзать `mc alias set` с явными аргументами.
- Админка: `:8090/admin/account/login` → 200, лог `C:\Logs\stostayer\log.YYYYMMDD.txt` чист от S3-ошибок после recycle.
- **Acceptance (реальный клиент, не curl — см. [[verify-on-real-client-not-own-curl-tests]]):** логин → Asset Manager / превью галереи — картинки грузятся из MinIO. Подтверждено оператором 2026-07-22.
## Гочи
- **Секрет с XML-specials.** В `Web.config` value-атрибуте `>` и `&` обязаны быть escaped (`&gt;`, `&amp;`) — иначе XML не парсится / .NET получает обрезанный секрет. `#`, `[`, `%`, `!` — безопасны в double-quoted XML-атрибуте.
- **`minio.stostayer.ru` ≠ API-порт.** Только `minio-api.stostayer.ru`.
- **Регион `us-west-1`, не `local`.** Для .NET AWSSDK админки — обязателен; прод Node `local` — отдельная история (imgproxy + env-override).
- **Креды не в pass** — единственный источник: `stostayer.new` config. Если репо уедет — креды потеряются; имеет смысл переложить в `pass` (отдельная hardening-таска).
- **Кэши Local** — `contentCache`/`uploadCache` намеренно Local (не S3), `imageCache` → S3/themes. Не унифицировано с snolla-admin (там все кэши Local) — оставлено как было.
## Связанные
- [[galleries-storage-class-local-not-s3]] — snolla-тираж: galleries/assets Local→S3 миграция, snolla catch-all admin на books-vds MinIO.
- [[snolla-admin-appdata-acl-500-after-scp-migration]] — split-brain админка(Local)↔боевой app(MinIO) на RUVDS; та же проблема «админка не переключена на S3».
- [[minio-imgproxy-on-vds]] — books-vds MinIO (root-ключ `AKIAJ2YJP72W6ZHCRE6Q`), который ошибочно попал в stostayer Web.config.

View File

@@ -47,6 +47,8 @@ docker push docker.stostayer.ru/stostayer-web:<tag>
``` ```
> ⚠️ **ОДИН push, без retry-циклов.** Инцидент 2026-06-17: retry-шторм (6 попыток, образ 3.29GB) на `docker.stostayer.ru` засветил egress нашего **VPN** → хостинг-провайдер забанил VPN-IP → ВЕСЬ HTTPS дом→хост (`:443`: сайт + registry + portainer) стал TLS-fail (`schannel: failed to receive handshake`), выглядело как «сайт лёг» (а TCP 443/SSH 20435 — открыты). Лечится сменой VPN. **На client-инфру: одна попытка, сбой → стоп и к человеку, не долбить.** (см. [[verify-on-real-client-not-own-curl-tests]]) > ⚠️ **ОДИН push, без retry-циклов.** Инцидент 2026-06-17: retry-шторм (6 попыток, образ 3.29GB) на `docker.stostayer.ru` засветил egress нашего **VPN** → хостинг-провайдер забанил VPN-IP → ВЕСЬ HTTPS дом→хост (`:443`: сайт + registry + portainer) стал TLS-fail (`schannel: failed to receive handshake`), выглядело как «сайт лёг» (а TCP 443/SSH 20435 — открыты). Лечится сменой VPN. **На client-инфру: одна попытка, сбой → стоп и к человеку, не долбить.** (см. [[verify-on-real-client-not-own-curl-tests]])
>
> ⚠️ **Инцидент 2026-07-21: единичный large-push ТОЖЕ триггерит бан** (не только retry-storm). Первый push `0.3.20` (3.33GB) с VPN → тот же VPN-IP бан → `:443` к хосту TLS-fail с операторского IP (сайт для остальных посетителей работал — Angie access-log это подтвердил; SSH:20435 оставался открыт). **Бан НЕ IP-специфичный для всех — для остальных сайт жив, режется только egress-IP источника push'а.** Митигация: (a) **resume-push после смены egress** — `docker push` resumable, уже залитые слои «Layer already exists» (без re-upload), добивается только остаток + manifest → egress мал → повторный бан не триггерится; (b) **build-on-host + push в localhost-registry** (registry-контейнер на хосте, `127.0.0.1` — вообще без внешнего egress) — самый чистый путь для крупных образов. **Не пытаться долбить упавший push повторно с того же egress.**
### 3. Передеплой стека — с ХОСТА, мимо Angie BA ### 3. Передеплой стека — с ХОСТА, мимо Angie BA
@@ -78,11 +80,25 @@ docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}' | grep stostayer-web
docker logs --tail 20 stostayer-web # чистый старт Nuxt, без ошибок/краш-лупа docker logs --tail 20 stostayer-web # чистый старт Nuxt, без ошибок/краш-лупа
``` ```
**Verify контента (напр. цен) — с хоста, прямым curl в nuxt-контейнер** (Angie даёт 403 на no-UA запрос с самого хоста; обход — `localhost:3000`, host-network):
```bash
# на хосте (sudo docker + host-network контейнер на :3000)
curl -s -H "Host: www.stostayer.ru" -A "Mozilla/5.0" http://localhost:3000/<page> -o /tmp/v.html
grep -c "<expected-text>" /tmp/v.html
```
**Гоча verify-URL:** bare `/remont-kondicionerov` 301-редиректит на `/remont/remont-kondicionerov` (DB-driven `oldPageRedirect`/unit-slug mapping) — это CMS-контент-страница БЕЗ nuxt-компонента. Компоненты `pages/<dir>/*.vue` реально live на своих nuxt-маршрутах (напр. `/remont-kondicionerov/{zapravka,diagnostika}-kondicionera`) — верифицируй по ним, не по bare-URL.
**Гоча локального smoke-контейнера:** `packages/web/config/default.json` содержит **прод-БД** (`www.stostayer.ru:3306` MariaDB + `:1433` MSSQL), и `www.stostayer.ru` резолвится в публичный IP хоста → smoke-контейнер без env-overrides **подключается к prod-БД клиента по интернету** (read-only SSR, без writes, но всё равно нежелательно). Для чистого локального smoke — переопределяй `data.sequelize.*.host` env'ом на localhost/заглушку, либо глуши контейнер сразу после `Server Listening`.
### Откат ### Откат
Тот же PUT с прежним тегом (`<NEWTAG>``0.3.18`), `pullImage:true`. 0.3.18 на хосте есть. RTO ~30-60с (пересоздание контейнера). Тот же PUT с прежним тегом (`<NEWTAG>``0.3.18`), `pullImage:true`. 0.3.18 на хосте есть. RTO ~30-60с (пересоздание контейнера).
## БЛОКЕР: легаси web НЕ пересобирается с текущего master (ESM-стена) ## БЛОКЕР РАЗРЕШЁН 2026-07-21 (retrofit + snolla 0.7.6)
> **Legacy web снова собирается+запускается с master.** Решение: ретрофит `packages/web` (`require()`→dynamic `import()` в 5 местах, 2 serverMiddleware → CJS) + фикс `@snollajs/snolla@0.7.6` (bare-`get`-баг, см. ниже). Образ `0.3.20` собран, запушен, передеплоен на прод stostayer.ru, verify зелёный. Ретрофит лежит в working-tree stostayer.new (незакоммичен на 2026-07-21) — **stostayer.new должен закоммитить** его в master, иначе следующий legacy-web деплой упрётся в ту же стену. Секция ниже оставлена как история.
## ⛔ БЛОКЕР (история, 2026-06-17): легаси web НЕ пересобирается с текущего master (ESM-стена)
> На 2026-06-17 фикс формы жалоб (`120bc07`, задача `stostayer-web-complaint-form-deploy`) **выкатить не удалось** — два ESM-барьера: > На 2026-06-17 фикс формы жалоб (`120bc07`, задача `stostayer-web-complaint-form-deploy`) **выкатить не удалось** — два ESM-барьера:

View File

@@ -1,15 +1,36 @@
--- ---
title: RUVDS IIS Host (Windows Server 2025 Core) — 80.64.31.36 title: RUVDS IIS Host (Windows Server 2025 Core) — 80.64.31.36 — DECOMM 2026-07-21
type: entity type: entity
tags: [hardware, vds, ruvds, windows, iis, cms, snolla, kupimknigi] tags: [hardware, vds, ruvds, windows, iis, cms, snolla, kupimknigi, decommissioned]
sources: [../sources/iis-migration-to-ruvds-2026-05-23.md, ../sources/ruvds-backup-daily-kreknin-2026-05-24.md] sources: [../sources/iis-migration-to-ruvds-2026-05-23.md, ../sources/ruvds-backup-daily-kreknin-2026-05-24.md]
updated: 2026-06-11 updated: 2026-07-21
--- ---
# RUVDS IIS Host # RUVDS IIS Host — ⛔ DECOMM 2026-07-21
> **Декоммишнен 2026-07-21.** Погашен у провайдера, забыт. Деньги экономим. Финальный offsite-снимок на kreknin верифицирован (см. § Decommission ниже). Rollback-target для всех мигрированных snolla-сайтов **более недоступен** — откат только образ-тегом в registry / DNS уже на VDS.
>
> Что ниже — историческая запись живого хоста (куплен 2026-05-23, DECOMM 2026-07-21, срок жизни ~2 месяца). Креды/доступ сохранены в `pass` ретроспективно, хост выключен.
Облачный Windows-VDS у [RUVDS](https://ruvds.com), DC Королёв. Куплен 2026-05-23 как destination для миграции IIS-хостинга с [[windows-recovery-host]] — closing SPOF домашней машины для prod CMS сайтов (snolla CMS multi-tenant). Это **отдельный VDS** от [[vds-kzntsv]] (Rusonyx Linux, инфра-стек): RUVDS = только IIS catch-all для CMS hostnames, Rusonyx VDS = gitea/verdaccio/registry/DB park. Облачный Windows-VDS у [RUVDS](https://ruvds.com), DC Королёв. Куплен 2026-05-23 как destination для миграции IIS-хостинга с [[windows-recovery-host]] — closing SPOF домашней машины для prod CMS сайтов (snolla CMS multi-tenant). Это **отдельный VDS** от [[vds-kzntsv]] (Rusonyx Linux, инфра-стек): RUVDS = только IIS catch-all для CMS hostnames, Rusonyx VDS = gitea/verdaccio/registry/DB park.
## Decommission (2026-07-21)
Сессия 2026-07-21: аудит «что осталось на RUVDS перед отказом от провайдера».
**Живого публично не осталось** — всё snolla-тираж уехало на VDS:
- Публичный DNS → RUVDS (`80.64.31.36`): **только `rimiz.ru`/`www.rimiz.ru`** — и тот труп (HTTP 404 на RUVDS; сломан с 2026-05-19 iis-migration cleanup, не чинен, не мигрирован).
- Всё остальное → VDS `89.253.255.94`: `on.snolla.com`, `snolla.com` apex, `kupimknigi.spb.ru`, `tandemmebel.ru`/www, `labtools.ru`/www, `labtools.pro`/www, `emspb.ru`/www, `pilorama98.ru`/www, `maljarka.tandemmebel.ru`, `ics-artmaterials`/`pilorama98`/`sestech`/`artmone`/`rimiz` `.snolla.com`. Smoke VDS 2026-07-21: 200 (кроме `snolla.com` apex — 404 TRAEFIK DEFAULT CERT, нет Host-правила; мёртв везде, отдельная мелочь, НЕ блокировала DECOMM).
- VDS snolla-стеки Portainer: labtools(17)/emspb(18)/labtools-pro(19)/tandemmebel(20)/kupimknigi(21)/on-snolla(22).
- Локальный .NET-админ (catch-all IIS `snolla` на воркстейшне, [[snolla-local-admin-and-on-snolla-migration-design]] §Task A) от RUVDS НЕ зависит — читает `mssql.kzntsv.site` + `minio.kzntsv.site` (оба на vds-kzntsv).
**Финальный offsite-снимок верифицирован 2026-07-21:**
- `RUVDS-Backup-Daily` последний прогон 2026-07-21 04:30:01, `LastTaskResult=0`, 0 missed. DONE 72 мин (лог `C:\ProgramData\backup\logs\2026-07-21.log`): IIS-config snapshot + 13 certs exported (non-exportable pvt-key warning benign) + rclone sync snolla/applicationHost/iis-backup/certs/ssh-config + retention keep 7.
- kreknin `/volume1/NetBackup/ruvds-iis/2026-07-21/` = **8.8G**, создан 05:37, полный (`sites/`+`iis-config/`+`iis-backup-webconfiguration/`+`certs/`+`ssh-config/`). 7 снимков в retention (2026-07-15…07-21).
- Динамика (.NET-админ не несёт) — MSSQL+MinIO на vds-kzntsv, бэкапятся отдельно (см. [[backup-inventory-2026-06]]). Сам .NET-админ = статичные бинарники MoreThenCms + S3 drop-in, восстанавливаемо из source + локальная копия на воркстейшне.
**Loose end — RESOLVED 2026-07-21:** `rimiz.ru`/`www.rimiz.ru` A-запись переправлена оператором на `89.253.255.94`. Авторит. NS ns1/ns2.reg.ru = `89.253.255.94` (verified); public resolvers ещё держат stale `80.64.31.36` по TTL 86400 — сбросится в сутки. На RUVDS больше НИ ОДИН публичный домен не смотрит.
## Hardware / tariff ## Hardware / tariff
- **Vendor:** RUVDS (https://ruvds.com) - **Vendor:** RUVDS (https://ruvds.com)

View File

@@ -49,6 +49,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
- [portainer-stack-management-vds](concepts/portainer-stack-management-vds.md) — Portainer-managed stacks на VDS — canonical pattern + migration script + stack-redeploy recipe (новый тег) + gotchas (вкл. PS 5.1 ISO-8859-1 коррапт кириллицы при API round-trip) - [portainer-stack-management-vds](concepts/portainer-stack-management-vds.md) — Portainer-managed stacks на VDS — canonical pattern + migration script + stack-redeploy recipe (новый тег) + gotchas (вкл. PS 5.1 ISO-8859-1 коррапт кириллицы при API round-trip)
- [snolla-live-prod-inplace-image-bump](concepts/snolla-live-prod-inplace-image-bump.md) — переиспользуемый рецепт обновления образа на ЖИВОМ snolla-стеке VDS in-place (build→throwaway-staging-acceptance С VDS→env-preserving Portainer PUT→live-smoke); вкл. `put-stack.js`; отработан на тираже 0.42.1 (4 сайта: labtools/emspb/labtools.pro 2026-07-05 + tandemmebel 2026-07-12) - [snolla-live-prod-inplace-image-bump](concepts/snolla-live-prod-inplace-image-bump.md) — переиспользуемый рецепт обновления образа на ЖИВОМ snolla-стеке VDS in-place (build→throwaway-staging-acceptance С VDS→env-preserving Portainer PUT→live-smoke); вкл. `put-stack.js`; отработан на тираже 0.42.1 (4 сайта: labtools/emspb/labtools.pro 2026-07-05 + tandemmebel 2026-07-12)
- [stostayer-web-deploy-runbook](concepts/stostayer-web-deploy-runbook.md) — деплой легаси web (`www.stostayer.ru`) на прод клиента: build→registry→Portainer-стек 16 через container-IP API с хоста; **БЛОКЕР: легаси не пересобрать с master (ESM-стена: top-level await @stostayer/data на build, ERR_REQUIRE_ESM @snollajs/snolla в рантайме)**; гоча — один push без retry (VPN-IP банится хостером) - [stostayer-web-deploy-runbook](concepts/stostayer-web-deploy-runbook.md) — деплой легаси web (`www.stostayer.ru`) на прод клиента: build→registry→Portainer-стек 16 через container-IP API с хоста; **БЛОКЕР: легаси не пересобрать с master (ESM-стена: top-level await @stostayer/data на build, ERR_REQUIRE_ESM @snollajs/snolla в рантайме)**; гоча — один push без retry (VPN-IP банится хостером)
- [stostayer-admin-minio-config](concepts/stostayer-admin-minio-config.md) — локальная .NET-админка stostayer (:8090) на stostayer MinIO: креды НЕ в pass (в stostayer.new config), endpoint `minio-api.stostayer.ru` (minio.stostayer.ru — не API-порт), region `us-west-1` (не local); fix 2026-07-22 — 3 замены в Web.config, админка=прод единое хранилище
- [proxy-debugging-test-the-real-client](concepts/proxy-debugging-test-the-real-client.md) — анти-паттерн: свои curl/standalone-тесты «работают», а боевой клиент пользователя нет; источник истины — реальный клиент - [proxy-debugging-test-the-real-client](concepts/proxy-debugging-test-the-real-client.md) — анти-паттерн: свои curl/standalone-тесты «работают», а боевой клиент пользователя нет; источник истины — реальный клиент
- [reality-pq-mldsa65-dest-incompatibility](concepts/reality-pq-mldsa65-dest-incompatibility.md) — REALITY+ML-DSA-65 (PQ) не работает с не-PQ dest (Akamai/intel шлёт HRR); fix = PQ-совместимый dest или выключить PQ; **caveat: снятие PQ ≠ рабочий Reality у GUI-клиентов** - [reality-pq-mldsa65-dest-incompatibility](concepts/reality-pq-mldsa65-dest-incompatibility.md) — REALITY+ML-DSA-65 (PQ) не работает с не-PQ dest (Akamai/intel шлёт HRR); fix = PQ-совместимый dest или выключить PQ; **caveat: снятие PQ ≠ рабочий Reality у GUI-клиентов**
- [recovery-architecture-snapshot](concepts/recovery-architecture-snapshot.md) — текущая recovery architecture (2026-05-19/21, attempt 2) - [recovery-architecture-snapshot](concepts/recovery-architecture-snapshot.md) — текущая recovery architecture (2026-05-19/21, attempt 2)

View File

@@ -2,6 +2,8 @@
Append-only log of wiki operations (ingests, promotions, lints, migrations). Append-only log of wiki operations (ingests, promotions, lints, migrations).
## [2026-07-22] fix | NEW concepts/stostayer-admin-minio-config.md — локальная .NET-админка stostayer (`C:\sites\stostayer\Web.config`, IIS :8090) переведена на рабочий stostayer MinIO. Исходно S3-провайдеры (`MoreThenCms.FileStorage.S3` в `bin/`) уже стояли, но с 3 багами: (1) чужие креды `AKIAJ2YJP72W6ZHCRE6Q` = books-vds/snolla root-ключ (копипаст из snolla catch-all admin, не заменён на stostayer-свои → "Access Key Id does not exist"); (2) `serviceURL=https://minio.stostayer.ru` — НЕ S3 API-порт ("S3 API Requests must be made to API port"), прод юзает `minio-api.stostayer.ru`; (3) `authenticationRegion="local"` → SigV4 регион-мismatch → `AmazonS3Exception: the region is wrong; expecting 'us-west-1'` (400) на `GalleriesStorage.EnsureKeyPrefixExists` (`ToolsController.ImagePreview` → `CreatePreview`). Креды stostayer MinIO **не в pass** — лежат в `~/projects/stostayer.new/packages/web/config/default.json` → `s3`: accessKey `stayer_minio`, endpoint `https://minio-api.stostayer.ru`, region `us-west-1`. Fix: 3 глобальные замены × 9 S3-блоков в Web.config (accessKey/secretKey[XML-escaped `>`/`&`]/serviceURL) + region `local`→`us-west-1`; бэкап `Web.config.bak-2026-07-22`; IIS recycle, лог чист. mc `alias set` + ListBuckets OK (10 бакетов: assets/galleries/themes/…); `assets/9cf0a8e52cf144619fd290606a146d35/` несёт реальные файлы. Acceptance оператором: картинки грузятся. Админка=прод единое хранилище, ручное зеркалирование в `App_Data` упразднено. +index.md concepts-строка. Cross-ref [[galleries-storage-class-local-not-s3]], [[snolla-admin-appdata-acl-500-after-scp-migration]], [[minio-imgproxy-on-vds]].
## [2026-07-14] ingest | sources/de-vds-3xui-setup-2026-07-14.md (new) + entities/de-vds-3xui.md (sources: привязан) — полноценный ingest DE-сессии: sources-хроника с борьбой против 3x-ui 3.5.0 (CSRF-403 на login → `CSRFMiddleware`/`X-CSRF-Token`/`GET {BP}csrf-token`; прямой DB-insert в `inbounds` не рендерит → client-tables `clients`/`client_inbounds`/`client_traffics` заполняются только `AddInbound` → panel API `POST {BP}panel/api/inbounds/add` с stringified settings/streamSettings/sniffing; wrapper `x-ui setting` не применяет креды → бинарник). Эталон JSON снят с рабочего nl-vds 32030. +index.md sources-строка. Real RF-client confirmed. ## [2026-07-14] ingest | sources/de-vds-3xui-setup-2026-07-14.md (new) + entities/de-vds-3xui.md (sources: привязан) — полноценный ingest DE-сессии: sources-хроника с борьбой против 3x-ui 3.5.0 (CSRF-403 на login → `CSRFMiddleware`/`X-CSRF-Token`/`GET {BP}csrf-token`; прямой DB-insert в `inbounds` не рендерит → client-tables `clients`/`client_inbounds`/`client_traffics` заполняются только `AddInbound` → panel API `POST {BP}panel/api/inbounds/add` с stringified settings/streamSettings/sniffing; wrapper `x-ui setting` не применяет креды → бинарник). Эталон JSON снят с рабочего nl-vds 32030. +index.md sources-строка. Real RF-client confirmed.
## [2026-07-14] decision | entities/de-vds-3xui.md — real-client из РФ подтверждён (user подключён через 32030, сессия идёт через узел). Plain VLESS = proven-working на обоих NL+DE. Задача [de-vds-3xui-setup] закрыта. Перекрестил `Проверено` блок ✅. ## [2026-07-14] decision | entities/de-vds-3xui.md — real-client из РФ подтверждён (user подключён через 32030, сессия идёт через узел). Plain VLESS = proven-working на обоих NL+DE. Задача [de-vds-3xui-setup] закрыта. Перекрестил `Проверено` блок ✅.

View File

@@ -0,0 +1,61 @@
# on-snolla — посадочная on.snolla.com (snolla-app, 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).
#
# Модель: монолит @snollajs/snolla (0.42.1) / core / liquid / data — читает БД-контент MoreThenCms
# (mssql.kzntsv.site) + server-side Liquid render. Ассеты из MinIO (minio.kzntsv.site). In-process sharp,
# delivery=serve-bytes, cache=local-variant. imgproxy в путях нет (как tandemmebel — сырой /imgproxy -> 404 норма).
# node-config: NODE_ENV=production -> default(dev,gitignored)+production.json+ENV-override.
# ЛЕНДИНГ (1 Page / + 2 StaticPages), БЕЗ блога/каталога/e-commerce.
#
# Образ собирается АДМИНОМ на VDS из victor/on.snolla.com/deploy/Dockerfile (build-arg VERDACCIO_TOKEN,
# @snollajs/* из verdaccio.kzntsv.site). Тег = git-sha репо on.snolla.com (текущий 473923e494db).
# Homepage render byte-identical прод-HTML (verified direct diff + completeness-gate 3/3 loc parity 2026-07-20).
#
# Домен: LIVE — on.snolla.com (cert LE HTTP-01, CN=on.snolla.com, issued 2026-07-20, until 2026-10-18).
# Cutover завершён 2026-07-20: DNS reg.ru on.snolla.com A 80.64.31.36 -> 89.253.255.94 (operator flip,
# авторит. NS ns1/ns2.reg.ru verified -> VDS), traefik rule staging->боевой (LE-порядок соблюдён),
# LE-серт issued on first hit. Live-smoke GREEN, homepage byte-identical прод-HTML, sitemap 3/3 parity.
# Предыстория: жил на RUVDS IIS (80.64.31.36) до 2026-07-20. RUVDS IIS не тронут (rollback = revert DNS).
# План отката = revert DNS reg.ru -> 80.64.31.36 (RUVDS IIS жив) ИЛИ PUT стека 22 назад на тег (пока
# единственный тег 473923e494db = :latest — первый deploy; образных rollback-тегов пока нет).
#
# Runtime egress: mssql.kzntsv.site:1433, minio.kzntsv.site:443 (оригиналы + variant-cache), smtp.yandex.ru:465.
#
# Секреты — env на VDS (Portainer stack env), НЕ в образ, НЕ в git. Значения ${...} инжектит Portainer
# из stack-переменных (переиспользованы verbatim из tandemmebel стека 20 — общая snolla-инфра). Контракт:
# DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD
# siteId=B9ECDB50-2B93-4CE5-B238-A9918B3F9CF7 — non-secret, в production.json (не env). Culture en. siteUrl=https://on.snolla.com
services:
on-snolla:
container_name: on-snolla
image: registry.kzntsv.site/on-snolla:473923e494db # git-sha victor/on.snolla.com master 473923e494db (snolla 0.42.1). rollback-теги в registry: 473923e494db (:latest он же — первый deploy, отката пока нет, revert DNS -> RUVDS).
restart: unless-stopped
mem_limit: 512m # guardrail: snolla baseline ~100-200M; OOM -> restart. standalone-compose -> mem_limit (НЕ deploy.resources, swarm-only).
environment:
NODE_ENV: production
TZ: Europe/Moscow
# --- 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.on-snolla.entrypoints: websecure
# LIVE (cutover 2026-07-20). Rollback = Host(`on-snolla.vds.kzntsv.site`) + revert DNS -> 80.64.31.36.
traefik.http.routers.on-snolla.rule: Host(`on.snolla.com`)
traefik.http.routers.on-snolla.tls.certresolver: letsEncrypt
traefik.http.services.on-snolla.loadbalancer.server.port: "5000"
networks:
proxy:
external: true

View File

@@ -11,7 +11,7 @@ Import-Module WebAdministration
$siteName = 'snolla' $siteName = 'snolla'
$poolName = 'snolla' $poolName = 'snolla'
$physPath = 'C:\sites\snolla' $physPath = 'C:\sites\snolla'
$aliases = @('tandemmebel.snolla.com','labtools.snolla.com','labtoolspro.snolla.com','emspb.snolla.com','kupimknigi.snolla.com','on.snolla.com') $aliases = @('tandemmebel.snolla.com','labtools.snolla.com','labtoolspro.snolla.com','emspb.snolla.com','kupimknigi.snolla.com','internal.snolla.com')
Write-Host "== 1. AppPool $poolName ==" Write-Host "== 1. AppPool $poolName =="
if (Test-Path "IIS:\AppPools\$poolName") { if (Test-Path "IIS:\AppPools\$poolName") {