Compare commits

...

5 Commits

4 changed files with 49 additions and 42 deletions
+25 -32
View File
@@ -10373,21 +10373,39 @@ def my_borrowed_items():
) )
@app.route('/api/push/vapid-key', methods=['GET'])
def get_vapid_key():
"""
Returns the VAPID public key for web push subscriptions
"""
from Web.push_notifications import _get_vapid_public
if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authenticated'}), 401
try:
if not _get_vapid_public():
return jsonify({'success': False, 'error': 'VAPID public key not configured'}), 500
return jsonify({
'success': True,
'publicKey': _get_vapid_public()
})
except Exception as e:
app.logger.error(f'Error getting VAPID key: {e}')
return jsonify({'success': False}), 500
@app.route('/notifications') @app.route('/notifications')
def notifications_view(): def notifications_view():
"""Notification center for users and admins.""" """Notification center for users and admins."""
from Web.push_notifications import _get_vapid_public
if 'username' not in session: if 'username' not in session:
flash('Bitte melden Sie sich an, um Benachrichtigungen zu sehen.', 'error') flash('Bitte melden Sie sich an, um Benachrichtigungen zu sehen.', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
username = session['username'] username = session['username']
is_admin_user = False
current_permissions = us.get_effective_permissions(session['username']) current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_settings', False): is_admin_user = current_permissions['actions'].get('can_manage_settings', False)
is_admin_user = True
else:
is_admin_user = False
client = None client = None
try: try:
@@ -10421,6 +10439,7 @@ def notifications_view():
is_admin_user=is_admin_user, is_admin_user=is_admin_user,
library_module_enabled=cfg.MODULES.is_enabled('library'), library_module_enabled=cfg.MODULES.is_enabled('library'),
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'), student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
vapid_public_key=_get_vapid_public()
) )
except Exception as exc: except Exception as exc:
app.logger.error(f"Error loading notifications: {exc}") app.logger.error(f"Error loading notifications: {exc}")
@@ -12219,32 +12238,6 @@ def get_push_subscriptions():
app.logger.error(f'Error getting push subscriptions: {e}') app.logger.error(f'Error getting push subscriptions: {e}')
return jsonify({'success': False}), 500 return jsonify({'success': False}), 500
@app.route('/api/push/vapid-key', methods=['GET'])
def get_vapid_key():
"""
Get the VAPID public key for push notifications
Used by the service worker to communicate with push service
"""
try:
vapid_key = pn.VAPID_PUBLIC_KEY
if not vapid_key:
app.logger.warning('VAPID_PUBLIC_KEY not configured')
return jsonify({
'success': False,
'error': 'Push notifications not configured on server'
}), 501
return jsonify({
'success': True,
'vapid_key': vapid_key
})
except Exception as e:
app.logger.error(f'Error getting VAPID key: {e}')
return jsonify({'success': False}), 500
@app.route('/api/push/test', methods=['POST']) @app.route('/api/push/test', methods=['POST'])
def test_push_notification(): def test_push_notification():
""" """
+10 -3
View File
@@ -40,14 +40,18 @@ try:
else: else:
vapid = Vapid.from_file(VAPID_PRIVATE_PEM) vapid = Vapid.from_file(VAPID_PRIVATE_PEM)
# Extract public key in standard uncompressed point format encoded in URL-safe base64
raw_pub = vapid.public_key.public_bytes( raw_pub = vapid.public_key.public_bytes(
serialization.Encoding.X962, serialization.Encoding.X962,
serialization.PublicFormat.UncompressedPoint serialization.PublicFormat.UncompressedPoint
) )
VAPID_PUBLIC_KEY = b64urlencode(raw_pub).decode('utf-8') encoded_pub = b64urlencode(raw_pub)
VAPID_PRIVATE_KEY = VAPID_PRIVATE_PEM # pywebpush accepts the file path to the private PEM if isinstance(encoded_pub, bytes):
VAPID_PUBLIC_KEY = encoded_pub.decode('utf-8')
else:
VAPID_PUBLIC_KEY = encoded_pub
VAPID_PRIVATE_KEY = VAPID_PRIVATE_PEM
except Exception as e: except Exception as e:
logger.error(f'Could not load or generate VAPID keys: {e}') logger.error(f'Could not load or generate VAPID keys: {e}')
@@ -60,6 +64,9 @@ VAPID_SUBJECT = os.getenv('VAPID_SUBJECT', 'mailto:support@invario-software.de')
FCM_API_KEY = os.getenv('FCM_API_KEY', '') FCM_API_KEY = os.getenv('FCM_API_KEY', '')
def _get_vapid_public():
return VAPID_PUBLIC_KEY
def _get_username_hash(username): def _get_username_hash(username):
"""Generates a deterministic hash for database lookups.""" """Generates a deterministic hash for database lookups."""
if not username: if not username:
+13 -6
View File
@@ -25,7 +25,7 @@ class PushNotificationManager {
* Initialize push notification system * Initialize push notification system
* Must be called after page load * Must be called after page load
*/ */
async init() { async init(providedKey = null) {
if (!this.isSupported) { if (!this.isSupported) {
console.log('Push notifications not supported in this browser'); console.log('Push notifications not supported in this browser');
return false; return false;
@@ -49,11 +49,18 @@ class PushNotificationManager {
} }
} }
// Fetch VAPID public key from server // Wenn der Key direkt aus dem Template übergeben wurde, nutze diesen (spart einen Request)
if (providedKey) {
this.vapidKey = providedKey;
return true;
}
// Ansonsten: Fetch VAPID public key from server
const keyResponse = await fetch('/api/push/vapid-key'); const keyResponse = await fetch('/api/push/vapid-key');
if (keyResponse.ok) { if (keyResponse.ok) {
const keyData = await keyResponse.json(); const keyData = await keyResponse.json();
this.vapidKey = keyData.vapid_key; // WICHTIG: Muss exakt mit dem Python-JSON-Key übereinstimmen!
this.vapidKey = keyData.publicKey;
} else { } else {
console.warn('Failed to fetch VAPID key'); console.warn('Failed to fetch VAPID key');
return false; return false;
@@ -324,7 +331,7 @@ const pushNotificationManager = new PushNotificationManager();
/** /**
* Show notification subscription UI (typically in settings) * Show notification subscription UI (typically in settings)
*/ */
function showPushNotificationSettings() { function showPushNotificationSettings(vapidPublicKey) {
const container = document.getElementById('push-notification-settings'); const container = document.getElementById('push-notification-settings');
if (!container) return; if (!container) return;
@@ -350,9 +357,9 @@ function showPushNotificationSettings() {
container.innerHTML = html; container.innerHTML = html;
// Set up button handler // Set up button handler and pass the VAPID public key to init()
const toggleBtn = document.getElementById('toggle-push-btn'); const toggleBtn = document.getElementById('toggle-push-btn');
pushNotificationManager.init().then(() => { pushNotificationManager.init(vapidPublicKey).then(() => {
updatePushStatus(); updatePushStatus();
}); });
+1 -1
View File
@@ -89,7 +89,7 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
if (typeof showPushNotificationSettings === 'function') { if (typeof showPushNotificationSettings === 'function') {
showPushNotificationSettings(); showPushNotificationSettings('{{ vapid_public_key }}');
} }
}); });
</script> </script>