from datetime import datetime from pydantic import BaseModel, Field, field_validator from admin_app.core import AdminCredentialStrategy from shared.contracts import AdminPermission, ServiceName, StaffRole, ToolLifecycleStatus, ToolParameterType class AdminRootResponse(BaseModel): service: str status: str message: str environment: str class AdminHealthResponse(BaseModel): service: str status: str version: str class AdminSystemInfoResponse(BaseModel): service: str app_name: str environment: str version: str api_prefix: str debug: bool class AdminAuthenticatedStaffResponse(BaseModel): id: int email: str display_name: str role: StaffRole is_active: bool class AdminCurrentAccessResponse(BaseModel): service: str staff_account: AdminAuthenticatedStaffResponse permissions: list[str] class AdminCapabilityResponse(BaseModel): service: str action: str allowed: bool role: StaffRole class AdminAuditEntryResponse(BaseModel): id: int actor_staff_account_id: int | None event_type: str resource_type: str resource_id: str | None outcome: str message: str | None payload_json: dict | None ip_address: str | None user_agent: str | None created_at: datetime class AdminAuditListResponse(BaseModel): service: str events: list[AdminAuditEntryResponse] class AdminRuntimeApplicationConfigurationResponse(BaseModel): app_name: str environment: str version: str api_prefix: str debug: bool class AdminRuntimeDatabaseConfigurationResponse(BaseModel): host: str port: int name: str cloud_sql_configured: bool class AdminPanelSessionConfigurationResponse(BaseModel): access_cookie_name: str refresh_cookie_name: str cookie_path: str same_site: str secure_cookies: bool class AdminSystemRuntimeConfigurationPayload(BaseModel): application: AdminRuntimeApplicationConfigurationResponse database: AdminRuntimeDatabaseConfigurationResponse panel_session: AdminPanelSessionConfigurationResponse class AdminConfigurationSourceResponse(BaseModel): key: str source: str mutable: bool description: str class AdminSystemRuntimeConfigurationResponse(BaseModel): service: str runtime: AdminSystemRuntimeConfigurationPayload class AdminSystemSecurityConfigurationResponse(BaseModel): service: str security: AdminCredentialStrategy class AdminSystemConfigurationResponse(BaseModel): service: str runtime: AdminSystemRuntimeConfigurationPayload security: AdminCredentialStrategy sources: list[AdminConfigurationSourceResponse] class AdminLoginRequest(BaseModel): email: str password: str = Field(min_length=1) @field_validator("email") @classmethod def validate_email(cls, value: str) -> str: normalized = value.strip().lower() if "@" not in normalized or normalized.startswith("@") or normalized.endswith("@"): raise ValueError("email must be a valid administrative login") return normalized class AdminRefreshTokenRequest(BaseModel): refresh_token: str = Field(min_length=1) class AdminSessionResponse(BaseModel): session_id: int access_token: str refresh_token: str token_type: str expires_in_seconds: int staff_account: AdminAuthenticatedStaffResponse class AdminLogoutResponse(BaseModel): service: str status: str message: str session_id: int class AdminPanelWebSessionResponse(BaseModel): service: str status: str message: str session_id: int expires_in_seconds: int staff_account: AdminAuthenticatedStaffResponse redirect_to: str | None = None class AdminPanelLogoutResponse(BaseModel): service: str status: str message: str session_id: int | None redirect_to: str class AdminToolManagementMetricResponse(BaseModel): key: str label: str value: str description: str class AdminToolLifecycleStageResponse(BaseModel): code: ToolLifecycleStatus label: str description: str class AdminToolParameterTypeResponse(BaseModel): code: ToolParameterType label: str description: str class AdminToolManagementActionResponse(BaseModel): key: str label: str href: str required_permission: AdminPermission description: str class AdminToolOverviewResponse(BaseModel): service: str mode: str metrics: list[AdminToolManagementMetricResponse] workflow: list[AdminToolLifecycleStageResponse] actions: list[AdminToolManagementActionResponse] next_steps: list[str] class AdminToolContractsResponse(BaseModel): service: str publication_source_service: ServiceName publication_target_service: ServiceName lifecycle_statuses: list[AdminToolLifecycleStageResponse] parameter_types: list[AdminToolParameterTypeResponse] publication_fields: list[str] published_tool_fields: list[str] class AdminToolDraftSummaryResponse(BaseModel): draft_id: str tool_name: str display_name: str status: ToolLifecycleStatus summary: str owner_name: str | None = None updated_at: datetime | None = None class AdminToolDraftListResponse(BaseModel): service: str storage_status: str message: str drafts: list[AdminToolDraftSummaryResponse] supported_statuses: list[ToolLifecycleStatus] class AdminToolReviewQueueEntryResponse(BaseModel): entry_id: str tool_name: str display_name: str status: ToolLifecycleStatus gate: str summary: str owner_name: str | None = None queued_at: datetime | None = None class AdminToolReviewQueueResponse(BaseModel): service: str queue_mode: str message: str items: list[AdminToolReviewQueueEntryResponse] supported_statuses: list[ToolLifecycleStatus] class AdminToolPublicationSummaryResponse(BaseModel): publication_id: str tool_name: str display_name: str description: str domain: str version: int status: ToolLifecycleStatus parameter_count: int implementation_module: str implementation_callable: str published_by: str | None = None published_at: datetime | None = None class AdminToolPublicationListResponse(BaseModel): service: str source: str target_service: ServiceName publications: list[AdminToolPublicationSummaryResponse] class AdminToolDraftIntakeParameterRequest(BaseModel): name: str = Field(min_length=1, max_length=64) parameter_type: ToolParameterType description: str = Field(min_length=1, max_length=180) required: bool = True @field_validator("name") @classmethod def normalize_name(cls, value: str) -> str: return value.strip().lower() @field_validator("description") @classmethod def normalize_description(cls, value: str) -> str: return value.strip() class AdminToolDraftIntakeRequest(BaseModel): tool_name: str = Field(min_length=3, max_length=64) display_name: str = Field(min_length=4, max_length=120) domain: str = Field(min_length=3, max_length=40) description: str = Field(min_length=16, max_length=280) business_goal: str = Field(min_length=12, max_length=280) parameters: list[AdminToolDraftIntakeParameterRequest] = Field(default_factory=list, max_length=10) @field_validator("tool_name") @classmethod def normalize_tool_name(cls, value: str) -> str: return value.strip().lower() @field_validator("display_name", "description", "business_goal") @classmethod def strip_text_fields(cls, value: str) -> str: return value.strip() @field_validator("domain") @classmethod def normalize_domain(cls, value: str) -> str: return value.strip().lower() class AdminToolDraftIntakePreviewParameterResponse(BaseModel): name: str parameter_type: ToolParameterType description: str required: bool class AdminToolDraftIntakePreviewResponse(BaseModel): draft_id: str tool_name: str display_name: str domain: str status: ToolLifecycleStatus summary: str business_goal: str parameter_count: int required_parameter_count: int requires_director_approval: bool owner_name: str | None = None parameters: list[AdminToolDraftIntakePreviewParameterResponse] class AdminToolDraftIntakeResponse(BaseModel): service: str storage_status: str message: str draft_preview: AdminToolDraftIntakePreviewResponse warnings: list[str] next_steps: list[str] class AdminCollaboratorSummaryResponse(BaseModel): id: int email: str display_name: str role: StaffRole is_active: bool last_login_at: datetime | None = None created_at: datetime | None = None updated_at: datetime | None = None class AdminCollaboratorListResponse(BaseModel): service: str total: int active_count: int inactive_count: int collaborators: list[AdminCollaboratorSummaryResponse] class AdminCollaboratorCreateRequest(BaseModel): email: str display_name: str = Field(min_length=3, max_length=150) password: str = Field(min_length=1) is_active: bool = True @field_validator("email") @classmethod def normalize_email(cls, value: str) -> str: normalized = value.strip().lower() if "@" not in normalized or normalized.startswith("@") or normalized.endswith("@"): raise ValueError("email must be a valid administrative login") return normalized @field_validator("display_name") @classmethod def normalize_display_name(cls, value: str) -> str: return value.strip() class AdminCollaboratorCreateResponse(BaseModel): service: str message: str collaborator: AdminCollaboratorSummaryResponse class AdminCollaboratorStatusUpdateRequest(BaseModel): is_active: bool class AdminCollaboratorStatusUpdateResponse(BaseModel): service: str message: str collaborator: AdminCollaboratorSummaryResponse