feat: Add automatic periodic location requests via silent push

- Server sends silent push notifications every 60s (configurable)
- Only requests from rooms with active participants
- Cleans up stale push subscriptions automatically
- Interval configurable via LOCATION_REQUEST_INTERVAL env var

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-29 01:27:00 +01:00
parent 27e8344e7a
commit ca1cd4877d
2 changed files with 48 additions and 0 deletions

View File

@ -9,6 +9,8 @@ services:
- VAPID_PUBLIC_KEY=BNWACJudUOeHEZKEFB-0Wz086nHYsWzj12LqQ7lsUNT38ThtNUoZTJYEH9lttQitCROE2G3Ob71ZUww47yvCDbk - VAPID_PUBLIC_KEY=BNWACJudUOeHEZKEFB-0Wz086nHYsWzj12LqQ7lsUNT38ThtNUoZTJYEH9lttQitCROE2G3Ob71ZUww47yvCDbk
- VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY:-x3yCse1Q4rbZ1XLgnJ1KpSuRlw2ccHDW0fMcKtQ1qcw} - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY:-x3yCse1Q4rbZ1XLgnJ1KpSuRlw2ccHDW0fMcKtQ1qcw}
- VAPID_SUBJECT=mailto:push@rmaps.online - VAPID_SUBJECT=mailto:push@rmaps.online
# Automatic location request interval (ms) - 0 to disable
- LOCATION_REQUEST_INTERVAL=60000
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
# HTTP router (redirects to HTTPS via Cloudflare) # HTTP router (redirects to HTTPS via Cloudflare)

View File

@ -6,6 +6,7 @@ import webpush from 'web-push';
const PORT = process.env.PORT || 3001; const PORT = process.env.PORT || 3001;
const STALE_THRESHOLD_MS = 60 * 60 * 1000; // 1 hour const STALE_THRESHOLD_MS = 60 * 60 * 1000; // 1 hour
const LOCATION_REQUEST_INTERVAL_MS = parseInt(process.env.LOCATION_REQUEST_INTERVAL || '60000', 10); // 60 seconds default
// VAPID keys for push notifications // VAPID keys for push notifications
// Generate with: npx web-push generate-vapid-keys // Generate with: npx web-push generate-vapid-keys
@ -292,6 +293,51 @@ setInterval(() => {
} }
}, 5 * 60 * 1000); // Every 5 minutes }, 5 * 60 * 1000); // Every 5 minutes
// Automatic location request - periodically ask all clients for location updates via silent push
async function requestLocationFromAllRooms() {
if (!VAPID_PUBLIC_KEY || !VAPID_PRIVATE_KEY) return;
for (const [roomSlug, subs] of pushSubscriptions.entries()) {
if (subs.size === 0) continue;
const room = rooms.get(roomSlug);
// Only request if room has participants (active room)
if (!room || Object.keys(room.participants).length === 0) continue;
console.log(`[${roomSlug}] Requesting location from ${subs.size} subscribers`);
const failedEndpoints = [];
for (const sub of subs) {
try {
await webpush.sendNotification(sub, JSON.stringify({
silent: true,
data: { type: 'location_request', roomSlug }
}));
} catch (error) {
if (error.statusCode === 404 || error.statusCode === 410) {
failedEndpoints.push(sub.endpoint);
}
}
}
// Clean up failed subscriptions
for (const endpoint of failedEndpoints) {
for (const sub of subs) {
if (sub.endpoint === endpoint) {
subs.delete(sub);
console.log(`[${roomSlug}] Removed stale push subscription`);
}
}
}
}
}
// Start automatic location request interval
if (VAPID_PUBLIC_KEY && VAPID_PRIVATE_KEY && LOCATION_REQUEST_INTERVAL_MS > 0) {
setInterval(requestLocationFromAllRooms, LOCATION_REQUEST_INTERVAL_MS);
console.log(`Automatic location requests enabled every ${LOCATION_REQUEST_INTERVAL_MS / 1000}s`);
}
// Parse JSON body from request // Parse JSON body from request
async function parseJsonBody(req) { async function parseJsonBody(req) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {