Initial commit: enterprise digital platform with portal SSO, DigiServer, IT Assets, NetworkView, Server Monitor
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""Player model for digital signage players."""
|
||||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
|
||||
from app.extensions import db
|
||||
|
||||
|
||||
class Player(db.Model):
|
||||
"""Player model representing a digital signage device.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
name: Display name for the player
|
||||
hostname: Unique hostname/identifier for the player
|
||||
location: Physical location description
|
||||
auth_code: Authentication code for API access (legacy)
|
||||
password_hash: Hashed password for player authentication
|
||||
quickconnect_code: Hashed quick connect code for easy pairing
|
||||
orientation: Display orientation (Landscape/Portrait)
|
||||
status: Current player status (online, offline, error)
|
||||
last_seen: Last activity timestamp
|
||||
playlist_version: Version number for playlist synchronization
|
||||
created_at: Player creation timestamp
|
||||
"""
|
||||
__tablename__ = 'player'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), nullable=False)
|
||||
hostname = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||
location = db.Column(db.String(255), nullable=True)
|
||||
auth_code = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||
password_hash = db.Column(db.String(255), nullable=False)
|
||||
quickconnect_code = db.Column(db.String(255), nullable=True)
|
||||
orientation = db.Column(db.String(16), default='Landscape', nullable=False)
|
||||
status = db.Column(db.String(50), default='offline', index=True)
|
||||
last_seen = db.Column(db.DateTime, nullable=True, index=True)
|
||||
last_heartbeat = db.Column(db.DateTime, nullable=True, index=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
# Playlist assignment
|
||||
playlist_id = db.Column(db.Integer, db.ForeignKey('playlist.id', ondelete='SET NULL'),
|
||||
nullable=True, index=True)
|
||||
|
||||
# Relationships
|
||||
playlist = db.relationship('Playlist', back_populates='players')
|
||||
feedback = db.relationship('PlayerFeedback', back_populates='player',
|
||||
cascade='all, delete-orphan', lazy='dynamic')
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""String representation of Player."""
|
||||
return f'<Player {self.name} (ID={self.id}, Status={self.status})>'
|
||||
|
||||
@property
|
||||
def is_online(self) -> bool:
|
||||
"""Check if player is online (seen in last 5 minutes)."""
|
||||
if not self.last_seen:
|
||||
return False
|
||||
delta = datetime.utcnow() - self.last_seen
|
||||
return delta.total_seconds() < 300 # 5 minutes
|
||||
|
||||
def update_status(self, status: str) -> None:
|
||||
"""Update player status and last seen timestamp.
|
||||
|
||||
Args:
|
||||
status: New status value
|
||||
"""
|
||||
self.status = status
|
||||
self.last_seen = datetime.utcnow()
|
||||
|
||||
def set_password(self, password: str) -> None:
|
||||
"""Set player password with bcrypt hashing.
|
||||
|
||||
Args:
|
||||
password: Plain text password
|
||||
"""
|
||||
from app.extensions import bcrypt
|
||||
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||
|
||||
def check_password(self, password: str) -> bool:
|
||||
"""Verify player password.
|
||||
|
||||
Args:
|
||||
password: Plain text password to check
|
||||
|
||||
Returns:
|
||||
True if password matches, False otherwise
|
||||
"""
|
||||
from app.extensions import bcrypt
|
||||
return bcrypt.check_password_hash(self.password_hash, password)
|
||||
|
||||
def set_quickconnect_code(self, code: str) -> None:
|
||||
"""Set quick connect code with bcrypt hashing.
|
||||
|
||||
Args:
|
||||
code: Plain text quick connect code
|
||||
"""
|
||||
from app.extensions import bcrypt
|
||||
self.quickconnect_code = bcrypt.generate_password_hash(code).decode('utf-8')
|
||||
|
||||
def check_quickconnect_code(self, code: str) -> bool:
|
||||
"""Verify quick connect code.
|
||||
|
||||
Args:
|
||||
code: Plain text code to check
|
||||
|
||||
Returns:
|
||||
True if code matches, False otherwise
|
||||
"""
|
||||
if not self.quickconnect_code:
|
||||
return False
|
||||
from app.extensions import bcrypt
|
||||
return bcrypt.check_password_hash(self.quickconnect_code, code)
|
||||
|
||||
@staticmethod
|
||||
def authenticate(hostname: str, password: str = None, quickconnect_code: str = None) -> Optional['Player']:
|
||||
"""Authenticate a player by hostname and password or quickconnect code.
|
||||
|
||||
Args:
|
||||
hostname: Player hostname
|
||||
password: Player password (optional if using quickconnect)
|
||||
quickconnect_code: Quick connect code (optional if using password)
|
||||
|
||||
Returns:
|
||||
Player instance if authentication successful, None otherwise
|
||||
"""
|
||||
player = Player.query.filter_by(hostname=hostname).first()
|
||||
if not player:
|
||||
return None
|
||||
|
||||
# Try password authentication first
|
||||
if password and player.check_password(password):
|
||||
return player
|
||||
|
||||
# Try quickconnect code authentication
|
||||
if quickconnect_code and player.check_quickconnect_code(quickconnect_code):
|
||||
return player
|
||||
|
||||
return None
|
||||
Reference in New Issue
Block a user