← 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;