feat: complete Git Repo Manager MVP implementation
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>
This commit is contained in:
172
backend/app/schemas/server.py
Normal file
172
backend/app/schemas/server.py
Normal file
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user