"""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