"""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 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"), default=DeviceStatus.ACTIVE, nullable=False, ) # Relationships user = db.relationship("User", backref="devices") organization = db.relationship("Organization", backref="devices") memberships = db.relationship( "DeviceNetworkMembership", back_populates="device", cascade="all, delete-orphan", ) def __repr__(self): return f"" @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 m in self.memberships if m.state == "active_authorized" and m.deleted_at is None ) return data