You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

162 lines
5.2 KiB

from cryptography.hazmat.primitives import serialization
from flask import Flask, request, jsonify
from models import db, VAPIDKey, Subscription
from pywebpush import webpush, WebPushException
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
import base64
import binascii
import json
import os
import re
import time
def create_app(config_name):
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data/webpush.db'
db.init_app(app)
def generate_and_save_vapid_keys():
try:
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
public_key_bytes = private_key.public_key().public_bytes(
encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.UncompressedPoint
)
public_key_base64 = base64.b64encode(public_key_bytes).decode()
private_key_hex = binascii.hexlify(private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)).decode()
except Exception as e:
print(f"Error generating VAPID keys: {e}")
key = VAPIDKey(public_key=public_key_base64, private_key=private_key_hex)
db.session.add(key)
db.session.commit()
def initialize():
if not VAPIDKey.query.first():
generate_and_save_vapid_keys()
def send_push_notification(subscription_info, message, vapid_key):
result = True
try:
private_key_hex = vapid_key.private_key
private_key_der = bytes.fromhex(private_key_hex)
private_key_base64 = base64.b64encode(private_key_der).decode()
webpush(
subscription_info=subscription_info,
data=json.dumps(message),
vapid_private_key=private_key_base64,
vapid_claims={
"sub": "mailto:matthew.raymer@gmail.com"
}
)
except Exception as e:
result = False
print(f"Failed to send notification: {e}")
return result
def is_valid_base64_url(s):
return re.match(r'^[A-Za-z0-9_-]*$', s) is not None
def is_valid_server_key(server_key):
if not is_valid_base64_url(server_key):
return False
return len(server_key) == 88
@app.route('/web-push/clear_subscriptions', methods=['POST'])
def clear_subscriptions():
try:
Subscription.query.delete()
return jsonify(message='Subscriptions cleared')
except Exception as e:
return jsonify(error=f'Error clearing subscriptions: {str(e)}'), 500
@app.route('/web-push/regenerate_vapid_keys', methods=['POST'])
def regenerate_vapid_keys():
try:
# Generate new VAPID keys
VAPIDKey.query.delete()
generate_and_save_vapid_keys()
return jsonify(message='VAPID keys regenerated successfully')
except Exception as e:
return jsonify(error=f'Error regenerating VAPID keys: {str(e)}'), 500
@app.route('/web-push/vapid', methods=['GET'])
def get_vapid():
key = VAPIDKey.query.first()
return jsonify(vapidKey=key.public_key)
@app.route('/web-push/subscribe', methods=['POST'])
def subscribe():
content = request.json
vapid_key = VAPIDKey.query.first()
if not vapid_key:
return jsonify(success=False, error="No VAPID keys available"), 500
subscription = Subscription(endpoint=content['endpoint'],
p256dh=content['keys']['p256dh'],
auth=content['keys']['auth'],
vapid_key_id=vapid_key.id)
db.session.add(subscription)
db.session.commit()
time.sleep(5)
subscription_info = {
"endpoint": subscription.endpoint,
"keys": {
"p256dh": subscription.p256dh,
"auth": subscription.auth
}
}
message = {"title": "Subscription Successful", "message": "Thank you for subscribing!"}
success = send_push_notification(subscription_info, message, vapid_key)
return jsonify(success=success, message=vapid_key.private_key)
@app.route('/web-push/unsubscribe', methods=['DELETE'])
def unsubscribe():
content = request.json
endpoint = content['endpoint']
subscription = Subscription.query.filter_by(endpoint=endpoint).first()
if subscription:
db.session.delete(subscription)
db.session.commit()
return jsonify(success=True, message="Subscription deleted successfully")
else:
return jsonify(success=False, error="Subscription not found"), 404
with app.app_context():
initialize()
return app