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