Poll loop now deletes DB rows for players no longer returned by the Yodeck API, and Zabbix sync deletes the corresponding hosts from the Yodeck Players group. Both actions are reflected in the activity log. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
5.3 KiB
Python
161 lines
5.3 KiB
Python
import os
|
|
import sqlite3
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from app.config import DB_PATH
|
|
|
|
|
|
def _conn():
|
|
conn = sqlite3.connect(DB_PATH)
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
|
|
|
|
def init_db():
|
|
os.makedirs(os.path.dirname(os.path.abspath(DB_PATH)), exist_ok=True)
|
|
conn = _conn()
|
|
conn.executescript('''
|
|
CREATE TABLE IF NOT EXISTS players (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
online INTEGER NOT NULL DEFAULT 0,
|
|
last_seen TEXT,
|
|
updating INTEGER NOT NULL DEFAULT 0,
|
|
registered INTEGER NOT NULL DEFAULT 0,
|
|
workspace_name TEXT,
|
|
player_type TEXT,
|
|
updated_at TEXT NOT NULL,
|
|
last_pushed TEXT,
|
|
last_ip_address TEXT,
|
|
status_last_updated TEXT,
|
|
screen_resolution TEXT,
|
|
hardware_version TEXT,
|
|
hostname TEXT,
|
|
eth0_ip TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp TEXT NOT NULL,
|
|
event_type TEXT NOT NULL,
|
|
message TEXT NOT NULL,
|
|
details TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs(timestamp DESC);
|
|
''')
|
|
# Migrate existing databases that predate the new columns
|
|
new_cols = [
|
|
('last_pushed', 'TEXT'),
|
|
('last_ip_address', 'TEXT'),
|
|
('status_last_updated', 'TEXT'),
|
|
('screen_resolution', 'TEXT'),
|
|
('hardware_version', 'TEXT'),
|
|
('hostname', 'TEXT'),
|
|
('eth0_ip', 'TEXT'),
|
|
]
|
|
for col, typ in new_cols:
|
|
try:
|
|
conn.execute(f'ALTER TABLE players ADD COLUMN {col} {typ}')
|
|
except Exception:
|
|
pass # column already exists
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def upsert_player(p):
|
|
state = p.get('state', {})
|
|
workspace = p.get('workspace', {})
|
|
ps = p.get('player_status') or {}
|
|
res = ps.get('screen_resolution')
|
|
eth0 = (ps.get('public_ip') or {}).get('eth0') or {}
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
conn = _conn()
|
|
conn.execute('''
|
|
INSERT INTO players (id, name, online, last_seen, updating, registered,
|
|
workspace_name, player_type, updated_at,
|
|
last_pushed, last_ip_address, status_last_updated,
|
|
screen_resolution, hardware_version, hostname, eth0_ip)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT(id) DO UPDATE SET
|
|
name = excluded.name,
|
|
online = excluded.online,
|
|
last_seen = excluded.last_seen,
|
|
updating = excluded.updating,
|
|
registered = excluded.registered,
|
|
workspace_name = excluded.workspace_name,
|
|
player_type = excluded.player_type,
|
|
updated_at = excluded.updated_at,
|
|
last_pushed = excluded.last_pushed,
|
|
last_ip_address = excluded.last_ip_address,
|
|
status_last_updated = excluded.status_last_updated,
|
|
screen_resolution = excluded.screen_resolution,
|
|
hardware_version = excluded.hardware_version,
|
|
hostname = excluded.hostname,
|
|
eth0_ip = excluded.eth0_ip
|
|
''', (
|
|
p['id'],
|
|
p['name'],
|
|
1 if state.get('online') else 0,
|
|
state.get('last_seen'),
|
|
1 if state.get('updating') else 0,
|
|
1 if state.get('registered') else 0,
|
|
workspace.get('name'),
|
|
p.get('player_type'),
|
|
now,
|
|
p.get('last_pushed'),
|
|
p.get('last_ip_address'),
|
|
ps.get('status_last_updated'),
|
|
f"{res[0]}x{res[1]}" if isinstance(res, list) and len(res) == 2 else None,
|
|
ps.get('hardware_version'),
|
|
ps.get('hostname'),
|
|
eth0.get('ip_v4'),
|
|
))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def delete_players_not_in(ids):
|
|
if not ids:
|
|
return 0
|
|
placeholders = ','.join('?' * len(ids))
|
|
conn = _conn()
|
|
cur = conn.execute(f'DELETE FROM players WHERE id NOT IN ({placeholders})', ids)
|
|
deleted = cur.rowcount
|
|
conn.commit()
|
|
conn.close()
|
|
return deleted
|
|
|
|
|
|
def get_all_players():
|
|
conn = _conn()
|
|
rows = conn.execute('SELECT * FROM players ORDER BY name').fetchall()
|
|
conn.close()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
def get_player_counts():
|
|
conn = _conn()
|
|
total = conn.execute('SELECT COUNT(*) FROM players').fetchone()[0]
|
|
online = conn.execute('SELECT COUNT(*) FROM players WHERE online = 1').fetchone()[0]
|
|
conn.close()
|
|
return total, online
|
|
|
|
|
|
def add_log(event_type, message, details=None):
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
conn = _conn()
|
|
conn.execute(
|
|
'INSERT INTO logs (timestamp, event_type, message, details) VALUES (?, ?, ?, ?)',
|
|
(now, event_type, message, json.dumps(details) if details else None),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def get_recent_logs(limit=200):
|
|
conn = _conn()
|
|
rows = conn.execute('SELECT * FROM logs ORDER BY id DESC LIMIT ?', (limit,)).fetchall()
|
|
conn.close()
|
|
return [dict(r) for r in rows]
|