blakeblackshear.frigate/frigate/api/notification.py
Nicolas Mowen 690ee3dc15 Implement support for notifications (#12523)
* Setup basic notification page

* Add basic notification implementation

* Register for push notifications

* Implement dispatching

* Add fields

* Handle image and link

* Add notification config

* Add field for users notification tokens

* Implement saving of notification tokens

* Implement VAPID key generation

* Implement public key encoding

* Implement webpush from server

* Implement push notification handling

* Make notifications config only

* Add maskable icon

* Use zod form to control notification settings in the UI

* Use js

* Always open notification

* Support multiple endpoints

* Handle cleaning up expired notification registrations

* Correctly unsubscribe notifications

* Change ttl dynamically

* Add note about notification latency and features

* Cleanup docs

* Fix firefox pushes

* Add links to docs and improve formatting

* Improve wording

* Fix docstring

Co-authored-by: Blake Blackshear <blake@frigate.video>

* Handle case where native auth is not enabled

* Show errors in UI

---------

Co-authored-by: Blake Blackshear <blake@frigate.video>
2024-08-29 20:19:50 -06:00

66 lines
1.9 KiB
Python

"""Notification apis."""
import logging
import os
from cryptography.hazmat.primitives import serialization
from flask import (
Blueprint,
current_app,
jsonify,
make_response,
request,
)
from peewee import DoesNotExist
from py_vapid import Vapid01, utils
from frigate.const import CONFIG_DIR
from frigate.models import User
logger = logging.getLogger(__name__)
NotificationBp = Blueprint("notifications", __name__)
@NotificationBp.route("/notifications/pubkey", methods=["GET"])
def get_vapid_pub_key():
if not current_app.frigate_config.notifications.enabled:
return make_response(
jsonify({"success": False, "message": "Notifications are not enabled."}),
400,
)
key = Vapid01.from_file(os.path.join(CONFIG_DIR, "notifications.pem"))
raw_pub = key.public_key.public_bytes(
serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint
)
return jsonify(utils.b64urlencode(raw_pub)), 200
@NotificationBp.route("/notifications/register", methods=["POST"])
def register_notifications():
if current_app.frigate_config.auth.enabled:
username = request.headers.get("remote-user", type=str) or "admin"
else:
username = "admin"
json: dict[str, any] = request.get_json(silent=True) or {}
sub = json.get("sub")
if not sub:
return jsonify(
{"success": False, "message": "Subscription must be provided."}
), 400
try:
User.update(notification_tokens=User.notification_tokens.append(sub)).where(
User.username == username
).execute()
return make_response(
jsonify({"success": True, "message": "Successfully saved token."}), 200
)
except DoesNotExist:
return make_response(
jsonify({"success": False, "message": "Could not find user."}), 404
)