7.8 KiB
Sprint 20 — Mapster + SSO scaffolding + maintenance automation
Цель: закрыть TD-3 (Mapster вместо ручных LINQ-проекций), добавить SSO-скелет (Google + Microsoft), включить maintenance-автоматику (stale cleanup / VACUUM / disk / performance regression / analytics).
Старт: 2026-06-07 (после Sprint 19). Исполнитель: Claude Opus 4.7.
Принципы
- Mapster — без AutoMapper (платный + CVE), config в
Application/Mapping/. - SSO — только скелет. Реальные client_id/secret не коммитим, пустые → 503.
- Hangfire jobs — идемпотентные, с лимитом на rows (не зачищать слишком много за раз).
- НЕ трогать:
global.json, prod admin.food-market.kz, POS WPF.
Чек-лист
-
1. TD-3 Mapster —
Application/Mapping/MapsterConfig.csсTypeAdapterConfigдля Product+ProductBarcode+ProductPrice+Counterparty. Singleton зарегистрирован вProgram.cs. ProductsController.List/Get/ GetInternalAsync + CounterpartiesController.List/Get переведены на.ProjectToType<TDto>(MapsterConfig.Config). InlineProjectionудалён. -
2. SSO Google + Microsoft scaffolding — пакеты
Microsoft.AspNetCore.Authentication.Google8.0.11 +.MicrosoftAccount8.0.11 +.Cookies2.3.0. Условная регистрация в Program.cs: еслиAuthentication:{Google|Microsoft}:ClientIdпустой — провайдер не подключается.ExternalAuthControllerс endpoint'ами:GET /api/auth/external/{provider}— Challenge или 503 с подсказкойGET /api/auth/external/callback?provider=...— 501 с email (invite-flow TODO)GET /api/auth/external/providers—{google: bool, microsoft: bool}docs/sso.md— инструкция получения keys у Google/Microsoft.
-
3. Stale-data cleanup автоматика — HousekeepingJobs расширен:
PruneOrgAuditLogAsync—OrgAuditLog>Cleanup:OrgAuditLogDays(90)PruneDraftsAsync— Supply/RetailSale/Demand в Draft >Cleanup:DraftDays(30)PruneRevokedRefreshTokensAsync—OpenIddictTokensType=refresh, Status=revoked/redeemed >Cleanup:RevokedRefreshTokenDays(7).
Три новых cron'a в
HangfireJobsConfigurator(03:00 / 03:15 / 03:20 UTC). -
4. DB VACUUM automation —
DatabaseMaintenanceJobs.VacuumTopTablesAsync:pg_total_relation_sizeвыбирает топ-Maintenance:VacuumTopN(5) таблиц →VACUUM (ANALYZE) public."<table>"per-table. БезFULL→ не блокирует пишущие транзакции. Логирует время per-table. Cron еженедельно вс 04:00 UTC (Hangfire:Cron:VacuumTopTables). -
5. Disk usage monitoring —
DiskMonitoringJobежечасно (Hangfire:Cron:DiskMonitor):DriveInfo.AvailableFreeSpaceна пути изMonitoring:DiskPaths(default/opt,/var/lib/docker). При свободе <Monitoring:DiskMinFreeBytes(1GB) → Telegram-alert наMonitoring:SuperAdminTelegramChatIds(CSV). Anti-spam: один alert per mount perMonitoring:DiskAlertCooldownHours(6) часов (in-memory). Prometheus-gaugefood_market_disk_free_bytes{mount="..."}обновляется каждым прогоном. -
6. Performance regression detection —
~/nightly-perf-check.sh: парсит/metricsstage'а, считаетdb_avg_ms = sum/countпоfood_market_db_query_duration_seconds, сравнивает с baseline в~/.fm-watchdog/perf-baseline.json. Δ>PERF_THRESHOLD_PCT(30%) → Telegram-alert. Sliding window: baseline обновляется только при «нет регрессии». Cron-устанавливаемый скрипт (запускать послеnightly-verify.sh). -
7. Public-site analytics placeholder — Astro
food-market.public/src/layouts/BaseLayout.astroрендерит GA4<script async src="...gtag/js?id=...">и Yandex.Metrikaym(id, init, ...)только если заданыPUBLIC_GA_ID/PUBLIC_YM_IDenv-vars. Иначе —<script data-analytics="..." data-id="REPLACE_ME" data-doc="docs/analytics.md">маркер (виден в view-source).docs/analytics.md— инструкция по подключению + privacy-notes.
Журнал
2026-06-07 старт
Sprint 19 закрыт (7/7 ✓ + 1 hotfix). Поехали по tech debt + maintenance.
2026-06-07 итог
Все 7 пунктов ✓. Stage deploy + retest:
Recurring jobs зарегистрированы (проверка hangfire.hash WHERE key LIKE 'recurring-job%'): disk-monitor, vacuum-top-tables, prune-drafts,
prune-org-audit-log, prune-revoked-refresh-tokens — +5 к существующим
(prune-stock-movements, prune-audit-log, weekly-summary, low-stock-alert,
telegram-owner-daily-summary). Итого 10 recurring.
Endpoint smoke (через s20-smoke.ts, удалён после прогона):
| Endpoint | Статус | Результат |
|---|---|---|
| GET /api/catalog/products (Mapster) | 200 | 99ms first, avg 115ms over 5 |
| GET /api/catalog/counterparties (Mapster) | 200 | items=0 (свежий tenant) |
| GET /api/auth/external/providers | 200 | {google:false, microsoft:false} |
| GET /api/auth/external/google | 503 | error: SSO для Google не настроено. |
| GET /api/auth/external/unknown | 400 | unknown provider |
| GET /metrics | 200 | содержит food_market_disk_free_bytes HELP |
Stage scenarios: smoke 5/5 ✓, catalog 6/6 ✓.
Perf-check: ~/nightly-perf-check.sh отработал в первый раз —
baseline db_avg_ms=3.586 (sum=1.194 count=333) записан в
~/.fm-watchdog/perf-baseline.json. Будущие прогоны сравнят с этим
значением; >30% деградации → Telegram-alert.
Итог
Все 7 пунктов ✓. Локальные цифры:
- Mapster: 4 типа в config'е, 5 sites вызовов ProjectToType, ~115ms на список из 0 товаров (свежий tenant, сравнение «до/после» неинформативно без нагрузки — реальный замер на нагруженном tenant'е в следующем спринте).
- SSO: 3 endpoint'a, 2 провайдера, conditional auth-registration.
- Cleanup: 3 новых job'a (org-audit / drafts / refresh-tokens) + раздельные cron'ы 03:00-03:20 UTC.
- VACUUM: топ-5 таблиц еженедельно вс 04:00 UTC.
- Disk: ежечасно, 6h cooldown, Prom-gauge + Telegram.
- Perf-regression: nightly script, sliding baseline, 30% threshold.
- Analytics: 2 placeholder'a (GA4 + YM) в Astro layout, env-driven.
Условия для пользователя (после Sprint 20)
Watchdog ~/.fm-watchdog/DONE создан. Дальше нужны решения вне кода:
- SSO: получить OAuth-keys у Google и Microsoft, положить в
appsettings.Production.json. - Telegram:
Monitoring:SuperAdminTelegramChatIds— нужен список chat-id для disk-alert'ов. - ОФД-keys / Webkassa: реальные ключи у фискального оператора.
- MoySklad-tokens: при необходимости импорта.
- POS WPF: тест на Windows-машине.
- Прод-деплой: решение и инфраструктура для prod.
- Реальный SMTP: SendGrid / Mailgun / yandex300 для писем.
- kz-перевод: текущие i18n-строки на русском, для kz требуется носитель.