trading-platform-backend-v2/src/modules/notifications/controllers/notification.controller.ts
Adrian Flores Cortes 35a94f0529 feat: Complete notifications system with push support and tests
- Add Firebase client for FCM push notifications
- Update notification service with push token management
- Add push token registration/removal endpoints
- Update all queries to use auth schema
- Add comprehensive unit tests for notification.service
- Add unit tests for distribution.job

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 03:56:34 -06:00

293 lines
6.8 KiB
TypeScript

/**
* Notification Controller
* Handles notification-related HTTP requests
*/
import { Response } from 'express';
import { AuthenticatedRequest } from '../../../core/guards/auth.guard';
import { notificationService } from '../services/notification.service';
import { logger } from '../../../shared/utils/logger';
/**
* GET /api/v1/notifications
* Get user's notifications
*/
export async function getNotifications(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const { limit, offset, unreadOnly } = req.query;
const notifications = await notificationService.getUserNotifications(userId, {
limit: limit ? parseInt(limit as string, 10) : 50,
offset: offset ? parseInt(offset as string, 10) : 0,
unreadOnly: unreadOnly === 'true',
});
res.json({
success: true,
data: notifications,
});
} catch (error) {
logger.error('[NotificationController] Failed to get notifications:', error);
res.status(500).json({
success: false,
error: 'Failed to get notifications',
});
}
}
/**
* GET /api/v1/notifications/unread-count
* Get unread notification count
*/
export async function getUnreadCount(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const count = await notificationService.getUnreadCount(userId);
res.json({
success: true,
data: { count },
});
} catch (error) {
logger.error('[NotificationController] Failed to get unread count:', error);
res.status(500).json({
success: false,
error: 'Failed to get unread count',
});
}
}
/**
* PATCH /api/v1/notifications/:notificationId/read
* Mark notification as read
*/
export async function markAsRead(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const { notificationId } = req.params;
const success = await notificationService.markAsRead(notificationId, userId);
if (!success) {
res.status(404).json({
success: false,
error: 'Notification not found',
});
return;
}
res.json({
success: true,
message: 'Notification marked as read',
});
} catch (error) {
logger.error('[NotificationController] Failed to mark as read:', error);
res.status(500).json({
success: false,
error: 'Failed to mark notification as read',
});
}
}
/**
* POST /api/v1/notifications/read-all
* Mark all notifications as read
*/
export async function markAllAsRead(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const count = await notificationService.markAllAsRead(userId);
res.json({
success: true,
data: { markedCount: count },
});
} catch (error) {
logger.error('[NotificationController] Failed to mark all as read:', error);
res.status(500).json({
success: false,
error: 'Failed to mark notifications as read',
});
}
}
/**
* DELETE /api/v1/notifications/:notificationId
* Delete a notification
*/
export async function deleteNotification(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const { notificationId } = req.params;
const success = await notificationService.deleteNotification(notificationId, userId);
if (!success) {
res.status(404).json({
success: false,
error: 'Notification not found',
});
return;
}
res.json({
success: true,
message: 'Notification deleted',
});
} catch (error) {
logger.error('[NotificationController] Failed to delete notification:', error);
res.status(500).json({
success: false,
error: 'Failed to delete notification',
});
}
}
/**
* GET /api/v1/notifications/preferences
* Get notification preferences
*/
export async function getPreferences(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const preferences = await notificationService.getUserPreferences(userId);
res.json({
success: true,
data: preferences,
});
} catch (error) {
logger.error('[NotificationController] Failed to get preferences:', error);
res.status(500).json({
success: false,
error: 'Failed to get notification preferences',
});
}
}
/**
* PATCH /api/v1/notifications/preferences
* Update notification preferences
*/
export async function updatePreferences(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const updates = req.body;
await notificationService.updateUserPreferences(userId, updates);
const preferences = await notificationService.getUserPreferences(userId);
res.json({
success: true,
data: preferences,
});
} catch (error) {
logger.error('[NotificationController] Failed to update preferences:', error);
res.status(500).json({
success: false,
error: 'Failed to update notification preferences',
});
}
}
/**
* POST /api/v1/notifications/push-token
* Register a push notification token
*/
export async function registerPushToken(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const { token, platform, deviceInfo } = req.body;
if (!token || !platform) {
res.status(400).json({
success: false,
error: 'Token and platform are required',
});
return;
}
if (!['web', 'ios', 'android'].includes(platform)) {
res.status(400).json({
success: false,
error: 'Invalid platform. Must be web, ios, or android',
});
return;
}
await notificationService.registerPushToken(userId, token, platform, deviceInfo);
res.json({
success: true,
message: 'Push token registered successfully',
});
} catch (error) {
logger.error('[NotificationController] Failed to register push token:', error);
res.status(500).json({
success: false,
error: 'Failed to register push token',
});
}
}
/**
* DELETE /api/v1/notifications/push-token
* Remove a push notification token
*/
export async function removePushToken(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.user!.id;
const { token } = req.body;
if (!token) {
res.status(400).json({
success: false,
error: 'Token is required',
});
return;
}
await notificationService.removePushToken(userId, token);
res.json({
success: true,
message: 'Push token removed successfully',
});
} catch (error) {
logger.error('[NotificationController] Failed to remove push token:', error);
res.status(500).json({
success: false,
error: 'Failed to remove push token',
});
}
}