Initial checkin

This commit is contained in:
2026-06-02 23:00:35 +10:00
commit 6f5be7b451
8 changed files with 1312 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
// server.js — minimal Web Push backend.
//
// Setup:
// npm init -y
// npm install express web-push
// npx web-push generate-vapid-keys # paste the two keys below
// node server.js
//
// Serves the static test page from ./public, stores subscriptions in memory,
// and exposes /api/send to push to every stored subscription.
const express = require('express');
const webpush = require('web-push');
const path = require('path');
const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// --- VAPID keys -----------------------------------------------------------
// Paste the output of `npx web-push generate-vapid-keys` here.
const VAPID_PUBLIC = 'BDjn64xCA8SGTAZy8Tes-Kqm44sgFTyZlgXRfpeKvUjSSFdnfI4ru0jY64JExOlfV4qNke7kqKb6B_sBUmbTdIY';
const VAPID_PRIVATE = 'u2cXEA3HC2FUY7f4FedQNqnAcOeFdfG655WK_ktYWbQ';
webpush.setVapidDetails(
'mailto:simon@unisolve.com.au', // contact the push service can reach if there's a problem
VAPID_PUBLIC,
VAPID_PRIVATE
);
// Expose the public key so the page can subscribe without hardcoding it.
app.get('/api/vapidPublicKey', (req, res) => res.send(VAPID_PUBLIC));
// --- Subscription store ---------------------------------------------------
// In-memory for testing. Use a real table (one row per device) in production.
const subscriptions = new Map(); // endpoint -> subscription object
app.post('/api/subscribe', (req, res) => {
const sub = req.body;
if (!sub || !sub.endpoint) return res.status(400).json({ error: 'bad subscription' });
subscriptions.set(sub.endpoint, sub);
console.log('Stored subscription. Total:', subscriptions.size);
res.status(201).json({ ok: true });
});
// --- Send a push to everyone ----------------------------------------------
// POST /api/send { "title": "...", "body": "..." }
app.post('/api/send', async (req, res) => {
const payload = JSON.stringify({
title: req.body.title || 'Test push',
body: req.body.body || 'Sent from the server at ' + new Date().toLocaleTimeString()
});
const results = [];
for (const [endpoint, sub] of subscriptions) {
try {
await webpush.sendNotification(sub, payload);
results.push({ endpoint, ok: true });
} catch (err) {
// 404/410 mean the subscription is dead — drop it so we stop trying.
if (err.statusCode === 404 || err.statusCode === 410) {
subscriptions.delete(endpoint);
results.push({ endpoint, ok: false, removed: true });
} else {
results.push({ endpoint, ok: false, status: err.statusCode });
}
}
}
res.json({ sent: results.length, results });
});
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => console.log(`Listening on http://localhost:${PORT}`));