micro-mdx-parser
Version:
A tiny parser to convert markdown or html into JSON
163 lines (152 loc) • 94.9 kB
Markdown
Push notifications are a powerful and free browser feature that is often overlooked by products due to the complexity of service workers and the backend infra required to send notifications.
In this post we will discuss how to implement you own push notification service that will scale as you grow.
We will use service workers and AWS lambda functions to send notifications to a site saved as a PWA on the users phone
## Demo
Here is a demo of what we are building:
## Implementation
For this service we are using AWS lambda and deploying via the serverless framework. You can deploy this type of application in any kind of backend but I prefer the pay per execution model of the AWS serverless offerings.
### Frontend setup
Our frontend will be a debugger to test our push notification service.
<!-- doc-gen CODE src="https://github.com/DavidWells/saaslayer/blob/master/services/push-service/debugger/index.html" -->
<pre class="language-html" style="background-color:#fff;--shiki-dark-bg:transparent;color:#24292e;--shiki-dark:#F8F8F2" tabindex="0"><code class="language-html"><span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"><!</span><span style="color:#22863A;--shiki-dark:#FF79C6">DOCTYPE</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> html</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"><</span><span style="color:#22863A;--shiki-dark:#FF79C6">html</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> lang</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">en</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">head</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">meta</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> http-equiv</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Content-Type</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> content</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">text/html; charset=UTF-8</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">meta</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">viewport</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> content</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">width=device-width, initial-scale=1</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">title</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>Web Push Demo</</span><span style="color:#22863A;--shiki-dark:#FF79C6">title</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">meta</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">description</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> content</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Web Push Demo</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">link</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> rel</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">manifest</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> href</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">/manifest.json</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">link</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> rel</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">stylesheet</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> href</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">./assets/paper.min.css</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">link</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> rel</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">stylesheet</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> href</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">./assets/style.css</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">head</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">body</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">background</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">paper content</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">h2</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">heading</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">span</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> Web Push Debugger</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">span</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">status</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> Push supported: <</span><span style="color:#22863A;--shiki-dark:#FF79C6">u</span><span style="color:#24292E;--shiki-dark:#F8F8F2">><</span><span style="color:#22863A;--shiki-dark:#FF79C6">strong</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">push-notification-supported</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>true</</span><span style="color:#22863A;--shiki-dark:#FF79C6">strong</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></</span><span style="color:#22863A;--shiki-dark:#FF79C6">u</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> User consent: <</span><span style="color:#22863A;--shiki-dark:#FF79C6">u</span><span style="color:#24292E;--shiki-dark:#F8F8F2">><</span><span style="color:#22863A;--shiki-dark:#FF79C6">strong</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">push-notification-consent</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>granted</</span><span style="color:#22863A;--shiki-dark:#FF79C6">strong</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></</span><span style="color:#22863A;--shiki-dark:#FF79C6">u</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">h2</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">debugger</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">trigger</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>step 1: Ask user consent to show notifications</</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">button</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">ask-user-permission-button</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>Ask user permission</</span><span style="color:#22863A;--shiki-dark:#FF79C6">button</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>step 2: Subscribe to push notification</</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>this will create an endpoint that has to be sent to the push server</</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">button</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">create-notification-subscription-button</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>Create notification subscription</</span><span style="color:#22863A;--shiki-dark:#FF79C6">button</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>step 3: Send notification</</span><span style="color:#22863A;--shiki-dark:#FF79C6">h4</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>Fire push notification from server</</span><span style="color:#22863A;--shiki-dark:#FF79C6">p</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">form</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">send-push-form</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">input</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">title</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> value</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Message Title</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> placeholder</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Message Title</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> type</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">text</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> required</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">""</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">input</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">body</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> value</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Message Body</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> placeholder</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Message Body</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> type</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">text</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> required</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">""</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">input</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">data</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> value</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">{ "foo": "bar" }</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> placeholder</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Custom Data</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> type</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">text</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4"> <!-- <div></span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4"> <input name="delay" placeholder="Number of seconds to delay" type="text"></span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4"> </div> --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">textarea</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> name</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">subs</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">user-susbription</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></</span><span style="color:#22863A;--shiki-dark:#FF79C6">textarea</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">input</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">send-push-notification-button</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> type</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">submit</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> value</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Send a push notification 🚀</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">form</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">response</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">h3</span><span style="color:#24292E;--shiki-dark:#F8F8F2">>Web Push Custom Data:</</span><span style="color:#22863A;--shiki-dark:#FF79C6">h3</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">textarea</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> </span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> placeholder</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Push notification response data... This is shown when notification is clicked</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> </span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> id</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">web-notification</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> class</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">web-notification</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> disabled</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> ></</span><span style="color:#22863A;--shiki-dark:#FF79C6">textarea</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">div</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> <</span><span style="color:#22863A;--shiki-dark:#FF79C6">script</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> src</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">./assets/app.js</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#6F42C1;--shiki-dark:#50FA7B;font-style:inherit;--shiki-dark-font-style:italic"> type</span><span style="color:#24292E;--shiki-dark:#FF79C6">=</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">module</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></</span><span style="color:#22863A;--shiki-dark:#FF79C6">script</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> </</span><span style="color:#22863A;--shiki-dark:#FF79C6">body</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"></</span><span style="color:#22863A;--shiki-dark:#FF79C6">html</span><span style="color:#24292E;--shiki-dark:#F8F8F2">></span></span></code></pre>
<!-- end-doc-gen -->
Backend setup
<!-- doc-gen CODE src="https://github.com/DavidWells/saaslayer/blob/master/services/push-service/handler.js" -->
<pre class="language-js" style="background-color:#fff;--shiki-dark-bg:transparent;color:#24292e;--shiki-dark:#F8F8F2" tabindex="0"><code class="language-js"><span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">const</span><span style="color:#005CC5;--shiki-dark:#F8F8F2"> fs</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> =</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> require</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">fs</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">const</span><span style="color:#005CC5;--shiki-dark:#F8F8F2"> querystring</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> =</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> require</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">querystring</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">const</span><span style="color:#005CC5;--shiki-dark:#F8F8F2"> webPush</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> =</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> require</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">web-push</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">if</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> (</span><span style="color:#D73A49;--shiki-dark:#FF79C6">!</span><span style="color:#24292E;--shiki-dark:#F8F8F2">process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_PUBLIC_KEY</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> ||</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> !</span><span style="color:#24292E;--shiki-dark:#F8F8F2">process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_PRIVATE_KEY</span><span style="color:#24292E;--shiki-dark:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> console.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">log</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#032F62;--shiki-dark:#F1FA8C">You must set the VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY </span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> +</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#E9F284"> "</span><span style="color:#032F62;--shiki-dark:#F1FA8C">environment variables. You can use the following ones:</span><span style="color:#032F62;--shiki-dark:#E9F284">"</span><span style="color:#24292E;--shiki-dark:#F8F8F2">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> console.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">log</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(webPush.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">generateVAPIDKeys</span><span style="color:#24292E;--shiki-dark:#F8F8F2">());</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2">webPush.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">setVapidDetails</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_DOMAIN</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> ||</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_CONTACT</span><span style="color:#24292E;--shiki-dark:#F8F8F2">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_PUBLIC_KEY</span><span style="color:#24292E;--shiki-dark:#F8F8F2">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_PRIVATE_KEY</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">function</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> response</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#E36209;--shiki-dark:#FFB86C;font-style:inherit;--shiki-dark-font-style:italic">statusCode</span><span style="color:#24292E;--shiki-dark:#F8F8F2">, </span><span style="color:#E36209;--shiki-dark:#FFB86C;font-style:inherit;--shiki-dark-font-style:italic">body</span><span style="color:#24292E;--shiki-dark:#F8F8F2">, </span><span style="color:#E36209;--shiki-dark:#FFB86C;font-style:inherit;--shiki-dark-font-style:italic">file</span><span style="color:#24292E;--shiki-dark:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6"> let</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> payload </span><span style="color:#D73A49;--shiki-dark:#FF79C6">=</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> statusCode,</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4"> // api gateway cors headers</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> headers</span><span style="color:#24292E;--shiki-dark:#FF79C6">:</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#E9F284"> '</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Access-Control-Allow-Origin</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#FF79C6">:</span><span style="color:#032F62;--shiki-dark:#E9F284"> '</span><span style="color:#032F62;--shiki-dark:#F1FA8C">*</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">,</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#E9F284"> '</span><span style="color:#032F62;--shiki-dark:#F1FA8C">Access-Control-Allow-Credentials</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#FF79C6">:</span><span style="color:#005CC5;--shiki-dark:#BD93F9"> true</span><span style="color:#24292E;--shiki-dark:#F8F8F2">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> body</span><span style="color:#24292E;--shiki-dark:#FF79C6">:</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> typeof</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> (body) </span><span style="color:#D73A49;--shiki-dark:#FF79C6">===</span><span style="color:#032F62;--shiki-dark:#E9F284"> '</span><span style="color:#032F62;--shiki-dark:#F1FA8C">string</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> ?</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> body </span><span style="color:#D73A49;--shiki-dark:#FF79C6">:</span><span style="color:#005CC5;--shiki-dark:#BD93F9"> JSON</span><span style="color:#24292E;--shiki-dark:#F8F8F2">.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">stringify</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(body, </span><span style="color:#005CC5;--shiki-dark:#BD93F9">null</span><span style="color:#24292E;--shiki-dark:#F8F8F2">, </span><span style="color:#005CC5;--shiki-dark:#BD93F9">2</span><span style="color:#24292E;--shiki-dark:#F8F8F2">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2"> console.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">log</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#032F62;--shiki-dark:#F1FA8C">RESPOND</span><span style="color:#032F62;--shiki-dark:#E9F284">'</span><span style="color:#24292E;--shiki-dark:#F8F8F2">, payload);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6"> return</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> payload;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#8BE9FD;font-style:inherit;--shiki-dark-font-style:italic">module</span><span style="color:#24292E;--shiki-dark:#F8F8F2">.</span><span style="color:#005CC5;--shiki-dark:#8BE9FD;font-style:inherit;--shiki-dark-font-style:italic">exports</span><span style="color:#24292E;--shiki-dark:#F8F8F2">.</span><span style="color:#6F42C1;--shiki-dark:#50FA7B">vapidPublicKey</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> =</span><span style="color:#D73A49;--shiki-dark:#FF79C6"> async</span><span style="color:#24292E;--shiki-dark:#F8F8F2"> () </span><span style="color:#D73A49;--shiki-dark:#FF79C6">=></span><span style="color:#24292E;--shiki-dark:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6"> return</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> response</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#005CC5;--shiki-dark:#BD93F9">200</span><span style="color:#24292E;--shiki-dark:#F8F8F2">, process.env.</span><span style="color:#005CC5;--shiki-dark:#BD93F9">VAPID_PUBLIC_KEY</span><span style="color:#24292E;--shiki-dark:#F8F8F2">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// module.exports.register = async (event, context) => {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// // Save the registered users subscriptions (event.body)</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// await addSubscription(JSON.parse(event.body));</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// return response(201, event);</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// async function getSubscriptions() {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// const s = await s3.getObject({ Bucket: process.env.STATE_BUCKET, Key: 'subscriptions'}).promise();</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// return JSON.parse(s.Body.toString("utf-8"));</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// async function addSubscription(subscription) {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// let subscriptions = await getSubscriptions();</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// console.log(subscriptions);</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// let uniq = {};</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// for (let i=0; i < subscriptions.length; i++){</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// uniq[subscriptions[i].endpoint] = subscriptions[i];</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// }</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// uniq[subscription.endpoint] = subscription; // use latest for endpoint</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// await s3.putObject({</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// Bucket: process.env.STATE_BUCKET,</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// Key: 'subscriptions',</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// Body: JSON.stringify(Object.values(uniq)),</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// ContentType: 'application/json'</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// }).promise();</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6272A4">// TODO remove stale subscriptions </span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#FF79C6">function</span><span style="color:#6F42C1;--shiki-dark:#50FA7B"> send</span><span style="color:#24292E;--shiki-dark:#F8F8F2">(</span><span style="color:#E36209;--shiki-dark:#FFB86C;f