from datetime import datetime from enum import Enum from typing import List, Optional from pydantic import BaseModel, Field class LinkType(str, Enum): http = "http" ssh = "ssh" rdp = "rdp" command = "command" class LinkEnvironment(str, Enum): prod = "prod" test = "test" dev = "dev" class LinkScope(str, Enum): case = "case" customer = "customer" hardware = "hardware" global_scope = "global" class LinkCategoryBase(BaseModel): name: str = Field(..., min_length=1, max_length=100) icon: Optional[str] = Field(default=None, max_length=100) sort_order: int = 100 class LinkCategoryCreate(LinkCategoryBase): pass class LinkCategory(LinkCategoryBase): id: int created_at: datetime updated_at: datetime class LinkBase(BaseModel): name: str = Field(..., min_length=1, max_length=255) description: Optional[str] = None type: LinkType url: Optional[str] = None host: Optional[str] = None port: Optional[int] = Field(default=None, ge=1, le=65535) username: Optional[str] = None icon: Optional[str] = None color: Optional[str] = None customer_id: Optional[int] = None case_id: Optional[int] = None hardware_id: Optional[int] = None vault_item_id: Optional[str] = None vault_item_ids: List[str] = Field(default_factory=list) is_critical: bool = False is_favorite: bool = False environment: LinkEnvironment = LinkEnvironment.prod class LinkCreate(LinkBase): category_ids: List[int] = Field(default_factory=list) class LinkUpdate(BaseModel): name: Optional[str] = Field(default=None, min_length=1, max_length=255) description: Optional[str] = None type: Optional[LinkType] = None url: Optional[str] = None host: Optional[str] = None port: Optional[int] = Field(default=None, ge=1, le=65535) username: Optional[str] = None icon: Optional[str] = None color: Optional[str] = None customer_id: Optional[int] = None case_id: Optional[int] = None hardware_id: Optional[int] = None vault_item_id: Optional[str] = None vault_item_ids: Optional[List[str]] = None is_critical: Optional[bool] = None is_favorite: Optional[bool] = None environment: Optional[LinkEnvironment] = None category_ids: Optional[List[int]] = None class Link(LinkBase): id: int category_ids: List[int] = Field(default_factory=list) created_at: datetime updated_at: datetime deleted_at: Optional[datetime] = None class RelevantLink(Link): scope: LinkScope scope_priority: int score: int match_count: int matched_tag_ids: List[int] = Field(default_factory=list) category_ids: List[int] = Field(default_factory=list) class LinkActionLogCreate(BaseModel): action_type: str = Field(..., min_length=1, max_length=50) case_id: Optional[int] = None customer_id: Optional[int] = None metadata: Optional[dict] = None class LinkActionResult(BaseModel): link_id: int action_type: str type: LinkType open_url: Optional[str] = None ssh_command: Optional[str] = None rdp_content: Optional[str] = None command_text: Optional[str] = None username: Optional[str] = None vault_item_id: Optional[str] = None vault_search_hint: Optional[str] = None class LinkLatestStatus(BaseModel): link_id: int status: str checked_at: datetime details: dict = Field(default_factory=dict) class VaultCredential(BaseModel): item_id: Optional[str] = None item_name: Optional[str] = None username: Optional[str] = None password: Optional[str] = None totp: Optional[str] = None notes: Optional[str] = None url: Optional[str] = None class LinkVaultResolveRequest(BaseModel): item_id: Optional[str] = None search_hint: Optional[str] = None class LinkVaultResolveResponse(BaseModel): status: str configured: bool message: Optional[str] = None checked_item_ids: List[str] = Field(default_factory=list) credential: Optional[VaultCredential] = None