- 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)
223 lines
8.9 KiB
Python
223 lines
8.9 KiB
Python
import uuid
|
|
from datetime import datetime, date
|
|
from sqlalchemy import (
|
|
Column, String, Text, Boolean, Integer, Numeric, Date,
|
|
DateTime, ForeignKey, Enum as SAEnum, UniqueConstraint, Index
|
|
)
|
|
from sqlalchemy.dialects.postgresql import UUID, INET, MACADDR, JSONB, ARRAY
|
|
from sqlalchemy.orm import relationship
|
|
from app.database import Base
|
|
|
|
|
|
class Location(Base):
|
|
__tablename__ = "locations"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
name = Column(Text, nullable=False, unique=True)
|
|
description = Column(Text)
|
|
parent_id = Column(UUID(as_uuid=True), ForeignKey("locations.id"), nullable=True)
|
|
metadata_ = Column("metadata", JSONB, default={})
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
username = Column(Text, nullable=False, unique=True)
|
|
email = Column(Text, nullable=False, unique=True)
|
|
full_name = Column(Text)
|
|
role = Column(SAEnum("admin", "editor", "viewer", name="user_role"), nullable=False, default="viewer")
|
|
team = Column(Text)
|
|
is_active = Column(Boolean, nullable=False, default=True)
|
|
password_hash = Column(Text, nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class CIClass(Base):
|
|
__tablename__ = "ci_classes"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
name = Column(Text, nullable=False, unique=True)
|
|
description = Column(Text)
|
|
parent_id = Column(UUID(as_uuid=True), ForeignKey("ci_classes.id"), nullable=True)
|
|
icon = Column(Text)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class CIType(Base):
|
|
__tablename__ = "ci_types"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
class_id = Column(UUID(as_uuid=True), ForeignKey("ci_classes.id"), nullable=False)
|
|
name = Column(Text, nullable=False)
|
|
description = Column(Text)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("class_id", "name"),
|
|
)
|
|
|
|
|
|
class ConfigurationItem(Base):
|
|
__tablename__ = "configuration_items"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_type_id = Column(UUID(as_uuid=True), ForeignKey("ci_types.id"), nullable=False)
|
|
name = Column(Text, nullable=False)
|
|
description = Column(Text)
|
|
status = Column(
|
|
SAEnum("active", "inactive", "maintenance", "deprecated", "planned", name="ci_status"),
|
|
nullable=False, default="active"
|
|
)
|
|
location_id = Column(UUID(as_uuid=True), ForeignKey("locations.id"), nullable=True)
|
|
serial_number = Column(Text)
|
|
asset_tag = Column(Text)
|
|
purchase_date = Column(Date)
|
|
warranty_expiry = Column(Date)
|
|
attributes = Column(JSONB, default={})
|
|
tags = Column(ARRAY(Text), default=[])
|
|
created_by = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
version = Column(Integer, nullable=False, default=1)
|
|
|
|
ci_type = relationship("CIType", lazy="joined")
|
|
location = relationship("Location", lazy="joined")
|
|
created_by_user = relationship("User", lazy="joined")
|
|
|
|
__table_args__ = (
|
|
Index("idx_ci_name_type_active", "name", "ci_type_id", unique=True,
|
|
postgresql_where="deleted_at IS NULL"),
|
|
)
|
|
|
|
|
|
class IPAddress(Base):
|
|
__tablename__ = "ip_addresses"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
ip_address = Column(INET, nullable=False)
|
|
subnet_mask = Column(INET)
|
|
gateway = Column(INET)
|
|
dns_servers = Column(ARRAY(INET))
|
|
is_primary = Column(Boolean, nullable=False, default=False)
|
|
vlan_id = Column(Integer)
|
|
dhcp_enabled = Column(Boolean, default=False)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class NetworkInterface(Base):
|
|
__tablename__ = "network_interfaces"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
name = Column(Text, nullable=False)
|
|
mac_address = Column(MACADDR)
|
|
speed_mbps = Column(Integer)
|
|
interface_type = Column(Text, default="ethernet")
|
|
is_up = Column(Boolean, default=True)
|
|
ip_address_id = Column(UUID(as_uuid=True), ForeignKey("ip_addresses.id"), nullable=True)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class HardwareDetail(Base):
|
|
__tablename__ = "hardware_details"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
manufacturer = Column(Text)
|
|
model = Column(Text)
|
|
cpu_model = Column(Text)
|
|
cpu_cores = Column(Integer)
|
|
ram_gb = Column(Numeric(10, 2))
|
|
storage_gb = Column(Numeric(10, 2))
|
|
storage_type = Column(Text)
|
|
form_factor = Column(Text)
|
|
power_supply = Column(Text)
|
|
bios_version = Column(Text)
|
|
serial_number = Column(Text)
|
|
specs = Column(JSONB, default={})
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class SoftwareInstance(Base):
|
|
__tablename__ = "software_instances"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
name = Column(Text, nullable=False)
|
|
vendor = Column(Text)
|
|
version = Column(Text)
|
|
license_key = Column(Text)
|
|
license_type = Column(Text)
|
|
install_path = Column(Text)
|
|
config_path = Column(Text)
|
|
port = Column(Integer)
|
|
protocol = Column(Text)
|
|
start_command = Column(Text)
|
|
service_user = Column(Text)
|
|
config = Column(JSONB, default={})
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
|
class CIRelationship(Base):
|
|
__tablename__ = "ci_relationships"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
source_ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
target_ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
relationship = Column(
|
|
SAEnum("depends_on", "connected_to", "hosted_on", "runs_on",
|
|
"manages", "contains", "part_of", "related_to", name="relationship_type"),
|
|
nullable=False
|
|
)
|
|
description = Column(Text)
|
|
metadata_ = Column("metadata", JSONB, default={})
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
source_ci = relationship("ConfigurationItem", foreign_keys=[source_ci_id], lazy="joined")
|
|
target_ci = relationship("ConfigurationItem", foreign_keys=[target_ci_id], lazy="joined")
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("source_ci_id", "target_ci_id", "relationship"),
|
|
)
|
|
|
|
|
|
class ChangeLog(Base):
|
|
__tablename__ = "changelog"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
ci_id = Column(UUID(as_uuid=True), ForeignKey("configuration_items.id"), nullable=False)
|
|
action = Column(
|
|
SAEnum("create", "update", "delete", "restore",
|
|
"relationship_add", "relationship_remove", name="change_action"),
|
|
nullable=False
|
|
)
|
|
changed_by = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
|
field_name = Column(Text)
|
|
old_value = Column(Text)
|
|
new_value = Column(Text)
|
|
snapshot = Column(JSONB)
|
|
version = Column(Integer, nullable=False)
|
|
comment = Column(Text)
|
|
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|