WIP: building up VAPID and subscription
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*~
|
||||
41
Dockerfile
Normal file
41
Dockerfile
Normal file
@@ -0,0 +1,41 @@
|
||||
# Use an official Python runtime as a parent image
|
||||
FROM python:3.8-alpine3.18 as builder
|
||||
|
||||
RUN apk update && apk upgrade
|
||||
RUN apk add --no-cache --virtual .build-deps build-base git
|
||||
RUN apk add bash libffi-dev tzdata --upgrade --no-cache
|
||||
|
||||
ENV TZ America/New_York
|
||||
|
||||
# Set the working directory in the container to /app
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the current directory contents into the container at /app
|
||||
COPY app.py /app
|
||||
COPY requirements.txt /app
|
||||
COPY models.py /app
|
||||
COPY init_db.py /app/init_db.py
|
||||
|
||||
|
||||
# Install any needed packages specified in requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN python /app/init_db.py
|
||||
|
||||
RUN apk del .build-deps
|
||||
|
||||
# ---- Production Stage ----
|
||||
FROM python:3.8-alpine3.18 as production
|
||||
|
||||
# Create a user to run our application
|
||||
RUN adduser -D myuser
|
||||
|
||||
# Copy the dependencies and installed packages from the builder image
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app /app
|
||||
COPY --from=builder /usr/local /usr/local
|
||||
|
||||
# Switch to the created user
|
||||
USER myuser
|
||||
|
||||
# Start gunicorn with the appropriate options
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:5000", "--workers=3", "app:app"]
|
||||
95
app.py
Normal file
95
app.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from models import db, VAPIDKey, Subscription
|
||||
from py_vapid import Vapid
|
||||
from pywebpush import webpush, WebPushException
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///webpush.db'
|
||||
db.init_app(app)
|
||||
|
||||
def generate_and_save_vapid_keys():
|
||||
vapid = Vapid()
|
||||
vapid.generate_keys()
|
||||
private_key = vapid.get_private_key().to_pem().decode('utf-8').strip()
|
||||
public_key = vapid.get_public_key().to_pem().decode('utf-8').strip()
|
||||
|
||||
key = VAPIDKey(public_key=public_key, private_key=private_key)
|
||||
db.session.add(key)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def initialize():
|
||||
if not VAPIDKey.query.first():
|
||||
generate_and_save_vapid_keys()
|
||||
|
||||
def send_push_notification(subscription_info, message, vapid_key):
|
||||
try:
|
||||
webpush(
|
||||
subscription_info=subscription_info,
|
||||
data=json.dumps(message),
|
||||
vapid_private_key=vapid_key.private_key,
|
||||
vapid_claims={
|
||||
"sub": "mailto:your-email@example.com"
|
||||
}
|
||||
)
|
||||
except WebPushException as e:
|
||||
print(f"Failed to send notification: {e}")
|
||||
|
||||
|
||||
@app.route('/web-push/vapid', methods=['GET'])
|
||||
def get_vapid():
|
||||
key = VAPIDKey.query.first()
|
||||
if key:
|
||||
return jsonify(public_key=key.public_key)
|
||||
return jsonify(error='No VAPID keys found'), 404
|
||||
|
||||
|
||||
@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()
|
||||
|
||||
subscription_info = {
|
||||
"endpoint": subscription.endpoint,
|
||||
"keys": {
|
||||
"p256dh": subscription.p256dh,
|
||||
"auth": subscription.auth
|
||||
}
|
||||
}
|
||||
|
||||
message = {"title": "Subscription Successful", "body": "Thank you for subscribing!"}
|
||||
send_push_notification(subscription_info, message, vapid_key)
|
||||
|
||||
return jsonify(success=True)
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
3
build.sh
Executable file
3
build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build . -t endorser-push-server:1.0 --no-cache
|
||||
9
init_db.py
Normal file
9
init_db.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from models import db
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///webpush.db'
|
||||
db.init_app(app)
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
16
models.py
Normal file
16
models.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
class VAPIDKey(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
public_key = db.Column(db.String(255), nullable=False)
|
||||
private_key = db.Column(db.String(255), nullable=False)
|
||||
subscriptions = db.relationship('Subscription', backref='vapid_key', lazy=True)
|
||||
|
||||
class Subscription(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
endpoint = db.Column(db.String(500), nullable=False)
|
||||
p256dh = db.Column(db.String(255), nullable=False)
|
||||
auth = db.Column(db.String(255), nullable=False)
|
||||
vapid_key_id = db.Column(db.Integer, db.ForeignKey('vapid_key.id'), nullable=False)
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
flask
|
||||
flask_sqlalchemy
|
||||
py_vapid
|
||||
pywebpush
|
||||
Reference in New Issue
Block a user