diff --git a/Web/app.py b/Web/app.py index 390bdc5..836c29b 100755 --- a/Web/app.py +++ b/Web/app.py @@ -10479,45 +10479,49 @@ def mark_notification_read(notification_id): return redirect(url_for('notifications_view')) -@app.route('/notifications/mark_all_read', methods=['POST']) +@app.route('/notifications/mark-all-read', methods=['POST']) def mark_all_notifications_read(): - """Mark all visible notifications as read for the current user.""" if 'username' not in session: + flash('Bitte melden Sie sich an.', 'error') return redirect(url_for('login')) username = session['username'] - is_admin_user = False - current_permissions = us.get_effective_permissions(session['username']) - - if not current_permissions['actions'].get('can_manage_settings', False): - is_admin_user = True - else: - is_admin_user = False - - query = { - '$or': [ - {'Audience': 'user', 'TargetUser': username}, - ] - } - if is_admin_user: - query['$or'].append({'Audience': 'admin'}) + current_permissions = us.get_effective_permissions(username) + is_admin_user = current_permissions['actions'].get('can_manage_settings', False) client = None try: client = MongoClient(MONGODB_HOST, MONGODB_PORT) db = client[MONGODB_DB] - result = db['notifications'].update_many( + + # Filter-Logik: Welche Benachrichtigungen sollen als gelesen markiert werden? + # Wenn Sie 'Audience' nutzen (wie in Ihrer notifications_view Route): + query = {} + if is_admin_user: + # Ein Admin sieht sowohl an ihn gerichtete als auch allgemeine Admin-Meldungen + query['$or'] = [ + {'TargetUser': username}, + {'Audience': 'admin'} + ] + else: + # Normale User sehen nur Meldungen, die an sie oder alle User gerichtet sind + query['$or'] = [ + {'TargetUser': username}, + {'Audience': 'user'} + ] + + # WICHTIG: Fügt den Benutzernamen per $addToSet in das ReadBy-Array ein. + # $addToSet verhindert, dass der Name doppelt eingetragen wird, falls er schon drinsteht. + db['notifications'].update_many( query, - { - '$addToSet': {'ReadBy': username}, - '$set': {'UpdatedAt': datetime.datetime.now()} - } + {'$addToSet': {'ReadBy': username}} ) - if result.modified_count > 0: - _bump_notification_version(f'user:{username}') + + flash('Alle Benachrichtigungen wurden als gelesen markiert.', 'success') except Exception as exc: - app.logger.warning(f"Could not mark all notifications as read for {encrypt_text(username)}: {exc}") + app.logger.error(f"Error marking all notifications as read: {exc}") + flash('Fehler beim Aktualisieren der Benachrichtigungen.', 'error') finally: if client: client.close() @@ -12145,7 +12149,7 @@ def subscribe_to_push(): return jsonify({'success': False, 'error': 'Not authenticated'}), 401 try: - data = request.get_json() or {} + data = request.get_json(silent=True) or {} subscription = data.get('subscription') if not subscription or not subscription.get('endpoint'): @@ -12182,7 +12186,7 @@ def unsubscribe_from_push(): return jsonify({'success': False, 'error': 'Not authenticated'}), 401 try: - data = request.get_json() or {} + data = request.get_json(silent=True) or {} endpoint = data.get('endpoint') if not endpoint: @@ -12250,7 +12254,7 @@ def test_push_notification(): return jsonify({'success': False, 'error': 'Admin access required'}), 403 try: - data = request.get_json() or {} + data = request.get_json(silent=True) or {} target_user = data.get('target_user', session['username']) sent = pn.send_push_notification( diff --git a/Web/push_notifications.py b/Web/push_notifications.py index e8648ce..5c28433 100644 --- a/Web/push_notifications.py +++ b/Web/push_notifications.py @@ -122,15 +122,15 @@ def get_user_subscriptions(username): def save_push_subscription(username, subscription_obj): - """ - Save a new push subscription for a user with field-level encryption. - """ + """Save a new push subscription for a user with field-level encryption.""" try: endpoint = subscription_obj.get('endpoint') - if not endpoint: - logger.warning('Invalid subscription object: missing endpoint') + keys = subscription_obj.get('keys', {}) + + if not endpoint or not keys.get('p256dh') or not keys.get('auth'): + logger.warning(f'Invalid subscription object for {username}: missing endpoint or keys') return False - + client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] subs_col = get_push_subscriptions_collection(db) @@ -184,27 +184,23 @@ def save_push_subscription(username, subscription_obj): def remove_push_subscription(username, endpoint): - """ - Remove a push subscription by making it inactive. - """ try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = client[cfg.MONGODB_DB] subs_col = get_push_subscriptions_collection(db) - - # Recreate the deterministic hash to find the specific subscription + sub_hash = hashlib.shake_256( f"{username}:{endpoint}".encode('utf-8') ).hexdigest() - + result = subs_col.update_one( {'SubscriptionHash': sub_hash}, {'$set': {'IsActive': False}} ) - + client.close() - return result.modified_count > 0 - + return result.matched_count > 0 + except Exception as e: logger.error(f'Error removing push subscription: {e}') return False diff --git a/Web/static/js/push-notifications.js b/Web/static/js/push-notifications.js index b6aeb0f..626b17c 100644 --- a/Web/static/js/push-notifications.js +++ b/Web/static/js/push-notifications.js @@ -162,19 +162,17 @@ class PushNotificationManager { const subscription = await this.serviceWorkerRegistration.pushManager.getSubscription(); if (!subscription) { console.warn('No active push subscription'); - return false; + return true; // Bereits deaktiviert } - // Remove subscription on server - const success = await this.removeSubscriptionFromServer(subscription); + // Best-Effort: Dem Server Bescheid geben (wir ignorieren absichtlich, + // falls der Server das Abo nicht mehr kennt) + await this.removeSubscriptionFromServer(subscription); - // Unsubscribe from push service - if (success) { - await subscription.unsubscribe(); - return true; - } + // WICHTIG: Das Abo im Browser immer zwingend löschen! + await subscription.unsubscribe(); + return true; - return false; } catch (error) { console.error('Failed to unsubscribe from push notifications:', error); return false;