first commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
"""Pydantic schemas for API request/response validation."""
|
||||
|
||||
from .user import (
|
||||
UserCreate, UserUpdate, UserResponse, UserLogin,
|
||||
Token, TokenPayload
|
||||
)
|
||||
from .tenant import TenantCreate, TenantUpdate, TenantResponse
|
||||
from .gateway import GatewayCreate, GatewayUpdate, GatewayResponse, GatewayStatus
|
||||
from .endpoint import (
|
||||
EndpointCreate, EndpointUpdate, EndpointResponse,
|
||||
ApplicationTemplateResponse
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# User
|
||||
"UserCreate", "UserUpdate", "UserResponse", "UserLogin",
|
||||
"Token", "TokenPayload",
|
||||
# Tenant
|
||||
"TenantCreate", "TenantUpdate", "TenantResponse",
|
||||
# Gateway
|
||||
"GatewayCreate", "GatewayUpdate", "GatewayResponse", "GatewayStatus",
|
||||
# Endpoint
|
||||
"EndpointCreate", "EndpointUpdate", "EndpointResponse",
|
||||
"ApplicationTemplateResponse",
|
||||
]
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Endpoint-related Pydantic schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import ipaddress
|
||||
from ..models.endpoint import Protocol
|
||||
|
||||
|
||||
class EndpointBase(BaseModel):
|
||||
"""Base endpoint schema."""
|
||||
name: str = Field(..., min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
internal_ip: str = Field(..., description="IP address in customer network")
|
||||
port: int = Field(..., ge=1, le=65535)
|
||||
protocol: Protocol = Protocol.TCP
|
||||
application_name: str | None = None
|
||||
|
||||
@field_validator('internal_ip')
|
||||
@classmethod
|
||||
def validate_ip(cls, v: str) -> str:
|
||||
try:
|
||||
ipaddress.ip_address(v)
|
||||
return v
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid IP address: {e}")
|
||||
|
||||
|
||||
class EndpointCreate(EndpointBase):
|
||||
"""Schema for creating an endpoint."""
|
||||
application_template_id: int | None = None
|
||||
|
||||
|
||||
class EndpointUpdate(BaseModel):
|
||||
"""Schema for updating an endpoint."""
|
||||
name: str | None = Field(None, min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
internal_ip: str | None = None
|
||||
port: int | None = Field(None, ge=1, le=65535)
|
||||
protocol: Protocol | None = None
|
||||
application_name: str | None = None
|
||||
|
||||
@field_validator('internal_ip')
|
||||
@classmethod
|
||||
def validate_ip(cls, v: str | None) -> str | None:
|
||||
if v is None:
|
||||
return v
|
||||
try:
|
||||
ipaddress.ip_address(v)
|
||||
return v
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid IP address: {e}")
|
||||
|
||||
|
||||
class EndpointResponse(EndpointBase):
|
||||
"""Schema for endpoint response."""
|
||||
id: int
|
||||
gateway_id: int
|
||||
application_template_id: int | None
|
||||
created_at: datetime
|
||||
updated_at: datetime | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ApplicationTemplateResponse(BaseModel):
|
||||
"""Schema for application template response."""
|
||||
id: int
|
||||
name: str
|
||||
description: str | None
|
||||
default_port: int
|
||||
protocol: Protocol
|
||||
icon: str | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
@@ -0,0 +1,82 @@
|
||||
"""Gateway-related Pydantic schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import ipaddress
|
||||
from ..models.gateway import RouterType, ProvisioningMethod
|
||||
|
||||
|
||||
class GatewayBase(BaseModel):
|
||||
"""Base gateway schema."""
|
||||
name: str = Field(..., min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
location: str | None = None
|
||||
router_type: RouterType
|
||||
firmware_version: str | None = None
|
||||
vpn_subnet: str | None = Field(None, description="Network behind gateway, e.g., 10.0.0.0/24")
|
||||
|
||||
@field_validator('vpn_subnet')
|
||||
@classmethod
|
||||
def validate_subnet(cls, v: str | None) -> str | None:
|
||||
if v is None:
|
||||
return v
|
||||
try:
|
||||
ipaddress.ip_network(v, strict=False)
|
||||
return v
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid subnet format: {e}")
|
||||
|
||||
|
||||
class GatewayCreate(GatewayBase):
|
||||
"""Schema for creating a gateway."""
|
||||
serial_number: str | None = None
|
||||
provisioning_method: ProvisioningMethod = ProvisioningMethod.ATV_FILE
|
||||
|
||||
|
||||
class GatewayUpdate(BaseModel):
|
||||
"""Schema for updating a gateway."""
|
||||
name: str | None = Field(None, min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
location: str | None = None
|
||||
serial_number: str | None = None
|
||||
firmware_version: str | None = None
|
||||
vpn_subnet: str | None = None
|
||||
provisioning_method: ProvisioningMethod | None = None
|
||||
|
||||
@field_validator('vpn_subnet')
|
||||
@classmethod
|
||||
def validate_subnet(cls, v: str | None) -> str | None:
|
||||
if v is None:
|
||||
return v
|
||||
try:
|
||||
ipaddress.ip_network(v, strict=False)
|
||||
return v
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid subnet format: {e}")
|
||||
|
||||
|
||||
class GatewayResponse(GatewayBase):
|
||||
"""Schema for gateway response."""
|
||||
id: int
|
||||
tenant_id: int
|
||||
serial_number: str | None
|
||||
provisioning_method: ProvisioningMethod
|
||||
vpn_ip: str | None
|
||||
vpn_cert_cn: str | None
|
||||
is_online: bool
|
||||
is_provisioned: bool
|
||||
last_seen: datetime | None
|
||||
created_at: datetime
|
||||
updated_at: datetime | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class GatewayStatus(BaseModel):
|
||||
"""Schema for gateway online status."""
|
||||
id: int
|
||||
name: str
|
||||
is_online: bool
|
||||
last_seen: datetime | None
|
||||
vpn_ip: str | None
|
||||
@@ -0,0 +1,33 @@
|
||||
"""Tenant-related Pydantic schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TenantBase(BaseModel):
|
||||
"""Base tenant schema."""
|
||||
name: str = Field(..., min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class TenantCreate(TenantBase):
|
||||
"""Schema for creating a tenant."""
|
||||
pass
|
||||
|
||||
|
||||
class TenantUpdate(BaseModel):
|
||||
"""Schema for updating a tenant."""
|
||||
name: str | None = Field(None, min_length=2, max_length=255)
|
||||
description: str | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
class TenantResponse(TenantBase):
|
||||
"""Schema for tenant response."""
|
||||
id: int
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
@@ -0,0 +1,63 @@
|
||||
"""User-related Pydantic schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
from ..models.user import UserRole
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
"""Base user schema with common fields."""
|
||||
username: str = Field(..., min_length=3, max_length=255)
|
||||
email: EmailStr
|
||||
full_name: str | None = None
|
||||
role: UserRole = UserRole.TECHNICIAN
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
"""Schema for creating a new user."""
|
||||
password: str = Field(..., min_length=8)
|
||||
tenant_id: int | None = None
|
||||
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
"""Schema for updating a user."""
|
||||
email: EmailStr | None = None
|
||||
full_name: str | None = None
|
||||
role: UserRole | None = None
|
||||
is_active: bool | None = None
|
||||
password: str | None = Field(None, min_length=8)
|
||||
|
||||
|
||||
class UserResponse(UserBase):
|
||||
"""Schema for user response."""
|
||||
id: int
|
||||
tenant_id: int | None
|
||||
is_active: bool
|
||||
last_login: datetime | None
|
||||
created_at: datetime
|
||||
updated_at: datetime | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
"""Schema for login request."""
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
"""Schema for JWT token response."""
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class TokenPayload(BaseModel):
|
||||
"""Schema for decoded JWT payload."""
|
||||
sub: int # user_id
|
||||
username: str
|
||||
role: str
|
||||
tenant_id: int | None
|
||||
exp: datetime
|
||||
Reference in New Issue
Block a user