WellNuo/backend/src/routes/notification-settings.js
Sergei 7d9e7e37bf Remove console.log statements and add structured logging
Created a centralized logger utility (src/utils/logger.js) that provides:
- Structured logging with context labels
- Log levels (ERROR, WARN, INFO, DEBUG)
- Environment-based log level control via LOG_LEVEL env variable
- Consistent timestamp and JSON data formatting

Removed console.log/error/warn statements from:
- All service files (mqtt, notifications, legacyAPI, email, storage, subscription-sync)
- All route handlers (auth, beneficiaries, deployments, webhook, admin, etc)
- Controllers (dashboard, auth, alarm)
- Database connection handler
- Main server file (index.js)

Preserved:
- Critical startup validation error for JWT_SECRET in index.js

Benefits:
- Production-ready logging that can be easily integrated with log aggregation services
- Reduced noise in production logs
- Easier debugging with structured context and data
- Configurable log levels per environment

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 11:58:06 -08:00

225 lines
6.4 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
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) {
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) {
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) {
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) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;