← back to Watches
pwa-routes.js
292 lines
/**
* PWA-specific routes for Omega Watches
* Handles push subscriptions, offline sync, and app features
*/
import express from 'express';
import pushNotifications from './push-notifications.js';
const router = express.Router();
/**
* GET /api/pwa/vapid-public-key
* Get VAPID public key for push subscription
*/
router.get('/vapid-public-key', (req, res) => {
res.json({
publicKey: pushNotifications.VAPID_PUBLIC_KEY
});
});
/**
* POST /api/pwa/subscribe
* Subscribe to push notifications
*/
router.post('/subscribe', (req, res) => {
try {
const { userId, subscription } = req.body;
if (!subscription) {
return res.status(400).json({ error: 'Subscription object is required' });
}
// Use userId from body or generate from subscription endpoint
const id = userId || subscription.endpoint;
pushNotifications.saveSubscription(id, subscription);
res.json({
success: true,
message: 'Subscription saved successfully'
});
} catch (error) {
console.error('[PWA] Subscribe error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/unsubscribe
* Unsubscribe from push notifications
*/
router.post('/unsubscribe', (req, res) => {
try {
const { userId, endpoint } = req.body;
const id = userId || endpoint;
if (!id) {
return res.status(400).json({ error: 'userId or endpoint is required' });
}
const removed = pushNotifications.removeSubscription(id);
res.json({
success: removed,
message: removed ? 'Unsubscribed successfully' : 'Subscription not found'
});
} catch (error) {
console.error('[PWA] Unsubscribe error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/send-notification
* Send push notification (admin only in production)
*/
router.post('/send-notification', async (req, res) => {
try {
const { userId, title, body, options } = req.body;
if (!title || !body) {
return res.status(400).json({ error: 'title and body are required' });
}
const result = await pushNotifications.sendCustomNotification(
userId || 'all',
title,
body,
options
);
res.json({
success: true,
result
});
} catch (error) {
console.error('[PWA] Send notification error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/price-alert
* Send price alert notification
*/
router.post('/price-alert', async (req, res) => {
try {
const { userId, watchData, priceChange } = req.body;
if (!userId || !watchData || priceChange === undefined) {
return res.status(400).json({
error: 'userId, watchData, and priceChange are required'
});
}
await pushNotifications.sendPriceAlert(userId, watchData, priceChange);
res.json({
success: true,
message: 'Price alert sent'
});
} catch (error) {
console.error('[PWA] Price alert error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* GET /api/pwa/subscription-stats
* Get subscription statistics
*/
router.get('/subscription-stats', (req, res) => {
try {
const stats = pushNotifications.getSubscriptionStats();
res.json(stats);
} catch (error) {
console.error('[PWA] Stats error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/sync-watchlist
* Sync user watchlist from offline storage
*/
router.post('/sync-watchlist', (req, res) => {
try {
const { watchlist } = req.body;
if (!Array.isArray(watchlist)) {
return res.status(400).json({ error: 'watchlist must be an array' });
}
// In production, save to database
console.log('[PWA] Watchlist synced:', watchlist.length, 'items');
res.json({
success: true,
synced: watchlist.length,
message: 'Watchlist synced successfully'
});
} catch (error) {
console.error('[PWA] Sync watchlist error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/sync-favorites
* Sync user favorites from offline storage
*/
router.post('/sync-favorites', (req, res) => {
try {
const { favorites } = req.body;
if (!Array.isArray(favorites)) {
return res.status(400).json({ error: 'favorites must be an array' });
}
console.log('[PWA] Favorites synced:', favorites.length, 'items');
res.json({
success: true,
synced: favorites.length,
message: 'Favorites synced successfully'
});
} catch (error) {
console.error('[PWA] Sync favorites error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/scan-result
* Save camera scan result
*/
router.post('/scan-result', (req, res) => {
try {
const { referenceNumber, confidence, imageData } = req.body;
if (!referenceNumber) {
return res.status(400).json({ error: 'referenceNumber is required' });
}
// In production, save scan to database
console.log('[PWA] Scan saved:', referenceNumber, 'confidence:', confidence);
res.json({
success: true,
message: 'Scan result saved'
});
} catch (error) {
console.error('[PWA] Save scan error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/pwa/ar-screenshot
* Save AR try-on screenshot
*/
router.post('/ar-screenshot', (req, res) => {
try {
const { watchId, imageData } = req.body;
if (!watchId || !imageData) {
return res.status(400).json({ error: 'watchId and imageData are required' });
}
// In production, save to cloud storage
console.log('[PWA] AR screenshot saved for watch:', watchId);
res.json({
success: true,
message: 'Screenshot saved',
url: `/screenshots/ar-${watchId}-${Date.now()}.jpg`
});
} catch (error) {
console.error('[PWA] Save screenshot error:', error);
res.status(500).json({ error: error.message });
}
});
/**
* GET /api/pwa/app-info
* Get PWA application info
*/
router.get('/app-info', (req, res) => {
res.json({
name: 'Omega Watch Price History',
version: '2.0.0',
features: {
offlineMode: true,
pushNotifications: true,
cameraScanner: true,
biometricAuth: true,
arTryOn: true,
backgroundSync: true
},
platform: {
serviceWorker: true,
webPush: true,
webAuthn: true,
mediaDevices: true,
webGL: true,
indexedDB: true
}
});
});
/**
* POST /share
* Handle Web Share Target
*/
router.post('/share', (req, res) => {
try {
const { title, text, url, image } = req.body;
console.log('[PWA] Share received:', { title, text, url });
// Process shared content
// In production, save or redirect to appropriate page
res.redirect(`/pwa-index.html?shared=true&title=${encodeURIComponent(title || '')}`);
} catch (error) {
console.error('[PWA] Share error:', error);
res.status(500).json({ error: error.message });
}
});
export default router;