Files
----/backend/app/schemas/schemas.py
smolkik-code 9b15f8b09c feat: CMDB full-stack app - FastAPI + PostgreSQL + React
- PostgreSQL schema: 14 tables, JSONB attributes, audit triggers, soft delete
- FastAPI backend: CRUD, search/filter, relationship graph, bulk import, JWT RBAC
- React frontend: CI table, detail card, force-graph, dashboard
- Seed data: homelab scenario (Proxmox, Mikrotik, VMs, services)
- Docker Compose + Kubernetes manifests
- 20 backend tests (pytest + httpx)
2026-06-25 13:01:40 +07:00

315 lines
8.6 KiB
Python

from __future__ import annotations
from datetime import date, datetime
from uuid import UUID
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional
# ── Auth ────────────────────────────────────────────────
class UserCreate(BaseModel):
username: str
email: str
full_name: Optional[str] = None
role: str = "viewer"
team: Optional[str] = None
password: str
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
username: str
email: str
full_name: Optional[str]
role: str
team: Optional[str]
is_active: bool
created_at: datetime
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
class LoginRequest(BaseModel):
username: str
password: str
# ── Locations ───────────────────────────────────────────
class LocationCreate(BaseModel):
name: str
description: Optional[str] = None
parent_id: Optional[UUID] = None
metadata: dict = {}
class LocationResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
name: str
description: Optional[str]
parent_id: Optional[UUID]
metadata: dict
created_at: datetime
# ── CI Classes / Types ──────────────────────────────────
class CIClassCreate(BaseModel):
name: str
description: Optional[str] = None
parent_id: Optional[UUID] = None
icon: Optional[str] = None
class CIClassResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
name: str
description: Optional[str]
parent_id: Optional[UUID]
icon: Optional[str]
class CITypeCreate(BaseModel):
class_id: UUID
name: str
description: Optional[str] = None
class CITypeResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
class_id: UUID
name: str
description: Optional[str]
# ── Configuration Item ──────────────────────────────────
class CICreate(BaseModel):
ci_type_id: UUID
name: str
description: Optional[str] = None
status: str = "active"
location_id: Optional[UUID] = None
serial_number: Optional[str] = None
asset_tag: Optional[str] = None
purchase_date: Optional[date] = None
warranty_expiry: Optional[date] = None
attributes: dict = {}
tags: list[str] = []
class CIUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
status: Optional[str] = None
location_id: Optional[UUID] = None
serial_number: Optional[str] = None
asset_tag: Optional[str] = None
purchase_date: Optional[date] = None
warranty_expiry: Optional[date] = None
attributes: Optional[dict] = None
tags: Optional[list[str]] = None
class CIResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_type_id: UUID
ci_type: Optional[CITypeResponse] = None
name: str
description: Optional[str]
status: str
location_id: Optional[UUID]
location: Optional[LocationResponse] = None
serial_number: Optional[str]
asset_tag: Optional[str]
purchase_date: Optional[date]
warranty_expiry: Optional[date]
attributes: dict
tags: list[str]
version: int
created_at: datetime
updated_at: datetime
class CIDetailResponse(CIResponse):
ip_addresses: list[IPAddressResponse] = []
network_interfaces: list[NetworkInterfaceResponse] = []
hardware_detail: Optional[HardwareDetailResponse] = None
software_instances: list[SoftwareInstanceResponse] = []
relationships_out: list[RelationshipResponse] = []
relationships_in: list[RelationshipResponse] = []
owners: list[OwnerResponse] = []
# ── IP Address ──────────────────────────────────────────
class IPAddressCreate(BaseModel):
ip_address: str
subnet_mask: Optional[str] = None
gateway: Optional[str] = None
is_primary: bool = False
vlan_id: Optional[int] = None
class IPAddressResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_id: UUID
ip_address: str
subnet_mask: Optional[str]
gateway: Optional[str]
is_primary: bool
vlan_id: Optional[int]
# ── Network Interface ───────────────────────────────────
class NetworkInterfaceCreate(BaseModel):
name: str
mac_address: Optional[str] = None
speed_mbps: Optional[int] = None
interface_type: str = "ethernet"
is_up: bool = True
ip_address_id: Optional[UUID] = None
class NetworkInterfaceResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_id: UUID
name: str
mac_address: Optional[str]
speed_mbps: Optional[int]
interface_type: str
is_up: bool
ip_address_id: Optional[UUID]
# ── Hardware Detail ─────────────────────────────────────
class HardwareDetailCreate(BaseModel):
manufacturer: Optional[str] = None
model: Optional[str] = None
cpu_model: Optional[str] = None
cpu_cores: Optional[int] = None
ram_gb: Optional[float] = None
storage_gb: Optional[float] = None
storage_type: Optional[str] = None
form_factor: Optional[str] = None
specs: dict = {}
class HardwareDetailResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_id: UUID
manufacturer: Optional[str]
model: Optional[str]
cpu_model: Optional[str]
cpu_cores: Optional[int]
ram_gb: Optional[float]
storage_gb: Optional[float]
storage_type: Optional[str]
form_factor: Optional[str]
specs: dict
# ── Software Instance ───────────────────────────────────
class SoftwareInstanceCreate(BaseModel):
name: str
vendor: Optional[str] = None
version: Optional[str] = None
port: Optional[int] = None
protocol: Optional[str] = None
config: dict = {}
class SoftwareInstanceResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_id: UUID
name: str
vendor: Optional[str]
version: Optional[str]
port: Optional[int]
protocol: Optional[str]
config: dict
# ── Relationships ───────────────────────────────────────
class RelationshipCreate(BaseModel):
source_ci_id: UUID
target_ci_id: UUID
relationship: str
description: Optional[str] = None
metadata: dict = {}
class RelationshipResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
source_ci_id: UUID
target_ci_id: UUID
relationship: str
description: Optional[str]
metadata: dict
created_at: datetime
# ── Owner ───────────────────────────────────────────────
class OwnerCreate(BaseModel):
user_id: UUID
role: str = "owner"
class OwnerResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
ci_id: UUID
user_id: UUID
role: str
# ── Pagination ──────────────────────────────────────────
class PaginatedResponse(BaseModel):
items: list
total: int
page: int
page_size: int
pages: int
# ── Graph ───────────────────────────────────────────────
class GraphNode(BaseModel):
id: str
label: str
group: str
status: str
class GraphEdge(BaseModel):
source: str
target: str
label: str
class GraphData(BaseModel):
nodes: list[GraphNode]
edges: list[GraphEdge]
# ── Bulk Import ─────────────────────────────────────────
class BulkImportItem(BaseModel):
name: str
ci_type_name: str
description: Optional[str] = None
status: str = "active"
location_name: Optional[str] = None
attributes: dict = {}
tags: list[str] = []
class BulkImportRequest(BaseModel):
items: list[BulkImportItem]