mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-05-02 04:16:24 +00:00
Update notification service
This commit is contained in:
@@ -3148,7 +3148,7 @@ class HealthMonitor:
|
|||||||
if inode:
|
if inode:
|
||||||
inode_hint = 'root directory' if inode == '2' else f'inode #{inode}'
|
inode_hint = 'root directory' if inode == '2' else f'inode #{inode}'
|
||||||
reason += f'\nAffected: {inode_hint}'
|
reason += f'\nAffected: {inode_hint}'
|
||||||
reason += f'\nAction: Run "fsck /dev/{device}" (unmount first)'
|
# Note: Action/recommendations are provided by AI when AI Suggestions is enabled
|
||||||
return reason
|
return reason
|
||||||
|
|
||||||
# Out of memory
|
# Out of memory
|
||||||
|
|||||||
@@ -2154,10 +2154,12 @@ class HealthPersistence:
|
|||||||
conn = self._get_conn()
|
conn = self._get_conn()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# Get all active (non-dismissed) observations
|
# Get all active (non-dismissed) observations with device info from disk_registry
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
SELECT id, device_name, serial FROM disk_observations
|
SELECT do.id, dr.device_name, dr.serial
|
||||||
WHERE dismissed = 0
|
FROM disk_observations do
|
||||||
|
JOIN disk_registry dr ON do.disk_registry_id = dr.id
|
||||||
|
WHERE do.dismissed = 0
|
||||||
''')
|
''')
|
||||||
observations = cursor.fetchall()
|
observations = cursor.fetchall()
|
||||||
|
|
||||||
@@ -2178,6 +2180,7 @@ class HealthPersistence:
|
|||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
if dismissed_count > 0:
|
||||||
print(f"[HealthPersistence] Cleaned up {dismissed_count} orphan observations")
|
print(f"[HealthPersistence] Cleaned up {dismissed_count} orphan observations")
|
||||||
return dismissed_count
|
return dismissed_count
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -609,12 +609,10 @@ class JournalWatcher:
|
|||||||
if inode:
|
if inode:
|
||||||
inode_hint = 'root directory' if inode == '2' else f'inode #{inode}'
|
inode_hint = 'root directory' if inode == '2' else f'inode #{inode}'
|
||||||
parts.append(f'Affected: {inode_hint}')
|
parts.append(f'Affected: {inode_hint}')
|
||||||
if smart_health == 'FAILED':
|
# Note: Specific recommendations are provided by AI when AI Suggestions is enabled
|
||||||
parts.append(f'Action: Disk is failing. Run "fsck /dev/{device}" (unmount first) and plan replacement')
|
# Only include SMART status note (not an action)
|
||||||
elif smart_health == 'PASSED':
|
if smart_health == 'PASSED':
|
||||||
parts.append(f'Note: SMART reports disk is healthy. This may be a transient error.')
|
parts.append(f'Note: SMART reports disk is healthy. This may be a transient error.')
|
||||||
else:
|
|
||||||
parts.append(f'Action: Run "fsck /dev/{device}" (unmount first) and check "smartctl -a /dev/{base_dev}"')
|
|
||||||
enriched = '\n'.join(parts)
|
enriched = '\n'.join(parts)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -775,6 +775,7 @@ class NotificationManager:
|
|||||||
default_detail = 'detailed' if ch_name == 'email' else 'standard'
|
default_detail = 'detailed' if ch_name == 'email' else 'standard'
|
||||||
detail_level = self._config.get(detail_level_key, default_detail)
|
detail_level = self._config.get(detail_level_key, default_detail)
|
||||||
|
|
||||||
|
# Rich format (emojis) is a user preference per channel
|
||||||
rich_key = f'{ch_name}.rich_format'
|
rich_key = f'{ch_name}.rich_format'
|
||||||
use_rich_format = self._config.get(rich_key, 'false') == 'true'
|
use_rich_format = self._config.get(rich_key, 'false') == 'true'
|
||||||
|
|
||||||
@@ -1857,7 +1858,7 @@ class NotificationManager:
|
|||||||
return {'checked': False, 'migrated': False, 'message': str(e)}
|
return {'checked': False, 'migrated': False, 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
# ─── Singleton (for server mode) ─────────────────────────────────
|
# ─── Singleton (for server mode) ────────────<EFBFBD><EFBFBD>────────────────────
|
||||||
|
|
||||||
notification_manager = NotificationManager()
|
notification_manager = NotificationManager()
|
||||||
|
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ def _format_system_startup(data: Dict[str, Any]) -> Tuple[str, str]:
|
|||||||
return title, body
|
return title, body
|
||||||
|
|
||||||
|
|
||||||
# ─── Severity Icons ────<EFBFBD><EFBFBD><EFBFBD>─────────────────────────────────────────
|
# ─── Severity Icons ──────────────────────────────────────────────
|
||||||
|
|
||||||
SEVERITY_ICONS = {
|
SEVERITY_ICONS = {
|
||||||
'CRITICAL': '\U0001F534',
|
'CRITICAL': '\U0001F534',
|
||||||
@@ -1310,22 +1310,30 @@ def enrich_with_emojis(event_type: str, title: str, body: str,
|
|||||||
# This helps when everything comes concatenated
|
# This helps when everything comes concatenated
|
||||||
preprocessed = body
|
preprocessed = body
|
||||||
|
|
||||||
# Patterns that should start on a new line (with emoji prefix)
|
# First, clean up duplicated device references like "/dev/sda: /dev/sda: /dev/sda [SAT]"
|
||||||
|
# Convert to just "/dev/sda [SAT]" or "/dev/sda:"
|
||||||
|
preprocessed = re.sub(r'(/dev/\w+):\s*\1:\s*\1', r'\1', preprocessed)
|
||||||
|
preprocessed = re.sub(r'(/dev/\w+):\s*\1', r'\1', preprocessed)
|
||||||
|
|
||||||
|
# Patterns that should start on a new line
|
||||||
line_break_patterns = [
|
line_break_patterns = [
|
||||||
(r';\s*/dev/', '\n/dev/'), # ;/dev/sdb -> newline + /dev/sdb
|
(r';\s*/dev/', '\n/dev/'), # ;/dev/sdb -> newline + /dev/sdb
|
||||||
(r'Device:', '\nDevice:'), # Device: on new line
|
(r'(?<=[a-z])\s+/dev/', '\n/dev/'), # "sectors /dev/sdb" -> newline before /dev/
|
||||||
(r'Error:', '\nError:'), # Error: on new line
|
(r'(?<=\))\s*/dev/', '\n/dev/'), # ") /dev/sdb" -> newline before /dev/
|
||||||
(r'Action:', '\nAction:'), # Action: on new line
|
(r'\bDevice:', '\nDevice:'), # Device: on new line
|
||||||
(r'Affected:', '\nAffected:'), # Affected: on new line
|
(r'\bError:', '\nError:'), # Error: on new line
|
||||||
|
(r'\bAction:', '\nAction:'), # Action: on new line
|
||||||
|
(r'\bAffected:', '\nAffected:'), # Affected: on new line
|
||||||
(r'Device not currently', '\nDevice not currently'), # Note about missing device
|
(r'Device not currently', '\nDevice not currently'), # Note about missing device
|
||||||
(r'SMART:', '\nSMART:'), # SMART status
|
(r'\bSMART:', '\nSMART:'), # SMART status
|
||||||
]
|
]
|
||||||
|
|
||||||
for pattern, replacement in line_break_patterns:
|
for pattern, replacement in line_break_patterns:
|
||||||
preprocessed = re.sub(pattern, replacement, preprocessed)
|
preprocessed = re.sub(pattern, replacement, preprocessed)
|
||||||
|
|
||||||
# Clean up multiple newlines
|
# Clean up multiple newlines and leading newlines
|
||||||
preprocessed = re.sub(r'\n{3,}', '\n\n', preprocessed)
|
preprocessed = re.sub(r'\n{3,}', '\n\n', preprocessed)
|
||||||
|
preprocessed = re.sub(r'^\n+', '', preprocessed)
|
||||||
preprocessed = preprocessed.strip()
|
preprocessed = preprocessed.strip()
|
||||||
|
|
||||||
# ── Extended emoji mappings for health/disk messages ──
|
# ── Extended emoji mappings for health/disk messages ──
|
||||||
|
|||||||
Reference in New Issue
Block a user