Spaces:
Running
Running
| """Note-related Pydantic models.""" | |
| from __future__ import annotations | |
| from datetime import datetime | |
| from typing import Optional | |
| from pydantic import BaseModel, ConfigDict, Field, field_validator | |
| class NoteMetadata(BaseModel): | |
| """Frontmatter metadata (allows arbitrary keys).""" | |
| model_config = ConfigDict(extra="allow") | |
| title: Optional[str] = None | |
| tags: Optional[list[str]] = None | |
| project: Optional[str] = None | |
| created: Optional[datetime] = None | |
| updated: Optional[datetime] = None | |
| class Note(BaseModel): | |
| """Complete note with content and metadata.""" | |
| model_config = ConfigDict( | |
| json_schema_extra={ | |
| "example": { | |
| "user_id": "alice", | |
| "note_path": "api/design.md", | |
| "version": 5, | |
| "title": "API Design", | |
| "metadata": { | |
| "tags": ["backend", "api"], | |
| "project": "auth-service", | |
| }, | |
| "body": "# API Design\\n\\nThis document describes...", | |
| "created": "2025-01-10T09:00:00Z", | |
| "updated": "2025-01-15T14:30:00Z", | |
| "size_bytes": 4096, | |
| } | |
| } | |
| ) | |
| user_id: str = Field(..., description="Owner user ID") | |
| note_path: str = Field( | |
| ..., | |
| min_length=1, | |
| max_length=256, | |
| description="Relative path to vault root (includes .md)", | |
| ) | |
| version: int = Field(..., ge=1, description="Optimistic concurrency version") | |
| title: str = Field(..., min_length=1, description="Display title") | |
| metadata: NoteMetadata = Field(default_factory=NoteMetadata, description="Frontmatter") | |
| body: str = Field(..., description="Markdown content") | |
| created: datetime = Field(..., description="Creation timestamp") | |
| updated: datetime = Field(..., description="Last update timestamp") | |
| size_bytes: int = Field(..., ge=0, le=1_048_576, description="File size in bytes") | |
| def validate_path(cls, value: str) -> str: | |
| if not value.endswith(".md"): | |
| raise ValueError("Note path must end with .md") | |
| if ".." in value: | |
| raise ValueError("Note path must not contain '..'") | |
| if "\\" in value: | |
| raise ValueError("Note path must use Unix-style separators (/)") | |
| if value.startswith("/"): | |
| raise ValueError("Note path must be relative (no leading /)") | |
| return value | |
| class NoteCreate(BaseModel): | |
| """Request payload to create a note.""" | |
| note_path: str = Field(..., min_length=1, max_length=256) | |
| title: Optional[str] = None | |
| metadata: Optional[NoteMetadata] = None | |
| body: str = Field(..., max_length=1_048_576) | |
| class NoteUpdate(BaseModel): | |
| """Request payload to update a note.""" | |
| title: Optional[str] = None | |
| metadata: Optional[NoteMetadata] = None | |
| body: str = Field(..., max_length=1_048_576) | |
| if_version: Optional[int] = Field( | |
| None, ge=1, description="Expected version for concurrency check" | |
| ) | |
| class NoteSummary(BaseModel): | |
| """Lightweight representation used for listings.""" | |
| note_path: str | |
| title: str | |
| updated: datetime | |
| __all__ = ["NoteMetadata", "Note", "NoteCreate", "NoteUpdate", "NoteSummary"] | |