Arag / app /schemas /superadmin.py
AuthorBot
SuperAdmin QoL: persistent session, TOTP session, shared provider keys.
65a6194
Raw
History Blame Contribute Delete
3.93 kB
"""Author RAG Chatbot SaaS — SuperAdmin Pydantic Schemas."""
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, EmailStr, Field, field_validator
class CreateAuthorRequest(BaseModel):
"""Request body for SuperAdmin to create a new author account."""
email: EmailStr
password: str = Field(..., min_length=8, max_length=128)
full_name: str = Field(..., min_length=1, max_length=255)
plan: Literal["monthly", "quarterly", "semi_annual", "annual"] | None = None
auto_renew: bool = False
@field_validator("password")
@classmethod
def password_complexity(cls, v: str) -> str:
if not any(c.isupper() for c in v):
raise ValueError("Password must contain at least one uppercase letter")
if not any(c.islower() for c in v):
raise ValueError("Password must contain at least one lowercase letter")
if not any(c.isdigit() for c in v):
raise ValueError("Password must contain at least one digit")
if not any(c in "!@#$%^&*()_+-=[]{}|;':\",./<>?" for c in v):
raise ValueError("Password must contain at least one special character")
return v
class GrantAccessRequest(BaseModel):
"""Request body for granting subscription access to an author."""
plan: Literal["monthly", "quarterly", "semi_annual", "annual"]
auto_renew: bool = False
notes: str | None = Field(default=None, max_length=500)
custom_token_budget: int | None = Field(default=None, ge=1)
class RevokeAccessRequest(BaseModel):
"""Request body for revoking access. Reason is mandatory."""
reason: str = Field(..., min_length=5, max_length=500)
class AddBonusTokensRequest(BaseModel):
"""Request body for adding bonus tokens."""
bonus_tokens: int = Field(..., ge=1000)
class ExtendSubscriptionRequest(BaseModel):
"""Request body for extending a subscription."""
extend_days: int = Field(..., ge=1, le=365)
class AccessSummary(BaseModel):
"""Summary of a client's active access."""
grant_id: str
plan: str
expires_at: datetime
is_revoked: bool
token_budget: int
bonus_tokens: int
model_config = {"from_attributes": True}
class ClientSummary(BaseModel):
"""Summary of an author client for the list view."""
id: str
email: str
full_name: str | None
website_url: str | None
chatbot_is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
class ClientListResponse(BaseModel):
clients: list[ClientSummary]
count: int
class ClientDetailResponse(BaseModel):
user: ClientSummary
access: AccessSummary | None
class AccessGrantResponse(BaseModel):
"""Response after granting access — includes the subscription token."""
token: str # The subscription token — send to author securely
access: AccessSummary
class AuditEntry(BaseModel):
id: str
timestamp: datetime
actor_email: str
action: str
target_type: str | None
target_id: str | None
details: str | None
model_config = {"from_attributes": True}
class AuditLogListResponse(BaseModel):
entries: list[AuditEntry]
count: int
class PlatformApiCredentialUpsert(BaseModel):
"""SuperAdmin: upsert optional retailer API credential (R-180)."""
platform_id: str = Field(min_length=1, max_length=50)
provider: str = Field(min_length=1, max_length=50)
api_key: str = Field(min_length=8, max_length=500)
api_secret: str | None = Field(default=None, max_length=500)
is_enabled: bool = True
class ProviderApiCredentialUpsert(BaseModel):
"""SuperAdmin: upsert shared provider API key (covers all linked platforms)."""
provider_id: str = Field(min_length=1, max_length=50)
api_key: str = Field(min_length=8, max_length=500)
api_secret: str | None = Field(default=None, max_length=500)
is_enabled: bool = True