All files / src/app/api/notifications route.ts

0% Statements 0/126
100% Branches 0/0
0% Functions 0/1
0% Lines 0/126

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127                                                                                                                                                                                                                                                             
/**
 * Notifications API Routes
 *
 * GET /api/notifications - Get user's notifications
 * DELETE /api/notifications - Delete all read notifications
 */

import { NextRequest, NextResponse } from 'next/server';
import {
  withAuth,
  withErrorHandling,
  successResponse,
  ApiSuccessResponse,
} from '@/lib/api';
import { RouteContext } from '@/lib/api/middleware';
import { prisma } from '@/lib/prisma';
import { Session } from 'next-auth';
import type { NotificationType } from '@/lib/notifications/types';

interface NotificationWithMeta {
  id: string;
  userId: number;
  type: NotificationType;
  title: string;
  message: string;
  link: string | null;
  read: boolean;
  readAt: Date | null;
  createdAt: Date;
  metadata?: Record<string, unknown>;
}

interface NotificationsResponseData {
  notifications: NotificationWithMeta[];
  unreadCount: number;
}

/**
 * GET /api/notifications
 *
 * Get paginated notifications for the authenticated user.
 * Includes unread count for badge display.
 */
async function handleGet(
  request: NextRequest,
  _context: RouteContext | undefined,
  session: Session
): Promise<NextResponse<ApiSuccessResponse<NotificationsResponseData>>> {
  const userId = session.user.id;
  const { searchParams } = new URL(request.url);

  const page = Math.max(1, parseInt(searchParams.get('page') || '1'));
  const limit = Math.min(50, Math.max(1, parseInt(searchParams.get('limit') || '20')));
  const unreadOnly = searchParams.get('unread') === 'true';

  const where = {
    userId,
    ...(unreadOnly && { read: false }),
  };

  const [notifications, total, unreadCount] = await Promise.all([
    prisma.notification.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip: (page - 1) * limit,
      take: limit,
    }),
    prisma.notification.count({ where }),
    prisma.notification.count({
      where: { userId, read: false },
    }),
  ]);

  const transformedNotifications: NotificationWithMeta[] = notifications.map((n) => ({
    id: n.id,
    userId: n.userId,
    type: n.type as NotificationType,
    title: n.title,
    message: n.message,
    link: n.link,
    read: n.read,
    readAt: null,
    createdAt: n.createdAt,
    metadata: undefined,
  }));

  // Return using paginatedResponse with custom shape
  return successResponse({
    notifications: transformedNotifications,
    unreadCount,
  }, {
    meta: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit),
      hasNext: page < Math.ceil(total / limit),
      hasPrev: page > 1,
    },
  });
}

/**
 * DELETE /api/notifications
 *
 * Delete all read notifications for the authenticated user.
 */
async function handleDelete(
  _request: NextRequest,
  _context: RouteContext | undefined,
  session: Session
): Promise<NextResponse<ApiSuccessResponse<{ deletedCount: number }>>> {
  const userId = session.user.id;

  const result = await prisma.notification.deleteMany({
    where: {
      userId,
      read: true,
    },
  });

  return successResponse({ deletedCount: result.count });
}

export const GET = withErrorHandling(withAuth(handleGet));
export const DELETE = withErrorHandling(withAuth(handleDelete));