stage-smoke (5 шагов): signup happy-path, bootstrap (Store/Roles/
Units/ProductGroup/PriceTypes/RetailPoint), login → access+refresh
+ /api/me с правильным orgId+role=Admin, edge-cases (дубликат
email, короткий пароль, пустое название, кривой телефон), проверка
public-сайта.
Informational gap: stage-public (test.food-market.kz) использует тот
же build что прод-public, поэтому его форма signup POST'ит в прод
admin. Для stage-testing регистрируемся напрямую POST на test.admin.
Чек-лист stage-testing: пункт 1 ✓.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
LogEnrichmentMiddleware: после Authentication+Authorization вытягивает из
ClaimsPrincipal OrgId (claim org_id) и UserId (sub/NameIdentifier), плюс
CorrelationId из заголовка X-Correlation-ID (или генерирует Guid). Все три
кладутся в Serilog LogContext через PushProperty — каждая ILogger.Log*
внутри пайплайна автоматически получает эти поля как структурные
properties (не текст), пригодные для фильтрации в Loki/ELK без regex.
Эхо CorrelationId в response-header — клиент видит id для support.
Business-логи (структурные плейсхолдеры, не string interpolation):
- Supply.Post → "Supply posted: {SupplyNumber} supplier={SupplierId}
store={StoreId} lines={LinesCount} total={Total}".
- RetailSale.Post → "RetailSale posted: {SaleNumber} store={StoreId}
payment={Payment} lines={LinesCount} total={Total}".
docs/logging.md — паттерн, anti-pattern'ы (string interpolation, PII в
логах, токены/пароли), correlation-id workflow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
API:
• SwaggerGen с OpenAPI info (title/version/description),
Bearer security-scheme (через OpenIddict JWT),
стабильные operationId = Controller_VerbAction (HTTP-verb включён
чтобы избежать коллизии когда ASP.NET стрипает Async-суффикс —
WipeAll и WipeAllAsync ранее давали одинаковый operationId);
• CustomSchemaIds с префиксом из namespace (одноимённые nested
record'ы в разных контроллерах больше не схлопываются — StockRow
есть в Inventory_StockController и Reports_StockReportController).
UI:
• /swagger (UI) и /swagger/v1/swagger.json (документ) — только в Development.
На prod не раскрываем (endpoint enumeration).
Web:
• Добавлен devDependency openapi-typescript@^7.5.2 + npm-script gen:api,
читающий http://localhost:5081/swagger/v1/swagger.json.
• src/lib/api.generated.ts — сгенерированные типы (~7700 строк, все
схемы и operations).
• src/lib/apiClient.ts — тонкая обёртка над axios api, использующая
типы из generated. Подключена для пары контроллеров (Reports/Sales,
Reports/ABC, Reports/Profit) как образец постепенной миграции.
docs/openapi.md — workflow генерации (live API или Swashbuckle CLI),
versioning, наставления для нового кода.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
deploy/.env.example — все required/опц. переменные (POSTGRES_PASSWORD, REGISTRY,
*_TAG, OPENIDDICT_ISSUER/CERT_PASSWORD, FM_* бэкапа, Cors/RateLimiting/MoySklad).
docs/secrets.md — таблица переменных, где живут секреты (SMTP в БД, сертификаты в
volume), ротация, гигиена. compose: api получает OpenIddict__Issuer (за прокси
обязателен) и OpenIddict__CertPassword из .env. compose config валиден.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
food-market-backup.sh: pg_dump -Fc контейнера + tar uploads, ротация 30 дней,
атомарная запись через .tmp+mv. food-market-backup.{service,timer} — ежедневно
03:00 с догоном пропущенных. docs/backup-restore.md — установка таймера, ручной
бэкап, восстановление БД (drop+create / --clean) и uploads, проверка дампа.
Скрипт проверен против food-market-postgres: дамп PGDMP custom-format,
248 TOC, pg_restore --list читает. Установку на prod-vm не делаем — только артефакты.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
OpenIddictKeyConfigurator: dev — прежний RSA-ключ в App_Data (поведение не
менялось, шифрование access-token выключено); prod/stage — отдельные X509
сертификаты подписи и шифрования из конфига (OpenIddict:SigningCertPath /
EncryptionCertPath / CertPassword, можно env). Нет файла → генерируется
persistent self-signed (RSA 2048, 5 лет) и сохраняется в App_Data (volume),
а не dev-ephemeral — токены переживают рестарт.
Проверено: prod выдаёт 5-сегментный JWE, /api/me 200; рестарт → те же
сертификаты (fingerprint совпал), pre-restart токен валиден. dev — 3-сегментный
JWT, /api/me 200. docs/openiddict-keys.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>