mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-01-31 01:51:29 -05:00
BE: in-app notifications overwrite prevention + device huristics update
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
@@ -155,9 +155,10 @@
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "001FA7", "vendor": "Sony" },
|
||||
{ "mac_prefix": "7C04D0", "vendor": "Nintendo" },
|
||||
{ "mac_prefix": "EC26CA", "vendor": "Sony" }
|
||||
{ "mac_prefix": "EC26CA", "vendor": "Sony" },
|
||||
{ "mac_prefix": "48B02D", "vendor": "NVIDIA" }
|
||||
],
|
||||
"name_pattern": ["playstation", "xbox"]
|
||||
"name_pattern": ["playstation", "xbox", "shield", "nvidia"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Camera",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import os
|
||||
import sys
|
||||
import _io
|
||||
import json
|
||||
import uuid
|
||||
import time
|
||||
import fcntl
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@@ -20,6 +20,35 @@ from api_server.sse_broadcast import broadcast_unread_notifications_count # noq
|
||||
NOTIFICATION_API_FILE = apiPath + 'user_notifications.json'
|
||||
|
||||
|
||||
def locked_notifications_file(callback):
|
||||
# Ensure file exists
|
||||
if not os.path.exists(NOTIFICATION_API_FILE):
|
||||
with open(NOTIFICATION_API_FILE, "w") as f:
|
||||
f.write("[]")
|
||||
|
||||
with open(NOTIFICATION_API_FILE, "r+") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
try:
|
||||
raw = f.read().strip() or "[]"
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
mylog("none", "[Notification] Corrupted JSON detected, resetting.")
|
||||
data = []
|
||||
|
||||
# Let caller modify data
|
||||
result = callback(data)
|
||||
|
||||
# Write back atomically
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
return result
|
||||
finally:
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
|
||||
# Show Frontend User Notification
|
||||
def write_notification(content, level="alert", timestamp=None):
|
||||
"""
|
||||
@@ -37,45 +66,21 @@ def write_notification(content, level="alert", timestamp=None):
|
||||
if timestamp is None:
|
||||
timestamp = timeNowDB()
|
||||
|
||||
# Generate GUID
|
||||
guid = str(uuid.uuid4())
|
||||
|
||||
# Prepare notification dictionary
|
||||
notification = {
|
||||
"timestamp": str(timestamp),
|
||||
"guid": guid,
|
||||
"guid": str(uuid.uuid4()),
|
||||
"read": 0,
|
||||
"level": level,
|
||||
"content": content,
|
||||
}
|
||||
|
||||
# If file exists, load existing data, otherwise initialize as empty list
|
||||
if os.path.exists(NOTIFICATION_API_FILE):
|
||||
with open(NOTIFICATION_API_FILE, "r") as file:
|
||||
# Check if the file object is of type _io.TextIOWrapper
|
||||
if isinstance(file, _io.TextIOWrapper):
|
||||
file_contents = file.read() # Read file contents
|
||||
if file_contents == "":
|
||||
file_contents = "[]" # If file is empty, initialize as empty list
|
||||
def update(notifications):
|
||||
notifications.append(notification)
|
||||
|
||||
# mylog('debug', ['[Notification] User Notifications file: ', file_contents])
|
||||
notifications = json.loads(file_contents) # Parse JSON data
|
||||
else:
|
||||
mylog("none", "[Notification] File is not of type _io.TextIOWrapper")
|
||||
notifications = []
|
||||
else:
|
||||
notifications = []
|
||||
locked_notifications_file(update)
|
||||
|
||||
# Append new notification
|
||||
notifications.append(notification)
|
||||
|
||||
# Write updated data back to file
|
||||
with open(NOTIFICATION_API_FILE, "w") as file:
|
||||
json.dump(notifications, file, indent=4)
|
||||
|
||||
# Broadcast unread count update
|
||||
try:
|
||||
unread_count = sum(1 for n in notifications if n.get("read", 0) == 0)
|
||||
unread_count = sum(1 for n in locked_notifications_file(lambda n: n) if n.get("read", 0) == 0)
|
||||
broadcast_unread_notifications_count(unread_count)
|
||||
except Exception as e:
|
||||
mylog("none", [f"[Notification] Failed to broadcast unread count: {e}"])
|
||||
@@ -143,24 +148,42 @@ def mark_all_notifications_read():
|
||||
"error": str (optional)
|
||||
}
|
||||
"""
|
||||
# If notifications file does not exist, nothing to mark
|
||||
if not os.path.exists(NOTIFICATION_API_FILE):
|
||||
return {"success": True}
|
||||
|
||||
try:
|
||||
with open(NOTIFICATION_API_FILE, "r") as f:
|
||||
notifications = json.load(f)
|
||||
except Exception as e:
|
||||
mylog("none", f"[Notification] Failed to read notifications: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
# Open file in read/write mode and acquire exclusive lock
|
||||
with open(NOTIFICATION_API_FILE, "r+") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
|
||||
for n in notifications:
|
||||
n["read"] = 1
|
||||
try:
|
||||
# Read file contents
|
||||
file_contents = f.read().strip()
|
||||
if file_contents == "":
|
||||
notifications = []
|
||||
else:
|
||||
try:
|
||||
notifications = json.loads(file_contents)
|
||||
except json.JSONDecodeError as e:
|
||||
mylog("none", f"[Notification] Corrupted notifications JSON: {e}")
|
||||
notifications = []
|
||||
|
||||
# Mark all notifications as read
|
||||
for n in notifications:
|
||||
n["read"] = 1
|
||||
|
||||
# Rewrite file safely
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(notifications, f, indent=4)
|
||||
|
||||
finally:
|
||||
# Always release file lock
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
try:
|
||||
with open(NOTIFICATION_API_FILE, "w") as f:
|
||||
json.dump(notifications, f, indent=4)
|
||||
except Exception as e:
|
||||
mylog("none", f"[Notification] Failed to write notifications: {e}")
|
||||
mylog("none", f"[Notification] Failed to read/write notifications: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
mylog("debug", "[Notification] All notifications marked as read.")
|
||||
|
||||
Reference in New Issue
Block a user