from typing import Optional, List from pydantic import BaseModel, HttpUrl from enum import Enum from datetime import datetime class ProtocolType(str, Enum): OIDC = "OIDC" SIMPLE_API = "SIMPLE_API" class ApplicationBase(BaseModel): app_name: str icon_url: Optional[str] = None protocol_type: ProtocolType = ProtocolType.SIMPLE_API redirect_uris: Optional[str] = None # Stored as JSON string or comma-separated for simplicity in MVP, or validate as list notification_url: Optional[str] = None description: Optional[str] = None category_id: Optional[int] = None class ApplicationCreate(ApplicationBase): pass class ApplicationUpdate(BaseModel): app_name: Optional[str] = None icon_url: Optional[str] = None protocol_type: Optional[ProtocolType] = None redirect_uris: Optional[str] = None notification_url: Optional[str] = None description: Optional[str] = None category_id: Optional[int] = None password: Optional[str] = None verification_code: Optional[str] = None class ApplicationTransferRequest(BaseModel): target_mobile: str password: str verification_code: str class RegenerateSecretRequest(BaseModel): password: str verification_code: str class ApplicationResponse(ApplicationBase): id: int app_id: str owner_id: int # We do NOT return app_secret_hash category_name: Optional[str] = None # 分类名称(从 relationship 获取) created_at: datetime updated_at: datetime class Config: from_attributes = True class ApplicationList(BaseModel): total: int items: List[ApplicationResponse] class ApplicationSecretDisplay(BaseModel): app_id: str app_secret: str access_token: Optional[str] = None class ViewSecretRequest(BaseModel): password: str class AppSyncRequest(BaseModel): mode: str = "ALL" # "ALL" or "SELECTED" user_ids: Optional[List[int]] = [] init_email: bool = False email_domain: Optional[str] = None verification_code: str # Category Management Schemas class CategoryStats(BaseModel): """分类统计信息""" category_id: int category_name: str app_count: int # App Category Schemas (Preset Categories) class AppCategoryBase(BaseModel): name: str description: Optional[str] = None class AppCategoryCreate(AppCategoryBase): pass class AppCategoryUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None password: Optional[str] = None # 密码验证 class AppCategoryResponse(AppCategoryBase): id: int created_at: datetime updated_at: datetime class Config: from_attributes = True class BatchUpdateCategoryRequest(BaseModel): """批量更新应用分类""" app_ids: List[int] category_id: Optional[int] = None # None 表示移除分类 password: str # 管理员密码验证