37c963cf96
- Add resolveNodeColors() utility merging type defaults with per-node overrides - Default colors per node type (cyan=isp/router/lxc/ap, green=switch/nas, purple=server/vm, orange=proxmox, amber=iot, gray=generic) - Remove glowColor prop from BaseNode — colors now come from node data - ProxmoxGroupNode uses resolveNodeColors for group border/header/icon - NodeModal: Appearance section with 3 color swatches (border, background, icon) — click swatch to open native color picker; Reset to defaults button - custom_colors persisted as JSON in DB (backend model + schemas + migration) - 7 new unit tests for resolveNodeColors covering all node types + partial overrides
92 lines
4.0 KiB
Python
92 lines
4.0 KiB
Python
import uuid
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import JSON, Boolean, DateTime, Float, ForeignKey, Integer, String, Text
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.db.database import Base
|
|
|
|
|
|
def _now() -> datetime:
|
|
return datetime.now(UTC)
|
|
|
|
|
|
def _uuid() -> str:
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
class Node(Base):
|
|
__tablename__ = "nodes"
|
|
|
|
id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid)
|
|
type: Mapped[str] = mapped_column(String, nullable=False)
|
|
label: Mapped[str] = mapped_column(String, nullable=False)
|
|
hostname: Mapped[str | None] = mapped_column(String)
|
|
ip: Mapped[str | None] = mapped_column(String)
|
|
mac: Mapped[str | None] = mapped_column(String)
|
|
os: Mapped[str | None] = mapped_column(String)
|
|
status: Mapped[str] = mapped_column(String, default="unknown")
|
|
check_method: Mapped[str | None] = mapped_column(String)
|
|
check_target: Mapped[str | None] = mapped_column(String)
|
|
services: Mapped[list] = mapped_column(JSON, default=list)
|
|
notes: Mapped[str | None] = mapped_column(Text)
|
|
pos_x: Mapped[float] = mapped_column(Float, default=0)
|
|
pos_y: Mapped[float] = mapped_column(Float, default=0)
|
|
parent_id: Mapped[str | None] = mapped_column(String, ForeignKey("nodes.id"))
|
|
container_mode: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
custom_colors: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
last_seen: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
response_time_ms: Mapped[int | None] = mapped_column(Integer)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now, onupdate=_now)
|
|
|
|
children: Mapped[list["Node"]] = relationship("Node", back_populates="parent")
|
|
parent: Mapped["Node | None"] = relationship("Node", back_populates="children", remote_side=[id])
|
|
|
|
|
|
class Edge(Base):
|
|
__tablename__ = "edges"
|
|
|
|
id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid)
|
|
source: Mapped[str] = mapped_column(String, ForeignKey("nodes.id", ondelete="CASCADE"))
|
|
target: Mapped[str] = mapped_column(String, ForeignKey("nodes.id", ondelete="CASCADE"))
|
|
type: Mapped[str] = mapped_column(String, default="ethernet")
|
|
label: Mapped[str | None] = mapped_column(String)
|
|
vlan_id: Mapped[int | None] = mapped_column(Integer)
|
|
speed: Mapped[str | None] = mapped_column(String)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
|
|
|
|
class CanvasState(Base):
|
|
__tablename__ = "canvas_state"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)
|
|
viewport: Mapped[dict] = mapped_column(JSON, default=dict)
|
|
saved_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
|
|
|
|
class PendingDevice(Base):
|
|
__tablename__ = "pending_devices"
|
|
|
|
id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid)
|
|
ip: Mapped[str] = mapped_column(String, nullable=False)
|
|
mac: Mapped[str | None] = mapped_column(String)
|
|
hostname: Mapped[str | None] = mapped_column(String)
|
|
os: Mapped[str | None] = mapped_column(String)
|
|
services: Mapped[list] = mapped_column(JSON, default=list)
|
|
suggested_type: Mapped[str | None] = mapped_column(String)
|
|
status: Mapped[str] = mapped_column(String, default="pending")
|
|
discovered_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
|
|
|
|
class ScanRun(Base):
|
|
__tablename__ = "scan_runs"
|
|
|
|
id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid)
|
|
status: Mapped[str] = mapped_column(String, default="running")
|
|
ranges: Mapped[list] = mapped_column(JSON, default=list)
|
|
devices_found: Mapped[int] = mapped_column(Integer, default=0)
|
|
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
|
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
error: Mapped[str | None] = mapped_column(Text)
|