food-market/docs/sprint20-progress.md
2026-06-07 22:00:50 +05:00

7.8 KiB
Raw Permalink Blame History

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 MapsterApplication/Mapping/MapsterConfig.cs с TypeAdapterConfig для Product+ProductBarcode+ProductPrice+Counterparty. Singleton зарегистрирован в Program.cs. ProductsController.List/Get/ GetInternalAsync + CounterpartiesController.List/Get переведены на .ProjectToType<TDto>(MapsterConfig.Config). Inline Projection удалён.

  • 2. SSO Google + Microsoft scaffolding — пакеты Microsoft.AspNetCore.Authentication.Google 8.0.11 + .MicrosoftAccount 8.0.11 + .Cookies 2.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 расширен:

    • PruneOrgAuditLogAsyncOrgAuditLog > Cleanup:OrgAuditLogDays (90)
    • PruneDraftsAsync — Supply/RetailSale/Demand в Draft > Cleanup:DraftDays (30)
    • PruneRevokedRefreshTokensAsyncOpenIddictTokens Type=refresh, Status=revoked/redeemed > Cleanup:RevokedRefreshTokenDays (7).

    Три новых cron'a в HangfireJobsConfigurator (03:00 / 03:15 / 03:20 UTC).

  • 4. DB VACUUM automationDatabaseMaintenanceJobs.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 monitoringDiskMonitoringJob ежечасно (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 per Monitoring:DiskAlertCooldownHours (6) часов (in-memory). Prometheus-gauge food_market_disk_free_bytes{mount="..."} обновляется каждым прогоном.

  • 6. Performance regression detection~/nightly-perf-check.sh: парсит /metrics stage'а, считает 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.Metrika ym(id, init, ...) только если заданы PUBLIC_GA_ID / PUBLIC_YM_ID env-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 требуется носитель.