- Add migration 010_create_notification_history.sql with indexes - Update notifications.js to log all sent/skipped/failed notifications - Add getNotificationHistory() function for querying history - Add GET /api/notification-settings/history endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
230 lines
6.7 KiB
JavaScript
230 lines
6.7 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const jwt = require('jsonwebtoken');
|
|
const { supabase } = require('../config/supabase');
|
|
const { getNotificationHistory } = require('../services/notifications');
|
|
|
|
/**
|
|
* Middleware to verify JWT token
|
|
*/
|
|
function authMiddleware(req, res, next) {
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
return res.status(401).json({ error: 'No token provided' });
|
|
}
|
|
|
|
try {
|
|
const token = authHeader.split(' ')[1];
|
|
req.user = jwt.verify(token, process.env.JWT_SECRET);
|
|
next();
|
|
} catch (err) {
|
|
return res.status(401).json({ error: 'Invalid token' });
|
|
}
|
|
}
|
|
|
|
// Apply auth middleware to all routes
|
|
router.use(authMiddleware);
|
|
|
|
/**
|
|
* GET /api/notification-settings
|
|
* Returns notification settings for current user
|
|
*/
|
|
router.get('/', async (req, res) => {
|
|
try {
|
|
const userId = req.user.userId;
|
|
|
|
// Get settings or return defaults
|
|
const { data: settings, error } = await supabase
|
|
.from('notification_settings')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.single();
|
|
|
|
if (error && error.code !== 'PGRST116') { // PGRST116 = no rows returned
|
|
console.error('Get notification settings error:', error);
|
|
return res.status(500).json({ error: 'Failed to get notification settings' });
|
|
}
|
|
|
|
// Return settings or defaults (using new field names for mobile app)
|
|
res.json({
|
|
settings: settings ? {
|
|
// Alert types
|
|
emergencyAlerts: settings.emergency_alerts ?? true,
|
|
activityAlerts: settings.activity_alerts ?? true,
|
|
lowBattery: settings.low_battery ?? true,
|
|
dailySummary: settings.daily_summary ?? false,
|
|
weeklySummary: settings.weekly_summary ?? true,
|
|
// Delivery methods
|
|
pushEnabled: settings.push_enabled ?? true,
|
|
emailEnabled: settings.email_enabled ?? false,
|
|
smsEnabled: settings.sms_enabled ?? false,
|
|
// Quiet hours
|
|
quietHours: settings.quiet_hours_enabled ?? false,
|
|
quietStart: settings.quiet_hours_start ?? '22:00',
|
|
quietEnd: settings.quiet_hours_end ?? '07:00',
|
|
} : {
|
|
// Defaults
|
|
emergencyAlerts: true,
|
|
activityAlerts: true,
|
|
lowBattery: true,
|
|
dailySummary: false,
|
|
weeklySummary: true,
|
|
pushEnabled: true,
|
|
emailEnabled: false,
|
|
smsEnabled: false,
|
|
quietHours: false,
|
|
quietStart: '22:00',
|
|
quietEnd: '07:00',
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Get notification settings error:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* PATCH /api/notification-settings
|
|
* Updates notification settings
|
|
*/
|
|
router.patch('/', async (req, res) => {
|
|
try {
|
|
const userId = req.user.userId;
|
|
const {
|
|
// New field names from mobile app
|
|
emergencyAlerts,
|
|
activityAlerts,
|
|
lowBattery,
|
|
dailySummary,
|
|
weeklySummary,
|
|
pushEnabled,
|
|
emailEnabled,
|
|
smsEnabled,
|
|
quietHours,
|
|
quietStart,
|
|
quietEnd,
|
|
} = req.body;
|
|
|
|
// Build update data (map to DB column names)
|
|
const updateData = {
|
|
user_id: userId,
|
|
updated_at: new Date().toISOString()
|
|
};
|
|
|
|
// Alert types
|
|
if (emergencyAlerts !== undefined) updateData.emergency_alerts = emergencyAlerts;
|
|
if (activityAlerts !== undefined) updateData.activity_alerts = activityAlerts;
|
|
if (lowBattery !== undefined) updateData.low_battery = lowBattery;
|
|
if (dailySummary !== undefined) updateData.daily_summary = dailySummary;
|
|
if (weeklySummary !== undefined) updateData.weekly_summary = weeklySummary;
|
|
// Delivery methods
|
|
if (pushEnabled !== undefined) updateData.push_enabled = pushEnabled;
|
|
if (emailEnabled !== undefined) updateData.email_enabled = emailEnabled;
|
|
if (smsEnabled !== undefined) updateData.sms_enabled = smsEnabled;
|
|
// Quiet hours
|
|
if (quietHours !== undefined) updateData.quiet_hours_enabled = quietHours;
|
|
if (quietStart !== undefined) updateData.quiet_hours_start = quietStart;
|
|
if (quietEnd !== undefined) updateData.quiet_hours_end = quietEnd;
|
|
|
|
// Upsert (insert or update)
|
|
const { data: settings, error } = await supabase
|
|
.from('notification_settings')
|
|
.upsert(updateData, { onConflict: 'user_id' })
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
console.error('Update notification settings error:', error);
|
|
return res.status(500).json({ error: 'Failed to update notification settings' });
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
settings: {
|
|
emergencyAlerts: settings.emergency_alerts,
|
|
activityAlerts: settings.activity_alerts,
|
|
lowBattery: settings.low_battery,
|
|
dailySummary: settings.daily_summary,
|
|
weeklySummary: settings.weekly_summary,
|
|
pushEnabled: settings.push_enabled,
|
|
emailEnabled: settings.email_enabled,
|
|
smsEnabled: settings.sms_enabled,
|
|
quietHours: settings.quiet_hours_enabled,
|
|
quietStart: settings.quiet_hours_start,
|
|
quietEnd: settings.quiet_hours_end,
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Update notification settings error:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/notification-settings/history
|
|
* Returns notification history for current user
|
|
*
|
|
* Query params:
|
|
* - limit: number (default 50, max 100)
|
|
* - offset: number (default 0)
|
|
* - type: string (filter by notification type)
|
|
* - status: string (filter by status)
|
|
*/
|
|
router.get('/history', async (req, res) => {
|
|
try {
|
|
const userId = req.user.userId;
|
|
const {
|
|
limit = 50,
|
|
offset = 0,
|
|
type,
|
|
status
|
|
} = req.query;
|
|
|
|
// Validate and cap limit
|
|
const parsedLimit = Math.min(parseInt(limit) || 50, 100);
|
|
const parsedOffset = parseInt(offset) || 0;
|
|
|
|
const result = await getNotificationHistory(userId, {
|
|
limit: parsedLimit,
|
|
offset: parsedOffset,
|
|
type,
|
|
status
|
|
});
|
|
|
|
if (result.error) {
|
|
return res.status(500).json({ error: result.error });
|
|
}
|
|
|
|
// Transform data for mobile app (camelCase)
|
|
const history = result.data.map(item => ({
|
|
id: item.id,
|
|
title: item.title,
|
|
body: item.body,
|
|
type: item.type,
|
|
channel: item.channel,
|
|
status: item.status,
|
|
skipReason: item.skip_reason,
|
|
data: item.data,
|
|
beneficiaryId: item.beneficiary_id,
|
|
createdAt: item.created_at,
|
|
sentAt: item.sent_at,
|
|
deliveredAt: item.delivered_at
|
|
}));
|
|
|
|
res.json({
|
|
history,
|
|
total: result.total,
|
|
limit: parsedLimit,
|
|
offset: parsedOffset
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Get notification history error:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|