7.6 KiB
Спринт 1 — стабилизация (P0 код/инфра)
Автономная работа. После каждого пункта: dotnet build (SDK 8.0.126), релевантные тесты,
коммит порцией, отметка [x] здесь, коммит прогресса.
Сборка: POS-проект (
food-market.pos, net8.0-windows) на Linux не собирается — это ожидаемо (нужен Windows SDK). Эталон сборки —dotnet build src/food-market.api/food-market.api.csproj
- solution-сборка тестовых проектов.
Чек-лист
- P0-3 Rate-limit —
Microsoft.AspNetCore.RateLimiting(sliding window) на/connect/tokenи/api/auth/signup. 5/мин/IP, 20/час/IP. Тест: 6-я попытка за минуту → 429. ✅AuthRateLimiterExtensions(global limiter + chained окна, gate по пути), отдельные бакеты на эндпоинт. Проверено curl на :5091 — token 6→429, signup 6→429, бакеты независимы. - P0-4 Health checks —
/health/live(alive) +/health/ready(DB ping + миграции применены). docker-compose healthcheck →/health/ready. ✅DatabaseReadyHealthCheck(CanConnect + GetPendingMigrations), JSON-writer, tagready. Проверено: live→200 (checks:[]), ready→200 (database Healthy). Dockerfile + compose api healthcheck на/health/ready, web ждёт apiservice_healthy./healthоставлен для совместимости. Прим.: startupMigrate()— fail-fast при DB-down на буте (вне scope, compose гейтит api наpostgres: service_healthy). - P0-5 Permission-based authz —
PermissionHandler+[RequiresPermission("...")]читающий флагиRolePermissions. Заменить[Authorize(Roles=...)]в каталоге/документах. E2E: кастомная роль безProductsEdit→ 403 на PUT товара. ✅PermissionAuthorizationHandler(live из БД: Employee→EmployeeRole→Permissions) +RequiresPermissionAttribute+ динамическийPermissionAuthorizationPolicyProvider(policyperm:*). SuperAdmin/Identity-Admin — full-access шорткат (custom-роли не маппятся на Admin). Заменены role-гейты в 8 catalog + 2 document контроллерах (Currencies/Countries оставлены SuperAdmin — глобальный справочник). Закрывает «роли — фикция» из аудита. Проверка: curl на :5091 (403/200/400) + e2erolesstep08 — зелёный 8/8. Rate-limit стал конфигурируемым (RateLimiting:*) — иначе повторные логины тестов → 429. - P0-1 OpenIddict prod-ключи — signing+encryption сертификаты из пути в конфиге,
persistent self-signed если файла нет. Dev-поведение не ломать. Документировать.
✅
OpenIddictKeyConfigurator: dev RSA-XML (без изменений), prod X509 изOpenIddict:SigningCertPath/EncryptionCertPath/CertPassword, self-signed (5 лет) в App_Data при отсутствии. Проверено: prod 5-сегм. JWE, persist через рестарт (тот же fingerprint, pre-restart токен валиден); dev 3-сегм. JWT.docs/openiddict-keys.md. - P0-6 Авто-бэкап —
deploy/food-market-backup.service+.timer, скрипт backup+ротация 30 дней,docs/backup-restore.md. Только артефакты в репо. ✅food-market-backup.sh(pg_dump -Fc + tar uploads, ротация 30д, атомарная запись), systemd timer ежедневно 03:00 (Persistent). Проверено: дамп PGDMP/248 TOC, pg_restore --list ок. - P0-8 —
deploy/.env.example+docs/secrets.md. ✅.env.example(все required+опц.),secrets.md(таблица/ротация/гигиена), пробросOpenIddict__Issuer/CertPasswordв compose.compose configвалиден. - P0-9 —
docs/release-checklist.md. ✅ Пред/во время/после выкатки + откат + прод; ссылки на secrets/backup/openiddict/stage-setup. - P1-20 Unit-тесты —
tests/food-market.UnitTests:StockService.ApplyMovement, расчёт Cost вSuppliesController.Post, валидация платежаRetailSalesController.Post, multi-tenant query filter. ✅ 23 теста зелёные. Чистая логика вынесена в Application (MovingAverageCost,RetailPaymentValidator) и используется контроллерами. StockService + query-filter на SQLite in-memory (EF8 поддерживаетToJson).FakeTenantContext,SqliteDbhelper. - P1-21 Integration-тесты — Testcontainers.PostgreSql + WebApplicationFactory:
signup-flow, supply post→unpost, retail overselling, tenant isolation A vs B, permission-проверки.
✅
tests/food-market.IntegrationTests— 10 тестов зелёные на реальном postgres:16-alpine (Ryuk off, RateLimiting off через env).ApiFactory+ApiActor. Все 5 сценариев покрыты.
Итог
Все 9 пунктов выполнены. Спринт 1 (стабилизация P0/P1-инфра) завершён 2026-05-27.
Сводка:
- P0-3 rate-limit (5/мин+20/час на IP, конфигурируем) —
AuthRateLimiterExtensions. - P0-4 health
/health/live+/health/ready(БД+миграции), compose/Dockerfile healthcheck. - P0-5 permission-based authz (
[RequiresPermission]+ handler по флагам роли), 10 контроллеров. - P0-1 OpenIddict prod X509-ключи из конфига, persistent self-signed.
- P0-6 авто-бэкап (systemd timer + скрипт + ротация 30д) +
backup-restore.md. - P0-8
deploy/.env.example+secrets.md. - P0-9
release-checklist.md. - P1-20 unit-тесты (23) —
MovingAverageCost,RetailPaymentValidator, StockService, query-filter. - P1-21 integration-тесты (10) — Testcontainers + WebApplicationFactory.
Сборка зелёная (dotnet build src/food-market.api); тесты: 23 unit + 10 integration = 33 зелёных.
POS (net8.0-windows) на Linux не собирается — ожидаемо, вне scope.
Пропущено намеренно (по инструкции): P0-7 ОФД (нужен внешний оператор), gateway nginx HTTPS,
global.json (локальный даунгрейд не коммитим). Установка backup-таймера/сертификатов на
prod-vm — отдельный деплой-шаг (артефакты готовы).
Эффект на код вне P0/P1
- Чистая логика вынесена в Application (
MovingAverageCost,RetailPaymentValidator) — контроллеры используют её. Programсталpublic partialдля WebApplicationFactory.- e2e
rolesstep08: gap → реальная проверка permission-enforcement (8/8 зелёный).
Лог
- Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса.
- Все правки на ветке
main(origin Forgejo), без коммитаglobal.json.