Public
HTTP (deprecated)
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { modifyFetchHandler as codeOnValTownBanner } from "https://esm.town/v/andreterron/codeOnValTown";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth";
import { Hono } from "npm:hono@3";
// TODO: in an upcoming version of deno, you can also use npm:web-push which is much more popular
// by just using pushSendNotification
import pushSendNotification, { PushParams } from "https://esm.town/v/jrmann100/pushSendNotification_negrel";
import valStorage from "https://esm.town/v/jrmann100/valStorage";
import { verifyToken } from "https://esm.town/v/pomdtr/verifyToken?v=1";
import { thisWebURL } from "https://esm.town/v/stevekrouse/thisWebURL?v=2";
// .svg required for manifest, fav.farm doesn't seem to mind
const iconURL = "https://fav.farm/%F0%9F%94%94.svg";
const app = new Hono();
// key format is same as web-push's generateVAPIDKeys
const vapidDetails = {
url: thisWebURL(),
pubKey: Deno.env.get("pushVapidPublicKey"),
privKey: Deno.env.get("pushVapidPrivateKey"),
};
if (vapidDetails.privKey === undefined || vapidDetails.pubKey === undefined) {
throw new Error("You must set pubKey and privKey secrets!");
}
const storage = valStorage<PushSubscriptionJSON>();
app.get("/serviceWorker.js", (c) =>
c.text(
`self.addEventListener("push", (event) =>
event.waitUntil(self.registration.showNotification(...event.data?.json()))
);
addEventListener("notificationclick", (event) => {
if (typeof event.notification.data?.url === "string") {
clients.openWindow(event.notification.data?.url);
}
});`,
200,
{ "Content-Type": "text/javascript" },
));
app.get("/manifest.json", (c) =>
c.json({
name: "ValPush",
start_url: ".",
display: "standalone",
icons: [
{
src: iconURL,
sizes: "any",
purpose: "any",
},
],
}));
app.get("/subscription", async (c) => {
const subscription = await storage.get();
return c.json(subscription?.keys.p256dh ?? null);
});
app.get("/vapidPublicKey", (c) => c.text(vapidDetails.pubKey));
app.get("/", async (c) => {
const serverSubscription = await storage.get();
return c.html(`<!DOCTYPE html><html>
<head>
<title>ValPush</title>
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="${iconURL}">
<style>
html, body { height: 100%; }
body { display: flex; align-items: center; justify-content: center; padding: 0.5rem; font-size: 1.2rem; font-family: -system-ui, sans-serif; }
button { text-align: left; padding: 0.5rem; font: inherit; }
</style>
</head>
<body>
<div>
<p id="status"></p>
<button disabled id="button">Loading&hellip;</button>
</div>
<script type="module">
import pushFrontend from "https://esm.town/v/jrmann100/pushFrontend"
pushFrontend();
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/philfung/add-to-homescreen@1.9/dist/add-to-homescreen.min.css">
<script src="https://cdn.jsdelivr.net/gh/philfung/add-to-homescreen@1.9/dist/add-to-homescreen.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
window.AddToHomeScreenInstance = new window.AddToHomeScreen({
appName: 'ValPush',
appIconUrl: '${iconURL}',
assetUrl: 'https://cdn.jsdelivr.net/gh/philfung/add-to-homescreen@1.9/dist/assets/img/', // Link to directory of library image assets.
maxModalDisplayCount: -1
});
ret = window.AddToHomeScreenInstance.show('en');
});
jrmann100-push.web.val.run
September 1, 2024