Files
gatehouse-api/gatehouse_app/models/zerotier/device.py
T

80 lines
2.7 KiB
Python

"""Device model — a user-registered ZeroTier node endpoint."""
from gatehouse_app.extensions import db
from gatehouse_app.models.base import BaseModel
from gatehouse_app.utils.constants import ApprovalState, DeviceStatus
class Device(BaseModel):
"""A user-owned endpoint identified by a ZeroTier Node ID.
A user registers their device in the portal using the 10-character ZeroTier
Node ID visible in the ZeroTier client. One active device record per
node_id at a time (unique constraint excludes soft-deleted duplicates).
Attributes:
user_id: FK to the owning user
organization_id: FK to the organization this device is registered in
node_id: 10-char hex ZeroTier node / device address
device_nickname: User-assigned friendly name
hostname: Device hostname reported by the client
asset_tag: Corporate asset tag if available
serial_number: Device serial number if available
status: active / inactive
"""
__tablename__ = "devices"
user_id = db.Column(
db.String(36),
db.ForeignKey("users.id"),
nullable=False,
index=True,
)
organization_id = db.Column(
db.String(36),
db.ForeignKey("organizations.id"),
nullable=False,
index=True,
)
node_id = db.Column(
db.String(10),
nullable=False,
index=True,
)
device_nickname = db.Column(db.String(255), nullable=True)
hostname = db.Column(db.String(255), nullable=True)
asset_tag = db.Column(db.String(255), nullable=True)
serial_number = db.Column(db.String(255), nullable=True)
status = db.Column(
db.Enum(DeviceStatus, name="device_status", values_callable=lambda x: [e.value for e in x]),
default=DeviceStatus.ACTIVE,
nullable=False,
)
# Relationships
user = db.relationship("User", backref="devices")
organization = db.relationship("Organization", backref="devices")
network_access_requests = db.relationship(
"NetworkAccessRequest",
back_populates="device",
cascade="all, delete-orphan",
)
def __repr__(self):
return f"<Device {self.node_id} ({self.device_nickname or 'unnamed'})>"
@property
def display_name(self) -> str:
return self.device_nickname or self.hostname or self.node_id
def to_dict(self, exclude=None):
exclude = exclude or []
data = super().to_dict(exclude=exclude)
data["display_name"] = self.display_name
data["active_membership_count"] = sum(
1 for r in self.network_access_requests
if r.active and r.status == ApprovalState.APPROVED and r.deleted_at is None
)
return data