first commit

This commit is contained in:
Stefan Hacker
2026-02-02 09:46:35 +01:00
commit 6901dc369b
98 changed files with 13030 additions and 0 deletions
+25
View File
@@ -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",
]
+76
View File
@@ -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
+82
View File
@@ -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
+33
View File
@@ -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
+63
View File
@@ -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