Token-Budget Hard-Stop + Banner bei aufgebrauchtem Budget
- check_license() liefert jetzt unlimited_budget, credits_total, credits_used, read_only_reason. Bei nicht-unlimited UND credits_used >= credits_total wird status=budget_exceeded, read_only=True gesetzt. - require_writable_license blockiert mit 403 + X-License-Status-Header je nach Reason. - /api/auth/me liefert read_only_reason und unlimited_budget; credits_percent_used wird nicht mehr auf 100 gekappt (echte Prozente). - Frontend: Banner-Text dynamisch je nach reason (budget_exceeded/expired/...). Refresh-Button bei read_only deaktiviert + Tooltip. Globaler 403-Handler in api.js: bei X-License-Status -> Banner + Toast aktualisieren.
Dieser Commit ist enthalten in:
@@ -187,10 +187,11 @@ async def get_me(
|
||||
from services.license_service import check_license
|
||||
license_info = await check_license(db, current_user["tenant_id"])
|
||||
|
||||
# Credits-Daten laden
|
||||
# Credits-Daten laden (echte Prozente, nicht gekappt)
|
||||
credits_total = None
|
||||
credits_remaining = None
|
||||
credits_percent_used = None
|
||||
unlimited_budget = bool(license_info.get("unlimited_budget", False))
|
||||
if current_user.get("tenant_id"):
|
||||
lic_cursor = await db.execute(
|
||||
"SELECT credits_total, credits_used, cost_per_credit FROM licenses WHERE organization_id = ? AND status = 'active' ORDER BY id DESC LIMIT 1",
|
||||
@@ -200,7 +201,7 @@ async def get_me(
|
||||
credits_total = lic_row["credits_total"]
|
||||
credits_used = lic_row["credits_used"] or 0
|
||||
credits_remaining = max(0, int(credits_total - credits_used))
|
||||
credits_percent_used = round(min(100, (credits_used / credits_total) * 100), 1) if credits_total > 0 else 0
|
||||
credits_percent_used = round((credits_used / credits_total) * 100, 1) if credits_total > 0 else 0
|
||||
|
||||
return UserMeResponse(
|
||||
id=current_user["id"],
|
||||
@@ -216,6 +217,8 @@ async def get_me(
|
||||
license_status=license_info.get("status", "unknown"),
|
||||
license_type=license_info.get("license_type", ""),
|
||||
read_only=license_info.get("read_only", False),
|
||||
read_only_reason=license_info.get("read_only_reason"),
|
||||
unlimited_budget=unlimited_budget,
|
||||
is_global_admin=current_user.get("is_global_admin", False),
|
||||
)
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren