Cleaned up code a bit
This commit is contained in:
		
							parent
							
								
									4350714534
								
							
						
					
					
						commit
						d966c9529b
					
				| @ -1,62 +1,49 @@ | |||||||
| // this.onpush = (event) => {
 | export const registerServiceWorker = async (silent = false) => { | ||||||
| //             console.log(event.data);
 |     try { | ||||||
| //             // From here we can write the data to IndexedDB, send it to any open
 |         const serviceWorkerRegistration = await navigator.serviceWorker | ||||||
| //             // windows, display a notification, etc.
 |             .register("/service-worker.js") | ||||||
| //         };
 |  | ||||||
| 
 | 
 | ||||||
| function arrayBufferToBase64(buffer) { |         await serviceWorkerRegistration.update() | ||||||
|     return btoa(String.fromCharCode(...new Uint8Array(buffer))) | 
 | ||||||
|  |         const keyResponse = await fetch('/push.json') | ||||||
|  |         const keyData = await keyResponse.json() | ||||||
|  | 
 | ||||||
|  |         const publicKey = Uint8Array.from(atob(keyData.publicKey), c => c.charCodeAt(0)) | ||||||
|  | 
 | ||||||
|  |         const pushSubscription = await serviceWorkerRegistration.pushManager.subscribe({ | ||||||
|  |             userVisibleOnly: true, applicationServerKey: publicKey, | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         const subscriptionObject = { | ||||||
|  |             ...pushSubscription.toJSON(), encoding: PushManager.supportedContentEncodings, | ||||||
|  |         }; | ||||||
|  |         console.log(pushSubscription.endpoint, pushSubscription, pushSubscription.toJSON(), subscriptionObject); | ||||||
|  | 
 | ||||||
|  |         const response = await fetch('/push.json', { | ||||||
|  |             method: 'POST', headers: { | ||||||
|  |                 'Content-Type': 'application/json', | ||||||
|  |             }, body: JSON.stringify(subscriptionObject), | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         if (!response.ok) { | ||||||
|  |             throw new Error('Bad status code from server.'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const responseData = await response.json(); | ||||||
|  |         console.log('Registration response', responseData); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error("Error registering service worker:", error); | ||||||
|  |         if (!silent) { | ||||||
|  |             alert("Registering push notifications failed. Please check your browser settings and try again.\n\n" + error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const keyResponse = await fetch('/push.json') |  | ||||||
| const keyData = await keyResponse.json() |  | ||||||
| 
 | 
 | ||||||
| console.log("Key data", keyData); | window.registerNotificationsServiceWorker = () => { | ||||||
| 
 |  | ||||||
| const publicKey = Uint8Array.from(atob(keyData.publicKey), c => c.charCodeAt(0)) |  | ||||||
| 
 |  | ||||||
| export const registerServiceWorker = async () => { |  | ||||||
|     navigator.serviceWorker |  | ||||||
|         .register("/service-worker.js") |  | ||||||
|         .then((serviceWorkerRegistration) => { |  | ||||||
|             console.log(serviceWorkerRegistration); |  | ||||||
|             serviceWorkerRegistration.pushManager.subscribe({ |  | ||||||
|                 userVisibleOnly: true, |  | ||||||
|                 applicationServerKey: publicKey, |  | ||||||
|             }).then( |  | ||||||
|                 (pushSubscription) => { |  | ||||||
|                     const subscriptionObject = { |  | ||||||
|                         ...pushSubscription.toJSON(), |  | ||||||
|                         encoding: PushManager.supportedContentEncodings, |  | ||||||
|                         /* other app-specific data, such as user identity */ |  | ||||||
|                     }; |  | ||||||
|                     console.log(pushSubscription.endpoint, pushSubscription, pushSubscription.toJSON(), subscriptionObject); |  | ||||||
| 
 |  | ||||||
|                     fetch('/push.json', { |  | ||||||
|                         method: 'POST', |  | ||||||
|                         headers: { |  | ||||||
|                             'Content-Type': 'application/json', |  | ||||||
|                         }, |  | ||||||
|                         body: JSON.stringify(subscriptionObject), |  | ||||||
|                     }).then((response) => { |  | ||||||
|                         if (!response.ok) { |  | ||||||
|                             throw new Error('Bad status code from server.'); |  | ||||||
|                         } |  | ||||||
|                         return response.json(); |  | ||||||
|                     }).then((responseData) => { |  | ||||||
|                         console.log(responseData); |  | ||||||
|                     }); |  | ||||||
|                 }, |  | ||||||
|                 (error) => { |  | ||||||
|                     console.error(error); |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| window.registerServiceWorker = () => { |  | ||||||
|     return Notification.requestPermission().then((permission) => { |     return Notification.requestPermission().then((permission) => { | ||||||
|         if (permission === "granted") { |         if (permission === "granted") { | ||||||
|  |             console.log("Permission was granted"); | ||||||
|             return registerServiceWorker(); |             return registerServiceWorker(); | ||||||
|         } else if (permission === "denied") { |         } else if (permission === "denied") { | ||||||
|             console.log("Permission was denied"); |             console.log("Permission was denied"); | ||||||
| @ -65,4 +52,4 @@ window.registerServiceWorker = () => { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| }; | }; | ||||||
| registerServiceWorker().catch(console.error); | registerServiceWorker(true).catch(console.error); | ||||||
|  | |||||||
| @ -1,83 +1,66 @@ | |||||||
| async function requestNotificationPermission() { | self.addEventListener("install", (event) => { | ||||||
|   const permission = await Notification.requestPermission(); |     console.log("Service worker installing..."); | ||||||
|   return permission === "granted"; |     event.waitUntil( | ||||||
| } |         caches.open("snek-cache").then((cache) => { | ||||||
|  |             return cache.addAll([]); | ||||||
|  |         }) | ||||||
|  |     ); | ||||||
|  | }) | ||||||
| 
 | 
 | ||||||
| // Subscribe to Push Notifications
 | self.addEventListener("activate", (event) => { | ||||||
| async function subscribeUser() { |   event.waitUntil(self.registration?.navigationPreload.enable()); | ||||||
|   const registration = | }); | ||||||
|     await navigator.serviceWorker.register("/service-worker.js"); |  | ||||||
|   const subscription = await registration.pushManager.subscribe({ |  | ||||||
|     userVisibleOnly: true, |  | ||||||
|     applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY), |  | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   // Send subscription to your backend
 | self.addEventListener("push", (event) => { | ||||||
|   await fetch("/subscribe", { |     if (!self.Notification || self.Notification.permission !== "granted") { | ||||||
|     method: "POST", |         console.log("Notification permission not granted"); | ||||||
|     body: JSON.stringify(subscription), |         return; | ||||||
|     headers: { |     } | ||||||
|       "Content-Type": "application/json", |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // Service Worker (service-worker.js)
 |     const data = event.data?.json() ?? {}; | ||||||
| // self.addEventListener("push", (event) => {
 |     console.log("Received a push message", event, data); | ||||||
| //   const data = event.data.json();
 |  | ||||||
| //   self.registration.showNotification(data.title, {
 |  | ||||||
| //     body: data.message,
 |  | ||||||
| //     icon: data.icon,
 |  | ||||||
| //   });
 |  | ||||||
| // });
 |  | ||||||
| 
 | 
 | ||||||
|  |     const title = data.title || "Something Has Happened"; | ||||||
|  |     const message = | ||||||
|  |         data.message || "Here's something you might want to check out."; | ||||||
|  |     const icon = data.icon || "/image/snek512.png"; | ||||||
| 
 | 
 | ||||||
| self.addEventListener("push",  (event) => { |     const notificationSettings = data.notificationSettings || {}; | ||||||
|   if (!self.Notification || self.Notification.permission !== "granted") { |  | ||||||
|     console.log("Notification permission not granted"); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   const data = event.data?.json() ?? {}; |     console.log("Showing message", title, message, icon); | ||||||
|   console.log("Received a push message", event, data); |  | ||||||
| 
 | 
 | ||||||
|   const title = data.title || "Something Has Happened"; |     const reg = self.registration.showNotification(title, { | ||||||
|   const message = |         body: message, | ||||||
|     data.message || "Here's something you might want to check out."; |         tag: "message-received", | ||||||
|   const icon = data.icon || "images/new-notification.png"; |         icon, | ||||||
|   console.log("showing message", title, message, icon); |         badge: icon, | ||||||
| 
 |         ...notificationSettings, | ||||||
|   const reg = self.registration.showNotification(title, { |         data, | ||||||
|     body: message, |     }).then(e => console.log("Showing notification", e)).catch(console.error); | ||||||
|     tag: "simple-push-demo-notification", |  | ||||||
|     icon, |  | ||||||
|   }).then(e => console.log("success", e)).catch(console.error); |  | ||||||
| 
 |  | ||||||
|   event.waitUntil(reg); |  | ||||||
| 
 | 
 | ||||||
|  |     event.waitUntil(reg); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| self.addEventListener("notificationclick", (event) => { | self.addEventListener("notificationclick", (event) => { | ||||||
|     console.log("Notification click Received.", event); |     console.log("Notification click Received.", event); | ||||||
|     event.notification.close(); |     event.notification.close(); | ||||||
|     event.waitUntil(clients.openWindow( |     event.waitUntil(clients.openWindow(`${event.notification.data.url || event.notification.data.link || `/web.html`}`)); | ||||||
|       "https://snek.molodetz.nl",)); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| self.addEventListener("notificationclose", (event) => { | self.addEventListener("notificationclose", (event) => { | ||||||
| 
 |     console.log("Notification closed", event); | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| self.addEventListener("fetch", (event) => { | self.addEventListener("fetch", (event) => { | ||||||
|     // console.log("Fetch event for ", event.request.url);
 |     // console.log("Fetch event for ", event.request.url);
 | ||||||
|     event.respondWith( |     event.respondWith( | ||||||
|         caches.match(event.request).then((response) => { |         caches.match(event.request).then((response) => { | ||||||
|         if (response) { |             if (response) { | ||||||
|             // console.log("Found response in cache: ", response);
 |                 // console.log("Found response in cache: ", response);
 | ||||||
|             return response; |                 return response; | ||||||
|         } |             } | ||||||
|         // console.log("No response found in cache. About to fetch from network...");
 |             // console.log("No response found in cache. About to fetch from network...");
 | ||||||
|         return fetch(event.request); |             return fetch(event.request); | ||||||
|         }) |         }) | ||||||
|     ); |     ); | ||||||
| }) | }) | ||||||
| @ -2,6 +2,7 @@ import time | |||||||
| import base64 | import base64 | ||||||
| import uuid | import uuid | ||||||
| from functools import cache | from functools import cache | ||||||
|  | from pathlib import Path | ||||||
| 
 | 
 | ||||||
| import jwt | import jwt | ||||||
| from cryptography.hazmat.primitives.asymmetric import ec | from cryptography.hazmat.primitives.asymmetric import ec | ||||||
| @ -14,66 +15,55 @@ from cryptography.hazmat.primitives.ciphers.aead import AESGCM | |||||||
| from cryptography.hazmat.primitives.hashes import SHA256 | from cryptography.hazmat.primitives.hashes import SHA256 | ||||||
| from cryptography.hazmat.primitives.kdf.hkdf import HKDF | from cryptography.hazmat.primitives.kdf.hkdf import HKDF | ||||||
| 
 | 
 | ||||||
| PRIVATE_KEY_FILE = "./notification-private.pem" | # The only reason to persist the keys is to be able to use them in the web push | ||||||
| PRIVATE_KEY_PKCS8_FILE = "./notification-private.pkcs8.pem" | 
 | ||||||
| PUBLIC_KEY_FILE = "./notification-public.pem" | PRIVATE_KEY_FILE = Path("./notification-private.pem") | ||||||
|  | PRIVATE_KEY_PKCS8_FILE = Path("./notification-private.pkcs8.pem") | ||||||
|  | PUBLIC_KEY_FILE = Path("./notification-public.pem") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def generate_private_key(): | def generate_private_key(): | ||||||
|     if not os.path.isfile(PRIVATE_KEY_FILE): |     if not PRIVATE_KEY_FILE.exists(): | ||||||
|         private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) |         private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) | ||||||
| 
 | 
 | ||||||
|         # Serialize the private key to PEM format |  | ||||||
|         pem = private_key.private_bytes( |         pem = private_key.private_bytes( | ||||||
|             encoding=serialization.Encoding.PEM, |             encoding=serialization.Encoding.PEM, | ||||||
|             format=serialization.PrivateFormat.TraditionalOpenSSL, |             format=serialization.PrivateFormat.TraditionalOpenSSL, | ||||||
|             encryption_algorithm=serialization.NoEncryption(), |             encryption_algorithm=serialization.NoEncryption(), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Write the private key to a file |         PRIVATE_KEY_FILE.write_bytes(pem) | ||||||
|         with open(PRIVATE_KEY_FILE, "wb") as pem_out: |  | ||||||
|             pem_out.write(pem) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def generate_pcks8_private_key(): | def generate_pcks8_private_key(): | ||||||
|     # openssl pkcs8 -topk8 -nocrypt -in private.pem -out private.pkcs8.pem |     if not PRIVATE_KEY_PKCS8_FILE.exists(): | ||||||
|     if not os.path.isfile(PRIVATE_KEY_PKCS8_FILE): |         private_key = serialization.load_pem_private_key( | ||||||
|         with open(PRIVATE_KEY_FILE, "rb") as pem_in: |             PRIVATE_KEY_FILE.read_bytes(), password=None, backend=default_backend() | ||||||
|             private_key = serialization.load_pem_private_key( |         ) | ||||||
|                 pem_in.read(), password=None, backend=default_backend() |  | ||||||
|             ) |  | ||||||
| 
 | 
 | ||||||
|         # Serialize the private key to PKCS8 format |  | ||||||
|         pem = private_key.private_bytes( |         pem = private_key.private_bytes( | ||||||
|             encoding=serialization.Encoding.PEM, |             encoding=serialization.Encoding.PEM, | ||||||
|             format=serialization.PrivateFormat.PKCS8, |             format=serialization.PrivateFormat.PKCS8, | ||||||
|             encryption_algorithm=serialization.NoEncryption(), |             encryption_algorithm=serialization.NoEncryption(), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Write the private key to a file |         PRIVATE_KEY_PKCS8_FILE.write_bytes(pem) | ||||||
|         with open(PRIVATE_KEY_PKCS8_FILE, "wb") as pem_out: |  | ||||||
|             pem_out.write(pem) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def generate_public_key(): | def generate_public_key(): | ||||||
|     if not os.path.isfile(PUBLIC_KEY_FILE): |     if not PUBLIC_KEY_FILE.exists(): | ||||||
|         with open(PRIVATE_KEY_FILE, "rb") as pem_in: |         private_key = serialization.load_pem_private_key( | ||||||
|             private_key = serialization.load_pem_private_key( |             PRIVATE_KEY_FILE.read_bytes(), password=None, backend=default_backend() | ||||||
|                 pem_in.read(), password=None, backend=default_backend() |         ) | ||||||
|             ) |  | ||||||
| 
 | 
 | ||||||
|         # Get the public key from the private key |  | ||||||
|         public_key = private_key.public_key() |         public_key = private_key.public_key() | ||||||
| 
 | 
 | ||||||
|         # Serialize the public key to PEM format |  | ||||||
|         pem = public_key.public_bytes( |         pem = public_key.public_bytes( | ||||||
|             encoding=serialization.Encoding.PEM, |             encoding=serialization.Encoding.PEM, | ||||||
|             format=serialization.PublicFormat.SubjectPublicKeyInfo, |             format=serialization.PublicFormat.SubjectPublicKeyInfo, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Write the public key to a file |         PUBLIC_KEY_FILE.write_bytes(pem) | ||||||
|         with open(PUBLIC_KEY_FILE, "wb") as pem_out: |  | ||||||
|             pem_out.write(pem) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def ensure_certificates(): | def ensure_certificates(): | ||||||
| @ -92,41 +82,37 @@ def hkdf(input_key, salt, info, length): | |||||||
|     ).derive(input_key) |     ).derive(input_key) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def _browser_base64(data): | ||||||
|  |     return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class Notifications: | class Notifications: | ||||||
|     private_key_pem = None |     private_key_pem = None | ||||||
|     public_key = None |     public_key = None | ||||||
|     public_key_jwk = None |     public_key_base64 = None | ||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         ensure_certificates() |         ensure_certificates() | ||||||
|         with open(PRIVATE_KEY_FILE, "rb") as pem_in: |  | ||||||
|             private_key = serialization.load_pem_private_key( |  | ||||||
|                 pem_in.read(), password=None, backend=default_backend() |  | ||||||
|             ) |  | ||||||
|             self.private_key_pem = private_key.private_bytes( |  | ||||||
|                 encoding=serialization.Encoding.PEM, |  | ||||||
|                 format=serialization.PrivateFormat.TraditionalOpenSSL, |  | ||||||
|                 encryption_algorithm=serialization.NoEncryption(), |  | ||||||
|             ) |  | ||||||
| 
 | 
 | ||||||
|         with open(PUBLIC_KEY_FILE, "rb") as pem_in: |         private_key = serialization.load_pem_private_key( | ||||||
|             self.public_key = serialization.load_pem_public_key( |             PRIVATE_KEY_FILE.read_bytes(), password=None, backend=default_backend() | ||||||
|                 pem_in.read(), backend=default_backend() |         ) | ||||||
|  |         self.private_key_pem = private_key.private_bytes( | ||||||
|  |             encoding=serialization.Encoding.PEM, | ||||||
|  |             format=serialization.PrivateFormat.TraditionalOpenSSL, | ||||||
|  |             encryption_algorithm=serialization.NoEncryption(), | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         self.public_key = serialization.load_pem_public_key( | ||||||
|  |             PUBLIC_KEY_FILE.read_bytes(), backend=default_backend() | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         self.public_key_base64 = _browser_base64( | ||||||
|  |             self.public_key.public_bytes( | ||||||
|  |                 encoding=serialization.Encoding.X962, | ||||||
|  |                 format=serialization.PublicFormat.UncompressedPoint, | ||||||
|             ) |             ) | ||||||
|             public_numbers = self.public_key.public_numbers() |         ) | ||||||
| 
 |  | ||||||
|             self.public_key_jwk = { |  | ||||||
|                 "kty": "EC", |  | ||||||
|                 "crv": "P-256", |  | ||||||
|                 "x": base64.urlsafe_b64encode( |  | ||||||
|                     public_numbers.x.to_bytes(32, byteorder="big") |  | ||||||
|                 ).decode("utf-8"), |  | ||||||
|                 "y": base64.urlsafe_b64encode( |  | ||||||
|                     public_numbers.y.to_bytes(32, byteorder="big") |  | ||||||
|                 ).decode("utf-8"), |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         print(f"Public key JWK: {self.public_key_jwk}") |  | ||||||
| 
 | 
 | ||||||
|     def create_notification_authorization(self, push_url): |     def create_notification_authorization(self, push_url): | ||||||
|         target = urlparse(push_url) |         target = urlparse(push_url) | ||||||
| @ -152,7 +138,7 @@ class Notifications: | |||||||
|             algorithm="ES256", |             algorithm="ES256", | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def create_encrypted_payload(self, auth: str, p256dh:str, payload:str): |     def create_encrypted_payload(self, auth: str, p256dh: str, payload: str): | ||||||
|         # 1. Generate private key |         # 1. Generate private key | ||||||
|         private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) |         private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) | ||||||
| 
 | 
 | ||||||
| @ -161,13 +147,13 @@ class Notifications: | |||||||
| 
 | 
 | ||||||
|         salt = os.urandom(16) |         salt = os.urandom(16) | ||||||
| 
 | 
 | ||||||
|         subscription_pub_key_bytes = base64.urlsafe_b64decode(p256dh+ '==') |         subscription_pub_key_bytes = base64.urlsafe_b64decode(p256dh + "====") | ||||||
|         subscription_public_key = ec.EllipticCurvePublicKey.from_encoded_point( |         subscription_public_key = ec.EllipticCurvePublicKey.from_encoded_point( | ||||||
|             ec.SECP256R1(), subscription_pub_key_bytes |             ec.SECP256R1(), subscription_pub_key_bytes | ||||||
|         ) |         ) | ||||||
|         shared_secret = private_key.exchange(ec.ECDH(), subscription_public_key) |         shared_secret = private_key.exchange(ec.ECDH(), subscription_public_key) | ||||||
| 
 | 
 | ||||||
|         auth_dec = base64.b64decode(auth+ '==') |         auth_dec = base64.urlsafe_b64decode(auth + "==") | ||||||
|         auth_enc = b"Content-Encoding: auth\x00" |         auth_enc = b"Content-Encoding: auth\x00" | ||||||
|         prk = hkdf(shared_secret, auth_dec, auth_enc, 32) |         prk = hkdf(shared_secret, auth_dec, auth_enc, 32) | ||||||
| 
 | 
 | ||||||
| @ -175,7 +161,8 @@ class Notifications: | |||||||
|         key_label = b"P-256\x00" |         key_label = b"P-256\x00" | ||||||
|         subscription_len = len(subscription_pub_key_bytes).to_bytes(2, "big") |         subscription_len = len(subscription_pub_key_bytes).to_bytes(2, "big") | ||||||
|         local_pub_bytes = public_key.public_bytes( |         local_pub_bytes = public_key.public_bytes( | ||||||
|             encoding=serialization.Encoding.X962, format=serialization.PublicFormat.UncompressedPoint |             encoding=serialization.Encoding.X962, | ||||||
|  |             format=serialization.PublicFormat.UncompressedPoint, | ||||||
|         ) |         ) | ||||||
|         local_len = len(local_pub_bytes).to_bytes(2, "big") |         local_len = len(local_pub_bytes).to_bytes(2, "big") | ||||||
|         context = ( |         context = ( | ||||||
| @ -208,18 +195,6 @@ class Notifications: | |||||||
|             'public_key': local_pub_bytes |             'public_key': local_pub_bytes | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     @property |  | ||||||
|     def public_key_base64(self): |  | ||||||
|         return ( |  | ||||||
|             base64.urlsafe_b64encode( |  | ||||||
|                 self.public_key.public_bytes( |  | ||||||
|                     encoding=serialization.Encoding.X962, |  | ||||||
|                     format=serialization.PublicFormat.UncompressedPoint, |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|             .decode("utf-8") |  | ||||||
|             .rstrip("=") |  | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
| @cache | @cache | ||||||
| def get_notifications(): | def get_notifications(): | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ | |||||||
|       <a class="no-select" style="display:none" id="install-button" href="#">📥</a> |       <a class="no-select" style="display:none" id="install-button" href="#">📥</a> | ||||||
|       <a class="no-select" href="/threads.html">👥</a> |       <a class="no-select" href="/threads.html">👥</a> | ||||||
|       <a class="no-select" href="/settings/index.html">⚙️</a> |       <a class="no-select" href="/settings/index.html">⚙️</a> | ||||||
|       <a class="no-select" href="#" onclick="registerServiceWorker">✉️</a> |       <a class="no-select" href="#" onclick="registerNotificationsServiceWorker()">✉️</a> | ||||||
|       <a class="no-select" href="/logout.html">🔒</a> |       <a class="no-select" href="/logout.html">🔒</a> | ||||||
|     </nav> |     </nav> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -82,8 +82,8 @@ class PushView(BaseFormView): | |||||||
|             test_payload = { |             test_payload = { | ||||||
|                 "title": "Hey retoor", |                 "title": "Hey retoor", | ||||||
|                 "message": "Guess what? ;P", |                 "message": "Guess what? ;P", | ||||||
|                 "icon": "https://molodetz.online/image/snek192.png", |                 "icon": "/image/snek192.png", | ||||||
|                 "url": "https://localhost:8081", |                 "url": "/web.html", | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             encryped = notifications.create_encrypted_payload( |             encryped = notifications.create_encrypted_payload( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 BordedDev
						BordedDev