|
|
@@ -43,7 +43,7 @@ class WebPushService():
|
|
|
|
|
|
|
|
|
|
|
|
# Setting the application instance
|
|
|
|
# Setting the application instance
|
|
|
|
self.app = app
|
|
|
|
self.app = app
|
|
|
|
self.app.add_url_rule('/web-push/regenerate_vapid', view_func=self.regenerate_vapid, methods=['POST'])
|
|
|
|
self.app.add_url_rule('/web-push/regenerate-vapid', view_func=self.regenerate_vapid, methods=['POST'])
|
|
|
|
|
|
|
|
|
|
|
|
# Setting the database URI for the application
|
|
|
|
# Setting the database URI for the application
|
|
|
|
db_uri = os.getenv('SQLALCHEMY_DATABASE_URI', 'sqlite:////app/instance/data/webpush.db')
|
|
|
|
db_uri = os.getenv('SQLALCHEMY_DATABASE_URI', 'sqlite:////app/instance/data/webpush.db')
|
|
|
@@ -102,7 +102,8 @@ class WebPushService():
|
|
|
|
db.session.commit()
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
print(f"Error generating VAPID keys: {e}")
|
|
|
|
print(f"Error generating VAPID keys: {str(e)}")
|
|
|
|
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _initialize(self) -> None:
|
|
|
|
def _initialize(self) -> None:
|
|
|
@@ -135,7 +136,8 @@ class WebPushService():
|
|
|
|
- vapid_key (VAPIDKey): The VAPID key model instance containing the private key used for sending the notification.
|
|
|
|
- vapid_key (VAPIDKey): The VAPID key model instance containing the private key used for sending the notification.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
- bool: True if the push notification was sent successfully, False otherwise.
|
|
|
|
- request.Response https://requests.readthedocs.io/en/latest/api.html#requests.Response if the push notification was sent successfully
|
|
|
|
|
|
|
|
or False if there was an exception
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
Notes:
|
|
|
|
- The `webpush` function is used to send the notification.
|
|
|
|
- The `webpush` function is used to send the notification.
|
|
|
@@ -144,14 +146,15 @@ class WebPushService():
|
|
|
|
|
|
|
|
|
|
|
|
# Sending the push notification using the webpush function
|
|
|
|
# Sending the push notification using the webpush function
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
webpush(
|
|
|
|
result = webpush(
|
|
|
|
subscription_info=subscription_info,
|
|
|
|
subscription_info=subscription_info,
|
|
|
|
data=json.dumps(message),
|
|
|
|
data=json.dumps(message),
|
|
|
|
vapid_private_key=vapid_key.private_key,
|
|
|
|
vapid_private_key=vapid_key.private_key,
|
|
|
|
vapid_claims={"sub": CONTACT_EMAIL}
|
|
|
|
vapid_claims={"sub": CONTACT_EMAIL}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# "because sometimes that's what I had to do to make it work!" - Matthew
|
|
|
|
time.sleep(1)
|
|
|
|
time.sleep(1)
|
|
|
|
return True
|
|
|
|
return {"success": True, "message": result.text, "result": result}
|
|
|
|
|
|
|
|
|
|
|
|
except WebPushException as ex:
|
|
|
|
except WebPushException as ex:
|
|
|
|
now = datetime.datetime.now().isoformat()
|
|
|
|
now = datetime.datetime.now().isoformat()
|
|
|
@@ -172,7 +175,7 @@ class WebPushService():
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
print("Error other than unsubscribed/expired.", ex.args[0], flush=True)
|
|
|
|
print("Error other than unsubscribed/expired.", ex.args[0], flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
return {"success": False, "message": ex.args[0]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _send_daily_notifications(self) -> None:
|
|
|
|
def _send_daily_notifications(self) -> None:
|
|
|
@@ -234,19 +237,19 @@ class WebPushService():
|
|
|
|
1. Deletes the current VAPID keys from the database.
|
|
|
|
1. Deletes the current VAPID keys from the database.
|
|
|
|
2. Generates and stores new VAPID keys using the `_generate_and_save_vapid_keys` method.
|
|
|
|
2. Generates and stores new VAPID keys using the `_generate_and_save_vapid_keys` method.
|
|
|
|
|
|
|
|
|
|
|
|
URL: /web-push/regenerate_vapid
|
|
|
|
URL: /web-push/regenerate-vapid
|
|
|
|
Method: POST
|
|
|
|
Method: POST
|
|
|
|
Header: Authentication: Basic ...
|
|
|
|
Header: Authentication: Basic ...
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
- Tuple[str, int]: A JSON response indicating the success or failure of the operation, along with the appropriate HTTP status code.
|
|
|
|
- tuple with "success" as True or False, and "message" message string
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
Notes:
|
|
|
|
- If the operation is successful, a JSON response with a success message is returned with a 200 status code.
|
|
|
|
- If the operation is successful, a JSON response with a success message is returned with a 200 status code.
|
|
|
|
- If there's an error during the operation, a JSON response with the error message is returned with a 500 status code.
|
|
|
|
- If there's an error during the operation, a JSON response with the error message is returned with a 500 status code.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# This default can be invoked thus: curl -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" localhost:3000/web-push/regenerate_vapid
|
|
|
|
# This default can be invoked thus: curl -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" localhost:3000/web-push/regenerate-vapid
|
|
|
|
envPassword = os.getenv('ADMIN_PASSWORD', 'admin')
|
|
|
|
envPassword = os.getenv('ADMIN_PASSWORD', 'admin')
|
|
|
|
auth = request.authorization
|
|
|
|
auth = request.authorization
|
|
|
|
if (auth is None
|
|
|
|
if (auth is None
|
|
|
@@ -273,7 +276,7 @@ class WebPushService():
|
|
|
|
return jsonify(success=True, message="VAPID keys regenerated successfully"), 200
|
|
|
|
return jsonify(success=True, message="VAPID keys regenerated successfully"), 200
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
return jsonify(error=f'Error regenerating VAPID keys: {str(e)}'), 500
|
|
|
|
return jsonify(success=False, message=f'Error regenerating VAPID keys: {str(e)}'), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
@@ -317,7 +320,7 @@ class WebPushService():
|
|
|
|
Method: POST
|
|
|
|
Method: POST
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
- Tuple[str, int]: A JSON response indicating the success or failure of the operation, along with the appropriate HTTP status code.
|
|
|
|
- A JSON response with "success" as True or False, "message" as a response message string, and potentially a "result" as a request.Response object from sending a test notification
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
Notes:
|
|
|
|
- If the operation is successful, a confirmation push notification is sent to the subscriber with a success message.
|
|
|
|
- If the operation is successful, a confirmation push notification is sent to the subscriber with a success message.
|
|
|
@@ -333,7 +336,7 @@ class WebPushService():
|
|
|
|
|
|
|
|
|
|
|
|
# Checking if the VAPID key is available
|
|
|
|
# Checking if the VAPID key is available
|
|
|
|
if not vapid_key:
|
|
|
|
if not vapid_key:
|
|
|
|
return jsonify(success=False, error="No VAPID keys available"), 500
|
|
|
|
return jsonify(success=False, message="No VAPID keys available"), 500
|
|
|
|
|
|
|
|
|
|
|
|
# Creating a new Subscription instance with the provided data
|
|
|
|
# Creating a new Subscription instance with the provided data
|
|
|
|
subscription = Subscription(endpoint=content['endpoint'],
|
|
|
|
subscription = Subscription(endpoint=content['endpoint'],
|
|
|
@@ -361,10 +364,10 @@ class WebPushService():
|
|
|
|
message = {"title": "Subscription Successful", "message": "Thank you for subscribing!"}
|
|
|
|
message = {"title": "Subscription Successful", "message": "Thank you for subscribing!"}
|
|
|
|
|
|
|
|
|
|
|
|
# Sending the confirmation push notification
|
|
|
|
# Sending the confirmation push notification
|
|
|
|
success = WebPushService._send_push_notification(subscription_info, message, vapid_key)
|
|
|
|
result = WebPushService._send_push_notification(subscription_info, message, vapid_key)
|
|
|
|
|
|
|
|
|
|
|
|
# Returning the operation status
|
|
|
|
# Returning the operation status
|
|
|
|
return jsonify(success=success)
|
|
|
|
return jsonify(success=result["status_code"]==201, message=result["text"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
@@ -404,7 +407,69 @@ class WebPushService():
|
|
|
|
|
|
|
|
|
|
|
|
# If the subscription is not found, return an error message
|
|
|
|
# If the subscription is not found, return an error message
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
return jsonify(success=False, error="Subscription not found"), 404
|
|
|
|
return jsonify(success=False, message="Subscription not found"), 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
|
|
@app.route('/web-push/send-test', methods=['POST'])
|
|
|
|
|
|
|
|
def send_test() -> Tuple[str, int]:
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
Endpoint to send a test push notification to a specific client.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This method:
|
|
|
|
|
|
|
|
1. Retrieves the subscription information from the incoming request.
|
|
|
|
|
|
|
|
2. Looks up the subscription in the database.
|
|
|
|
|
|
|
|
3. Calls the _send_push_notification method to send a test push notification.
|
|
|
|
|
|
|
|
4. Returns the result of the _send_push_notification call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
URL: /web-push/send-test
|
|
|
|
|
|
|
|
Method: POST
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
- A JSON response with the result of the _send_push_notification call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
- The incoming request should contain the parameters "endpoint", "p256dh", and "auth".
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Retrieving the subscription information from the incoming request
|
|
|
|
|
|
|
|
content = request.json
|
|
|
|
|
|
|
|
endpoint = content['endpoint']
|
|
|
|
|
|
|
|
p256dh = content['keys']['p256dh']
|
|
|
|
|
|
|
|
auth = content['keys']['auth']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Looking up the subscription in the database
|
|
|
|
|
|
|
|
subscription = Subscription.query.filter_by(endpoint=endpoint, p256dh=p256dh, auth=auth).first()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# If the subscription is found, call the _send_push_notification method
|
|
|
|
|
|
|
|
if subscription:
|
|
|
|
|
|
|
|
subscription_info = {
|
|
|
|
|
|
|
|
"endpoint": subscription.endpoint,
|
|
|
|
|
|
|
|
"keys": {
|
|
|
|
|
|
|
|
"p256dh": subscription.p256dh,
|
|
|
|
|
|
|
|
"auth": subscription.auth
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
title = "Test Notification"
|
|
|
|
|
|
|
|
if "title" in content:
|
|
|
|
|
|
|
|
title = content['title']
|
|
|
|
|
|
|
|
message = "This is a test notification."
|
|
|
|
|
|
|
|
if "message" in content:
|
|
|
|
|
|
|
|
message = content['message']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vapid_key = VAPIDKey.query.filter_by(id=subscription.vapid_key_id).first()
|
|
|
|
|
|
|
|
result = WebPushService._send_push_notification(
|
|
|
|
|
|
|
|
subscription_info,
|
|
|
|
|
|
|
|
{"title": title, "message": message},
|
|
|
|
|
|
|
|
vapid_key
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return jsonify(
|
|
|
|
|
|
|
|
success=result["result"].status_code==201,
|
|
|
|
|
|
|
|
message=result["result"].text,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return jsonify({"success": False, "message": "Subscription not found"}), 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
web_push_service = WebPushService(app, "app")
|
|
|
|
web_push_service = WebPushService(app, "app")
|
|
|
|