push notifs stuff from https://www.digitalocean.com/community/tutorials/how-to-send-web-push-notifications-from-django-applications
This commit is contained in:
parent
b47ee82bda
commit
49148320ee
BIN
server/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
server/__pycache__/__init__.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/__pycache__/admin.cpython-37.pyc
Normal file
BIN
server/__pycache__/admin.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/__pycache__/models.cpython-37.pyc
Normal file
BIN
server/__pycache__/models.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/__pycache__/urls.cpython-37.pyc
Normal file
BIN
server/__pycache__/urls.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/__pycache__/views.cpython-37.pyc
Normal file
BIN
server/__pycache__/views.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/__pycache__/wsgi.cpython-37.pyc
Normal file
BIN
server/__pycache__/wsgi.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/migrations/__pycache__/0001_initial.cpython-37.pyc
Normal file
BIN
server/migrations/__pycache__/0001_initial.cpython-37.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
server/migrations/__pycache__/0003_delete_todo.cpython-37.pyc
Normal file
BIN
server/migrations/__pycache__/0003_delete_todo.cpython-37.pyc
Normal file
Binary file not shown.
BIN
server/migrations/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
server/migrations/__pycache__/__init__.cpython-37.pyc
Normal file
Binary file not shown.
180
server/templates/home.html
Normal file
180
server/templates/home.html
Normal file
@ -0,0 +1,180 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="vapid-key" content="{{ vapid_key }}">
|
||||
{% if user.id %}
|
||||
<meta name="user_id" content="{{ user.id }}">
|
||||
{% endif %}
|
||||
<title>Web Push</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=PT+Sans:400,700" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<form id="send-push__form">
|
||||
<h3 class="header">Send a push notification</h3>
|
||||
<p class="error"></p>
|
||||
<input type="text" name="head" placeholder="Header: Your favorite airline 😍">
|
||||
<textarea name="body" id="" cols="30" rows="10" placeholder="Body: Your flight has been cancelled 😱😱😱"></textarea>
|
||||
<button>Send Me</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const registerSw = async () => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
const reg = await navigator.serviceWorker.register('sw.js');
|
||||
initialiseState(reg)
|
||||
|
||||
} else {
|
||||
showNotAllowed("You can't send push notifications ☹️😢")
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const initialiseState = (reg) => {
|
||||
if (!reg.showNotification) {
|
||||
showNotAllowed('Showing notifications isn\'t supported ☹️😢');
|
||||
return
|
||||
}
|
||||
if (Notification.permission === 'denied') {
|
||||
showNotAllowed('You prevented us from showing notifications ☹️🤔');
|
||||
return
|
||||
}
|
||||
if (!'PushManager' in window) {
|
||||
showNotAllowed("Push isn't allowed in your browser 🤔");
|
||||
return
|
||||
}
|
||||
subscribe(reg);
|
||||
}
|
||||
|
||||
const showNotAllowed = (message) => {
|
||||
const button = document.querySelector('form>button');
|
||||
button.innerHTML = `${message}`;
|
||||
button.setAttribute('disabled', 'true');
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
function urlB64ToUint8Array(base64String) {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/\-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
const rawData = window.atob(base64);
|
||||
const outputArray = new Uint8Array(rawData.length);
|
||||
const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));
|
||||
|
||||
return outputData;
|
||||
}
|
||||
|
||||
const subscribe = async (reg) => {
|
||||
const subscription = await reg.pushManager.getSubscription();
|
||||
if (subscription) {
|
||||
sendSubData(subscription);
|
||||
return;
|
||||
}
|
||||
|
||||
const vapidMeta = document.querySelector('meta[name="vapid-key"]');
|
||||
const key = vapidMeta.content;
|
||||
const options = {
|
||||
userVisibleOnly: true,
|
||||
// if key exists, create applicationServerKey property
|
||||
...(key && {applicationServerKey: urlB64ToUint8Array(key)})
|
||||
};
|
||||
|
||||
const sub = await reg.pushManager.subscribe(options);
|
||||
sendSubData(sub)
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const sendSubData = async (subscription) => {
|
||||
const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
|
||||
const data = {
|
||||
status_type: 'subscribe',
|
||||
subscription: subscription.toJSON(),
|
||||
browser: browser,
|
||||
};
|
||||
|
||||
const res = await fetch('/webpush/save_information', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
handleResponse(res);
|
||||
};
|
||||
|
||||
const handleResponse = (res) => {
|
||||
console.log(res.status);
|
||||
};
|
||||
|
||||
registerSw();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
const pushForm = document.getElementById('send-push__form');
|
||||
const errorMsg = document.querySelector('.error');
|
||||
|
||||
pushForm.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
const input = this[0];
|
||||
const textarea = this[1];
|
||||
const button = this[2];
|
||||
errorMsg.innerText = '';
|
||||
|
||||
const head = input.value;
|
||||
const body = textarea.value;
|
||||
const meta = document.querySelector('meta[name="user_id"]');
|
||||
const id = meta ? meta.content : null;
|
||||
|
||||
if (head && body && id) {
|
||||
button.innerText = 'Sending...';
|
||||
button.disabled = true;
|
||||
|
||||
const res = await fetch('/send_push', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({head, body, id}),
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (res.status === 200) {
|
||||
button.innerText = 'Send another 😃!';
|
||||
button.disabled = false;
|
||||
input.value = '';
|
||||
textarea.value = '';
|
||||
} else {
|
||||
errorMsg.innerText = res.message;
|
||||
button.innerText = 'Something broke 😢.. Try again?';
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let error;
|
||||
if (!head || !body){
|
||||
error = 'Please ensure you complete the form 🙏🏾'
|
||||
}
|
||||
else if (!id){
|
||||
error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"
|
||||
}
|
||||
errorMsg.innerText = error;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
18
server/templates/sw.js
Normal file
18
server/templates/sw.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Register event listener for the 'push' event.
|
||||
self.addEventListener('push', function (event) {
|
||||
// Retrieve the textual payload from event.data (a PushMessageData object).
|
||||
// Other formats are supported (ArrayBuffer, Blob, JSON), check out the documentation
|
||||
// on https://developer.mozilla.org/en-US/docs/Web/API/PushMessageData.
|
||||
const eventInfo = event.data.text();
|
||||
const data = JSON.parse(eventInfo);
|
||||
const head = data.head || 'New Notification 🕺🕺';
|
||||
const body = data.body || 'This is default content. Your notification didn\'t have one 🙄🙄';
|
||||
|
||||
// Keep the service worker alive until the notification is created.
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(head, {
|
||||
body: body,
|
||||
icon: 'https://i.imgur.com/MZM3K5w.png'
|
||||
})
|
||||
);
|
||||
});
|
@ -2,10 +2,17 @@ from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from .views import home, send_push
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^", include("djangoldp.urls")),
|
||||
url(r"^admin/", admin.site.urls),
|
||||
path('', home),
|
||||
path('send_push', send_push),
|
||||
path('webpush/', include('webpush.urls')),
|
||||
path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
36
server/views.py
Normal file
36
server/views.py
Normal file
@ -0,0 +1,36 @@
|
||||
from django.http.response import HttpResponse
|
||||
from django.views.decorators.http import require_GET
|
||||
from django.http.response import JsonResponse, HttpResponse
|
||||
from django.views.decorators.http import require_GET, require_POST
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.models import User
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from webpush import send_user_notification
|
||||
from django.conf import settings
|
||||
import json
|
||||
|
||||
@require_GET
|
||||
def home(request):
|
||||
webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {})
|
||||
vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY')
|
||||
user = request.user
|
||||
return render(request, 'home.html', {user: user, 'vapid_key': vapid_key})
|
||||
|
||||
@require_POST
|
||||
@csrf_exempt
|
||||
def send_push(request):
|
||||
try:
|
||||
body = request.body
|
||||
data = json.loads(body)
|
||||
|
||||
if 'head' not in data or 'body' not in data or 'id' not in data:
|
||||
return JsonResponse(status=400, data={"message": "Invalid data format"})
|
||||
|
||||
user_id = data['id']
|
||||
user = get_object_or_404(User, pk=user_id)
|
||||
payload = {'head': data['head'], 'body': data['body']}
|
||||
send_user_notification(user=user, payload=payload, ttl=1000)
|
||||
|
||||
return JsonResponse(status=200, data={"message": "Web push successful"})
|
||||
except TypeError:
|
||||
return JsonResponse(status=500, data={"message": "An error occurred"})
|
13
settings.yml
13
settings.yml
@ -1,5 +1,5 @@
|
||||
dependencies:
|
||||
|
||||
djangoldp_notifications
|
||||
ldppackages:
|
||||
|
||||
server:
|
||||
@ -11,12 +11,19 @@ server:
|
||||
default:
|
||||
ENGINE: django.db.backends.postgresql_psycopg2
|
||||
NAME: djangoldp
|
||||
USER: postgres
|
||||
PASSWORD: passw0rd
|
||||
LDP_RDF_CONTEXT: https://cdn.happy-dev.fr/owl/hdcontext.jsonld
|
||||
ROOT_URLCONF: server.urls
|
||||
STATIC_ROOT: static
|
||||
# STATIC_URL: = '/static/'
|
||||
# STATICFILES_DIRS: = '/Users/trav/Documents/gitnsurge/autonomic/startinblox/startinblox-startinoff'
|
||||
MEDIA_ROOT: media
|
||||
INSTALLED_APPS:
|
||||
- server
|
||||
- djangoldp_crypto # only needed by decentral1se for #236
|
||||
- webpush
|
||||
|
||||
|
||||
WEBPUSH_SETTINGS:
|
||||
VAPID_PUBLIC_KEY: "BIDVJ0sd4Cyycf_aGCxhQ_SmXBneWboI3wGO-Iyj3ofeGkvYyNp5o6W9eTf13YkJSz6NlRwiCHA08m8e82n5WXI"
|
||||
VAPID_PRIVATE_KEY: "zQ-Apj3yLGvcq-l_YNFMFgNVBxQ5_JYEsUbZA36Yhes"
|
||||
VAPID_ADMIN_EMAIL: "trav@teafry.me"
|
||||
|
Loading…
Reference in New Issue
Block a user