mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-05 20:03:48 +00:00
Update notification service
This commit is contained in:
@@ -55,9 +55,9 @@ class HealthPersistence:
|
||||
|
||||
def _get_conn(self) -> sqlite3.Connection:
|
||||
"""Get a SQLite connection with timeout and WAL mode for safe concurrency."""
|
||||
conn = sqlite3.connect(str(self.db_path), timeout=10)
|
||||
conn = sqlite3.connect(str(self.db_path), timeout=30)
|
||||
conn.execute('PRAGMA journal_mode=WAL')
|
||||
conn.execute('PRAGMA busy_timeout=5000')
|
||||
conn.execute('PRAGMA busy_timeout=10000')
|
||||
return conn
|
||||
|
||||
@contextmanager
|
||||
@@ -1327,7 +1327,7 @@ class HealthPersistence:
|
||||
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
# Disk Observations API
|
||||
# ──────────────────────<EFBFBD><EFBFBD><EFBFBD>─────────────────────────────────────────
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
|
||||
def register_disk(self, device_name: str, serial: Optional[str] = None,
|
||||
model: Optional[str] = None, size_bytes: Optional[int] = None):
|
||||
@@ -1340,56 +1340,57 @@ class HealthPersistence:
|
||||
under 'ata8' and we now know the real block device is 'sdh' with
|
||||
serial 'WX72...', update the old entry so observations are linked.
|
||||
"""
|
||||
now = datetime.now().isoformat()
|
||||
try:
|
||||
conn = self._get_conn()
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Consolidate: if serial is known and an old entry exists with
|
||||
# a different device_name (e.g. 'ata8' instead of 'sdh'),
|
||||
# update that entry's device_name so observations carry over.
|
||||
if serial:
|
||||
with self._db_lock:
|
||||
now = datetime.now().isoformat()
|
||||
try:
|
||||
conn = self._get_conn()
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Consolidate: if serial is known and an old entry exists with
|
||||
# a different device_name (e.g. 'ata8' instead of 'sdh'),
|
||||
# update that entry's device_name so observations carry over.
|
||||
if serial:
|
||||
cursor.execute('''
|
||||
SELECT id, device_name FROM disk_registry
|
||||
WHERE serial = ? AND serial != '' AND device_name != ?
|
||||
''', (serial, device_name))
|
||||
old_rows = cursor.fetchall()
|
||||
for old_id, old_dev in old_rows:
|
||||
# Only consolidate ATA names -> block device names
|
||||
if old_dev.startswith('ata') and not device_name.startswith('ata'):
|
||||
# Check if target (device_name, serial) already exists
|
||||
cursor.execute(
|
||||
'SELECT id FROM disk_registry WHERE device_name = ? AND serial = ?',
|
||||
(device_name, serial))
|
||||
existing = cursor.fetchone()
|
||||
if existing:
|
||||
# Merge: move observations from old -> existing, then delete old
|
||||
cursor.execute(
|
||||
'UPDATE disk_observations SET disk_registry_id = ? WHERE disk_registry_id = ?',
|
||||
(existing[0], old_id))
|
||||
cursor.execute('DELETE FROM disk_registry WHERE id = ?', (old_id,))
|
||||
else:
|
||||
# Rename the old entry to the real block device name
|
||||
cursor.execute(
|
||||
'UPDATE disk_registry SET device_name = ?, model = COALESCE(?, model), '
|
||||
'size_bytes = COALESCE(?, size_bytes), last_seen = ?, removed = 0 '
|
||||
'WHERE id = ?',
|
||||
(device_name, model, size_bytes, now, old_id))
|
||||
|
||||
cursor.execute('''
|
||||
SELECT id, device_name FROM disk_registry
|
||||
WHERE serial = ? AND serial != '' AND device_name != ?
|
||||
''', (serial, device_name))
|
||||
old_rows = cursor.fetchall()
|
||||
for old_id, old_dev in old_rows:
|
||||
# Only consolidate ATA names -> block device names
|
||||
if old_dev.startswith('ata') and not device_name.startswith('ata'):
|
||||
# Check if target (device_name, serial) already exists
|
||||
cursor.execute(
|
||||
'SELECT id FROM disk_registry WHERE device_name = ? AND serial = ?',
|
||||
(device_name, serial))
|
||||
existing = cursor.fetchone()
|
||||
if existing:
|
||||
# Merge: move observations from old -> existing, then delete old
|
||||
cursor.execute(
|
||||
'UPDATE disk_observations SET disk_registry_id = ? WHERE disk_registry_id = ?',
|
||||
(existing[0], old_id))
|
||||
cursor.execute('DELETE FROM disk_registry WHERE id = ?', (old_id,))
|
||||
else:
|
||||
# Rename the old entry to the real block device name
|
||||
cursor.execute(
|
||||
'UPDATE disk_registry SET device_name = ?, model = COALESCE(?, model), '
|
||||
'size_bytes = COALESCE(?, size_bytes), last_seen = ?, removed = 0 '
|
||||
'WHERE id = ?',
|
||||
(device_name, model, size_bytes, now, old_id))
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO disk_registry (device_name, serial, model, size_bytes, first_seen, last_seen, removed)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 0)
|
||||
ON CONFLICT(device_name, serial) DO UPDATE SET
|
||||
model = COALESCE(excluded.model, model),
|
||||
size_bytes = COALESCE(excluded.size_bytes, size_bytes),
|
||||
last_seen = excluded.last_seen,
|
||||
removed = 0
|
||||
''', (device_name, serial or '', model, size_bytes, now, now))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[HealthPersistence] Error registering disk {device_name}: {e}")
|
||||
INSERT INTO disk_registry (device_name, serial, model, size_bytes, first_seen, last_seen, removed)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 0)
|
||||
ON CONFLICT(device_name, serial) DO UPDATE SET
|
||||
model = COALESCE(excluded.model, model),
|
||||
size_bytes = COALESCE(excluded.size_bytes, size_bytes),
|
||||
last_seen = excluded.last_seen,
|
||||
removed = 0
|
||||
''', (device_name, serial or '', model, size_bytes, now, now))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[HealthPersistence] Error registering disk {device_name}: {e}")
|
||||
|
||||
def _get_disk_registry_id(self, cursor, device_name: str,
|
||||
serial: Optional[str] = None,
|
||||
|
||||
@@ -251,7 +251,7 @@ class TelegramChannel(NotificationChannel):
|
||||
.replace('>', '>'))
|
||||
|
||||
|
||||
# ─── Gotify ──────────────────────────────────────────────────────
|
||||
# ─── Gotify ───────────────────────────────────<EFBFBD><EFBFBD>──────────────────
|
||||
|
||||
class GotifyChannel(NotificationChannel):
|
||||
"""Gotify push notification channel with priority mapping."""
|
||||
|
||||
@@ -197,7 +197,7 @@ def capture_journal_context(keywords: list, lines: int = 30,
|
||||
return ""
|
||||
|
||||
|
||||
# ─── Journal Watcher (Real-time) ─────────────────────────────────
|
||||
# ─── Journal Watcher (Real-time) ───────────────<EFBFBD><EFBFBD>─────────────────
|
||||
|
||||
class JournalWatcher:
|
||||
"""Watches journald in real-time for critical system events.
|
||||
|
||||
@@ -1615,6 +1615,7 @@ class NotificationManager:
|
||||
'ai_openai_base_url': self._config.get('ai_openai_base_url', ''),
|
||||
'ai_prompt_mode': self._config.get('ai_prompt_mode', 'default'),
|
||||
'ai_custom_prompt': self._config.get('ai_custom_prompt', ''),
|
||||
'ai_allow_suggestions': self._config.get('ai_allow_suggestions', 'false') == 'true',
|
||||
'ai_detail_levels': ai_detail_levels,
|
||||
'hostname': self._config.get('hostname', ''),
|
||||
'webhook_secret': self._config.get('webhook_secret', ''),
|
||||
|
||||
Reference in New Issue
Block a user