Zwei Rückmeldungen des Nutzers. - Der Widerrufen-Knopf in der Lizenz-Tabelle ist raus. Eine neue Lizenz widerruft die alte automatisch, und einen Kunden abschalten erledigt der prominente Org-Status-Umschalter. Der API-Endpoint /revoke bleibt. - Die Warnschwelle lässt sich jetzt abschalten, 0 = aus, in allen drei Formularen (Budget, Neue Lizenz, Org-Anlage) samt Modell- und Endpoint-Validierung. Gegenstück im Monitor ist Commit 17b1886 (bisher fiel eine 0 dort still auf die Voreinstellung 80 zurück). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
125 Zeilen
3.9 KiB
Python
125 Zeilen
3.9 KiB
Python
"""Pydantic Models für das Verwaltungsportal."""
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional
|
|
|
|
|
|
class MagicLinkRequest(BaseModel):
|
|
email: str = Field(min_length=3, max_length=200)
|
|
|
|
|
|
class MagicLinkResponse(BaseModel):
|
|
message: str
|
|
|
|
|
|
class VerifyTokenRequest(BaseModel):
|
|
token: str = Field(min_length=10, max_length=200)
|
|
|
|
|
|
class TokenResponse(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
username: str
|
|
email: str = ""
|
|
|
|
|
|
class LicenseParams(BaseModel):
|
|
"""Stellwerte einer Lizenz ohne Org-Bezug.
|
|
|
|
Wird zweifach genutzt, direkt beim Lizenz-Anlegen (LicenseCreate) und
|
|
verschachtelt beim Org-Anlegen (OrgCreate.license). Die Laufzeitfelder
|
|
(credits_used, credits_carried, credits_period_start, budget_warning_sent)
|
|
verwaltet der Monitor, das Portal setzt nur Stellwerte.
|
|
"""
|
|
license_type: str = Field(pattern="^(trial|annual|permanent)$")
|
|
max_users: int = Field(default=5, ge=1, le=1000)
|
|
duration_days: Optional[int] = Field(default=None, ge=1, le=3650)
|
|
token_budget_usd: Optional[float] = None
|
|
credits_total: Optional[int] = None
|
|
cost_per_credit: Optional[float] = None
|
|
budget_warning_percent: Optional[int] = Field(default=80, ge=0, le=100) # 0 = Warnung aus
|
|
unlimited_budget: bool = False
|
|
# 'monthly' = Kontingent wird zum Monatswechsel neu gefuellt (der Monitor
|
|
# setzt den Verbrauch traege zurueck), 'total' = gilt fuer die Laufzeit.
|
|
credits_period: str = Field(default="monthly", pattern="^(monthly|total)$")
|
|
# Uebertrag ungenutzter Credits in den Folgemonat, der Monitor deckelt
|
|
# auf ein Monatskontingent. Standard AUS (Entscheidung Nutzer 25.07.2026),
|
|
# pro Kunde bewusst zuschaltbar.
|
|
credits_rollover: bool = False
|
|
|
|
|
|
class OrgCreate(BaseModel):
|
|
name: str = Field(min_length=1, max_length=200)
|
|
slug: str = Field(min_length=1, max_length=100, pattern="^[a-z0-9-]+$")
|
|
output_language: str = Field(default="de", pattern="^(de|en)$")
|
|
# Optional direkt eine Lizenz mit Credits-Kontingent anlegen
|
|
license: Optional[LicenseParams] = None
|
|
|
|
|
|
class OrgUpdate(BaseModel):
|
|
name: Optional[str] = Field(default=None, max_length=200)
|
|
is_active: Optional[bool] = None
|
|
output_language: Optional[str] = Field(default=None, pattern="^(de|en)$")
|
|
|
|
|
|
class OrgResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
slug: str
|
|
is_active: bool
|
|
user_count: int = 0
|
|
license_status: str = ""
|
|
license_type: str = ""
|
|
created_at: str
|
|
globe_access: bool = False
|
|
network_access: bool = False
|
|
output_language: str = "de"
|
|
|
|
|
|
class LicenseCreate(LicenseParams):
|
|
organization_id: int
|
|
|
|
|
|
class LicenseResponse(BaseModel):
|
|
id: int
|
|
organization_id: int
|
|
license_type: str
|
|
max_users: int
|
|
valid_from: str
|
|
valid_until: Optional[str]
|
|
status: str
|
|
notes: Optional[str]
|
|
token_budget_usd: Optional[float] = None
|
|
credits_total: Optional[int] = None
|
|
credits_used: Optional[float] = None
|
|
cost_per_credit: Optional[float] = None
|
|
budget_warning_percent: Optional[int] = None
|
|
unlimited_budget: bool = False
|
|
# Neue Kontingent-Felder, alle optional mit Default, damit Responses auch
|
|
# von einer noch nicht migrierten Datenbank validieren.
|
|
credits_period: Optional[str] = "monthly"
|
|
credits_rollover: Optional[bool] = False
|
|
credits_carried: Optional[float] = 0
|
|
credits_period_start: Optional[str] = None
|
|
created_at: str
|
|
globe_access: bool = False
|
|
network_access: bool = False
|
|
|
|
|
|
class UserCreate(BaseModel):
|
|
email: str = Field(min_length=3, max_length=200)
|
|
username: Optional[str] = Field(default=None, max_length=100)
|
|
role: str = Field(default="member", pattern="^(org_admin|member)$")
|
|
|
|
|
|
class UserResponse(BaseModel):
|
|
id: int
|
|
email: str
|
|
username: str
|
|
organization_id: int
|
|
role: str
|
|
is_active: bool
|
|
last_login_at: Optional[str]
|
|
created_at: str
|
|
globe_access: bool = False
|
|
network_access: bool = False
|