Backend (Phase 1-6): - Pydantic schemas for request/response validation - Service layer (SSH Key, Server, Repo, Sync) - API routes with authentication - FastAPI main application with lifespan management - ORM models (SshKey, Server, Repo, SyncLog) Frontend (Phase 7): - Vue 3 + Element Plus + Pinia + Vue Router - API client with Axios and interceptors - State management stores - All page components (Dashboard, Servers, Repos, SyncLogs, SshKeys, Settings) Deployment (Phase 8): - README with quick start guide - Startup script (start.sh) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
173 lines
6.3 KiB
Python
173 lines
6.3 KiB
Python
"""
|
|
Server Pydantic schemas.
|
|
"""
|
|
from typing import Optional
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class ServerCreate(BaseModel):
|
|
"""
|
|
Schema for creating a new server.
|
|
"""
|
|
name: str = Field(..., min_length=1, max_length=100, description="Server name")
|
|
url: str = Field(..., min_length=1, max_length=500, description="Gitea server URL")
|
|
api_token: str = Field(..., min_length=1, description="Gitea API token")
|
|
ssh_key_id: int = Field(..., gt=0, description="SSH key ID to use")
|
|
local_path: str = Field(..., min_length=1, max_length=500, description="Local storage path")
|
|
sync_enabled: bool = Field(default=False, description="Whether sync is enabled")
|
|
schedule_cron: Optional[str] = Field(
|
|
default=None,
|
|
max_length=50,
|
|
description="Cron expression for scheduled sync"
|
|
)
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def name_must_not_be_empty(cls, v: str) -> str:
|
|
"""Validate that name is not empty or whitespace only."""
|
|
if not v or not v.strip():
|
|
raise ValueError("name must not be empty")
|
|
return v.strip()
|
|
|
|
@field_validator("url")
|
|
@classmethod
|
|
def url_must_not_be_empty(cls, v: str) -> str:
|
|
"""Validate that url is not empty or whitespace only."""
|
|
if not v or not v.strip():
|
|
raise ValueError("url must not be empty")
|
|
return v.strip()
|
|
|
|
@field_validator("api_token")
|
|
@classmethod
|
|
def api_token_must_not_be_empty(cls, v: str) -> str:
|
|
"""Validate that api_token is not empty or whitespace only."""
|
|
if not v or not v.strip():
|
|
raise ValueError("api_token must not be empty")
|
|
return v.strip()
|
|
|
|
@field_validator("local_path")
|
|
@classmethod
|
|
def local_path_must_not_be_empty(cls, v: str) -> str:
|
|
"""Validate that local_path is not empty or whitespace only."""
|
|
if not v or not v.strip():
|
|
raise ValueError("local_path must not be empty")
|
|
return v.strip()
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [
|
|
{
|
|
"name": "my-gitea",
|
|
"url": "https://gitea.example.com",
|
|
"api_token": "your_api_token_here",
|
|
"ssh_key_id": 1,
|
|
"local_path": "/data/gitea-mirror",
|
|
"sync_enabled": False,
|
|
"schedule_cron": None
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class ServerUpdate(BaseModel):
|
|
"""
|
|
Schema for updating a server.
|
|
All fields are optional.
|
|
"""
|
|
name: Optional[str] = Field(None, min_length=1, max_length=100, description="Server name")
|
|
url: Optional[str] = Field(None, min_length=1, max_length=500, description="Gitea server URL")
|
|
api_token: Optional[str] = Field(None, min_length=1, description="Gitea API token")
|
|
ssh_key_id: Optional[int] = Field(None, gt=0, description="SSH key ID to use")
|
|
local_path: Optional[str] = Field(None, min_length=1, max_length=500, description="Local storage path")
|
|
sync_enabled: Optional[bool] = Field(None, description="Whether sync is enabled")
|
|
schedule_cron: Optional[str] = Field(
|
|
None,
|
|
max_length=50,
|
|
description="Cron expression for scheduled sync"
|
|
)
|
|
status: Optional[str] = Field(
|
|
None,
|
|
pattern="^(untested|testing|success|error)$",
|
|
description="Server status"
|
|
)
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def name_must_not_be_empty(cls, v: Optional[str]) -> Optional[str]:
|
|
"""Validate that name is not empty or whitespace only."""
|
|
if v is not None and (not v or not v.strip()):
|
|
raise ValueError("name must not be empty")
|
|
return v.strip() if v else None
|
|
|
|
@field_validator("url")
|
|
@classmethod
|
|
def url_must_not_be_empty(cls, v: Optional[str]) -> Optional[str]:
|
|
"""Validate that url is not empty or whitespace only."""
|
|
if v is not None and (not v or not v.strip()):
|
|
raise ValueError("url must not be empty")
|
|
return v.strip() if v else None
|
|
|
|
@field_validator("api_token")
|
|
@classmethod
|
|
def api_token_must_not_be_empty(cls, v: Optional[str]) -> Optional[str]:
|
|
"""Validate that api_token is not empty or whitespace only."""
|
|
if v is not None and (not v or not v.strip()):
|
|
raise ValueError("api_token must not be empty")
|
|
return v.strip() if v else None
|
|
|
|
@field_validator("local_path")
|
|
@classmethod
|
|
def local_path_must_not_be_empty(cls, v: Optional[str]) -> Optional[str]:
|
|
"""Validate that local_path is not empty or whitespace only."""
|
|
if v is not None and (not v or not v.strip()):
|
|
raise ValueError("local_path must not be empty")
|
|
return v.strip() if v else None
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [
|
|
{
|
|
"name": "updated-gitea",
|
|
"sync_enabled": True,
|
|
"schedule_cron": "0 */6 * * *"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class ServerResponse(BaseModel):
|
|
"""
|
|
Schema for server response.
|
|
"""
|
|
id: int = Field(description="Server ID")
|
|
name: str = Field(description="Server name")
|
|
url: str = Field(description="Gitea server URL")
|
|
ssh_key_id: int = Field(description="SSH key ID")
|
|
sync_enabled: bool = Field(description="Whether sync is enabled")
|
|
schedule_cron: Optional[str] = Field(default=None, description="Cron expression")
|
|
local_path: str = Field(description="Local storage path")
|
|
status: str = Field(description="Server status")
|
|
created_at: int = Field(description="Creation timestamp (Unix timestamp)")
|
|
updated_at: int = Field(description="Last update timestamp (Unix timestamp)")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [
|
|
{
|
|
"id": 1,
|
|
"name": "my-gitea",
|
|
"url": "https://gitea.example.com",
|
|
"ssh_key_id": 1,
|
|
"sync_enabled": True,
|
|
"schedule_cron": "0 */6 * * *",
|
|
"local_path": "/data/gitea-mirror",
|
|
"status": "success",
|
|
"created_at": 1711804800,
|
|
"updated_at": 1711891200
|
|
}
|
|
]
|
|
}
|
|
}
|