Files
chatballs/apps/backend/hub_platform/api/exceptions.py
T
AndreyandClaude Fable 5 14f5f33ce7 🔥 feat(platform)!: полный снос тарифного контура (этап 4 пивота, ADR-HUB-0042)
Тарифы, подписки, entitlements, quotas и managed AI удалены целиком:
- приложение subscriptions снесено (12 таблиц дропаются в tenancy/0020);
- CustoAI и credential_mode удалены (ai/0016): AI только через BYOK-провайдера
  организации; активация агента требует настроенного провайдера;
- вычищены все quota/entitlement-гейты: диалоги, порталы, интеграции, продукты,
  звонки (p2p-слоты), storage (остаётся технический учёт без лимитов);
- provisioning без плана/подписки; приглашение владельца без активации тарифа;
- эндпоинт и раздел «Тариф и оплата» удалены (фронт + бэк), HasEntitlement снят;
- уведомление LIMIT_REACHED удалено (notifications/0009);
- заодно: чек-лист запуска /company/launch-checklist/ (SPEC-HUB-0031 §5)
  и фикс сохранения provider_integration при обновлении агента.

Backend 550 тестов OK (докер, миграции с нуля), frontend tsc + vitest 67 OK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 20:28:56 +03:00

33 lines
984 B
Python

from typing import Any
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
def _flatten(data: Any) -> str:
if isinstance(data, str):
return data
if isinstance(data, dict):
return "; ".join(_flatten(value) for value in data.values())
if isinstance(data, (list, tuple)):
return "; ".join(_flatten(item) for item in data)
return str(data)
def api_exception_handler(exc: Exception, context: dict[str, Any]):
"""Normalize every DRF error body to the SPA contract: {"detail": "<text>"}."""
response = drf_exception_handler(exc, context)
if response is None:
return None
data = response.data
if (
isinstance(data, dict)
and list(data.keys()) == ["detail"]
and isinstance(data["detail"], str)
):
return response
response.data = {"detail": _flatten(data)}
return response