Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
1. Docs cross-check — обновил performance-baseline.md (Sprint 18/20/23 фиксы), secrets.md (16 новых env-vars из Sprint 20+ — Authentication Google/Microsoft, Monitoring, Cleanup, Hangfire:Cron, Telegram, Maintenance, App, Storage, PUBLIC_GA_ID/YM_ID). 2. Auto-gen api-reference — ApiReferenceDocsJob (Hangfire weekly вс 05:30 UTC) + Python-эквивалент `/tmp/gen-api-ref.py` для commit actual snapshot. docs/api-reference.md = 195 endpoints, 57 controllers. 3. Coverage gap-fill — Sprint18To23FeaturesTests.cs (16 Facts): - bulk-update + cross-tenant isolation - UserPresets CRUD - inline-edit price PATCH - CSV import 2 строки транзакцией - OrgExport create + list isolation - 1C-CSV import с русскими заголовками - audit-log export CSV streaming + BOM check - MoySklad sync-status stub - SSO providers + 503 unconfigured + 400 unknown provider - bug-001 NUL byte → 400 - bug-004 tiny price → 400 - export CSV BOM Покрывает все новые контроллеры Sprint 18-23 + regression-protect для критичных багов. 4. Contract tests — deploy/swagger-diff.sh: pull /swagger/v1/swagger.json с двух URL, diff endpoints+schemas через python3. Exit 0/1/2 для blue-green safety gate. Multi-path auto-detect. 5. docs/error-codes.md — каталог HTTP-кодов API (200-503) + humanizeError pattern для фронта + retry-policy таблица. 6. docs/glossary.md — 50+ доменных терминов (Tenant/Organization/Stock/ StockMovement/RetailSale/Counterparty/Owner/Employee/Role/Permission/ advisory lock/Serializable/…) с ссылками на code-сущности. 7. docs/ONBOARDING.md — first 3 days для нового разработчика (install → запуск → структура → первый PR + FAQ). 8. README.md — обновил под текущее состояние: React 19, Sprint-history 1-24, ссылки на ключевые docs, корректный 5-min quick start. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8.7 KiB
8.7 KiB
API error catalog
Каталог HTTP-кодов и тел ответов, которые возвращает food-market.api.
Используется фронтом для humanizeError(response) и QA для regression
проверки. Если поле error есть — это user-facing сообщение; errors
(множественное) — структурированные ошибки валидации (ASP.NET
ValidationProblemDetails).
Формат
// Универсальный шаблон single-error:
{ "error": "Понятный текст для пользователя.", "field": "Optional" }
// ValidationProblemDetails (FluentValidation / DataAnnotations):
{ "type": "...", "title": "One or more validation errors occurred.",
"status": 400, "errors": { "Name": ["..."], "Prices[0].Amount": ["..."] } }
// retryable flag (Sprint 23):
{ "error": "...", "retryable": true }
Коды
200/201/204 — OK / Created / NoContent
Корректно. Тело — DTO или пусто.
400 — Bad Request
| Когда | Тело | Что показать |
|---|---|---|
| Validation от FluentValidation | ValidationProblemDetails с errors.{field}: [msg] |
Подсветить поле + показать сообщение |
| Business-rule (например, draft пустой) | {error: "Нельзя провести пустой чек."} |
toast + не закрывать форму |
| Сумма оплаты < total | {error: "Сумма оплаты X меньше итога Y. Доплатите...", field: "PaidCash"} |
подсветить поле PaidCash |
| Required price = 0 после rounding (Sprint 23 bug-004) | {error: "Цена «X» обязательна и должна быть больше 0."} |
подсветить prices section |
| NUL-byte в строке (Sprint 23 bug-001) | errors.Name: ["Поле Name не должно содержать управляющих символов..."] |
подсветить поле |
| Дубликат barcode при создании | {error: "Штрихкод X уже используется товаром «Y»."} |
toast |
| Дубликат артикула | {error: "Артикул «X» уже занят в этой организации."} |
toast |
| Невалидный CSV / 1С-import | errors: [{row, error}] |
таблица с подсветкой строк |
401 — Unauthorized
| Когда | Тело | Что показать |
|---|---|---|
| Нет токена / устаревший токен | пусто или OpenIddict-{error: "missing_token"} |
редирект на /login, refresh с RT |
| Garbage / tampered JWT | {error: "missing_token"} |
logout + login |
| Refresh-token недействителен | {error: "invalid_grant", error_description: "..."} |
logout |
403 — Forbidden
| Когда | Тело | Что показать |
|---|---|---|
| Нет permission на mutating action | пусто или ProblemDetails | toast: «Нет прав на это действие» |
Регулярный Admin лезет в /hangfire |
пусто | redirect → 404 на фронте |
| Cashier пытается удалить заявку | пусто | скрыть кнопку delete для Cashier |
404 — Not Found
| Когда | Что показать |
|---|---|
| Document не найден (включая cross-tenant — нельзя раскрыть существование!) | «Запись не найдена. Возможно, удалена.» |
| Endpoint не существует (типо в URL) | (фронту не должно встречаться) |
409 — Conflict
| Когда | Тело | Что показать |
|---|---|---|
| DbUpdateConcurrencyException (xmin) | {error: "Документ изменён в другом окне..."} |
toast + reload |
| Чек уже проведён, повторный post | {error: "Чек уже проведён."} |
toast |
| Serialization failure 40001 (Sprint 23 bug-003) | {error: "Конфликт параллельных операций. Попробуйте ещё раз.", retryable: true} |
auto-retry один раз, при повторе — toast |
| Дубликат preset name | {error: "Пресет с таким именем уже существует..."} |
подсветить input name |
| In-flight org-export ≥3 | {error: "Уже в очереди 3+ экспорта. Подождите..."} |
toast |
| Удаление непустой группы товаров | {error: "Нельзя удалить группу, содержащую товары/подгруппы."} |
toast |
413 — Payload Too Large
| Когда | Что показать |
|---|---|
| Body > nginx limit (10 MB по default) | «Файл слишком большой. Лимит: 10 МБ.» |
429 — Too Many Requests
| Когда | Тело | Что показать |
|---|---|---|
| Rate-limit на signup (3/h IP) | пусто или Retry-After header |
«Слишком много попыток. Попробуйте через час.» |
| Rate-limit на forgot-password (3/h email + 10/h IP) | то же | то же |
| Rate-limit на feedback (5/час) | то же | то же |
| IP-limit (60/мин общий) | то же | «Слишком много запросов с вашего IP.» |
431 — Request Header Fields Too Large
| Когда | Что показать |
|---|---|
| Слишком большие/много HTTP-headers | (нечем фиксить с UI; нечасто) |
500 — Internal Server Error
После Sprint 23 — очень редко. Если встречается:
- Все NUL-byte 500 → теперь 400 (bug-001).
- Все serialization 40001 → теперь 409 (bug-003).
- Все остальные uncaught exceptions → Serilog лог +
correlation-idheader.
Что показать пользователю: «Произошла ошибка. Попробуйте ещё раз
или сообщите администратору. Код: {x-correlation-id}». Этот correlation
id находится в x-correlation-id response-header — записываем в audit.
501 — Not Implemented
| Когда | Тело | Что показать |
|---|---|---|
| SSO callback flow (Sprint 20 scaffold) | {status: "scaffolded", message, email, next} |
«SSO ещё не настроено полностью» |
503 — Service Unavailable
| Когда | Тело | Что показать |
|---|---|---|
| SSO провайдер не сконфигурирован | {error: "SSO для X не настроено.", hint: "..."} |
скрыть кнопку SSO |
| (резерв на maintenance window) | пусто | «Сервис недоступен» |
humanizeError на фронте
src/lib/api.ts → humanizeError(err):
export function humanizeError(err: AxiosError): string {
const data = err.response?.data as any
// 1. Single-error (наш стандарт)
if (data?.error) return data.error
// 2. ValidationProblemDetails
if (data?.errors) {
const first = Object.values(data.errors).flat()[0]
return first ?? 'Ошибка валидации'
}
// 3. По статусу
switch (err.response?.status) {
case 401: return 'Сессия истекла. Войдите снова.'
case 403: return 'Нет прав на это действие.'
case 404: return 'Запись не найдена.'
case 409: return 'Конфликт версий. Перезагрузите страницу.'
case 413: return 'Файл слишком большой.'
case 429: return 'Слишком много запросов. Подождите немного.'
case 500: return `Ошибка сервера. Код: ${err.response.headers['x-correlation-id'] ?? 'unknown'}`
case 503: return 'Сервис временно недоступен.'
}
return err.message ?? 'Неизвестная ошибка'
}
Retry-policy
| Код | Retry? | Условие |
|---|---|---|
| 401 | Один раз — после refresh-token | Если refresh тоже 401 → logout |
409 c retryable: true |
Один авто-retry с задержкой 500ms | Sprint 23 фикс — серверная сторона уже retry'ит до 5 раз, клиентский — дополнительный safety net |
| 429 | Через Retry-After секунд (если есть) |
Не более 3 попыток |
| 500 | НЕТ авто-retry | Пользователь сам решает |
| 503 | Через 5 секунд | До 2 попыток |
Без auto-retry: 400, 403, 404, 413, 501.