from __future__ import annotations from typing import List, Optional, Dict, Any, Literal from pydantic import BaseModel, Field, validator class SignedRequestHeaders(BaseModel): """Заголовки межузлового запроса с подписью Ed25519""" x_node_communication: Literal["true"] = Field(alias="X-Node-Communication") x_node_id: str = Field(alias="X-Node-ID") x_node_public_key: str = Field(alias="X-Node-Public-Key") x_node_signature: str = Field(alias="X-Node-Signature") class Config: populate_by_name = True class ChunkRef(BaseModel): chunk_id: str content_id: str chunk_index: int chunk_hash: str encrypted_data: str signature: Optional[str] = None created_at: Optional[str] = None class ContentRequest(BaseModel): action: Literal["content_sync"] sync_type: Literal["content_request", "new_content", "content_list"] content_info: Dict[str, Any] = Field(default_factory=dict) timestamp: Optional[int] = None @validator("content_info") def validate_content_info(cls, v, values): st = values.get("sync_type") if st == "content_request": # ожидаем content_id и indexes if "content_id" not in v or "indexes" not in v: raise ValueError("content_request requires content_info.content_id and content_info.indexes") if not isinstance(v.get("indexes"), list): raise ValueError("content_info.indexes must be a list") elif st == "new_content": if "content_id" not in v or "total_chunks" not in v: raise ValueError("new_content requires content_info.content_id and content_info.total_chunks") return v class ContentProvideResponse(BaseModel): success: bool = True chunks: List[ChunkRef] = Field(default_factory=list) errors: List[Dict[str, Any]] = Field(default_factory=list) class ContentStatusResponse(BaseModel): content_id: str total_chunks: int have_indexes: List[int] = Field(default_factory=list) missing_indexes: List[int] = Field(default_factory=list) verified: Optional[bool] = None message: Optional[str] = None class ContentVerifyRequest(BaseModel): content_id: str chunks: List[ChunkRef] = Field(default_factory=list) verify_signatures: bool = True class GenericSignedResponse(BaseModel): success: bool data: Dict[str, Any] = Field(default_factory=dict) node_id: Optional[str] = None timestamp: Optional[str] = None