diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-18 07:52:02 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-18 07:52:02 +0000 |
| commit | 48a2255bfc45ffcfb0b39ffefdd57cbacf8b36df (patch) | |
| tree | 0c88b7c126138233875e8d372a4e999e49c38a62 /app/api/notifications | |
| parent | 2ef02e27dbe639876fa3b90c30307dda183545ec (diff) | |
(대표님) 파일관리변경, 클라IP추적, 실시간알림, 미들웨어변경, 알림API
Diffstat (limited to 'app/api/notifications')
| -rw-r--r-- | app/api/notifications/[id]/deleate/route.ts | 34 | ||||
| -rw-r--r-- | app/api/notifications/[id]/read/route.ts | 34 | ||||
| -rw-r--r-- | app/api/notifications/read-all/route.ts | 26 | ||||
| -rw-r--r-- | app/api/notifications/route.ts | 37 | ||||
| -rw-r--r-- | app/api/notifications/stats/route.ts | 28 | ||||
| -rw-r--r-- | app/api/notifications/stream/route.ts | 54 |
6 files changed, 213 insertions, 0 deletions
diff --git a/app/api/notifications/[id]/deleate/route.ts b/app/api/notifications/[id]/deleate/route.ts new file mode 100644 index 00000000..21123c78 --- /dev/null +++ b/app/api/notifications/[id]/deleate/route.ts @@ -0,0 +1,34 @@ +// app/api/notifications/[id]/delete/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import { deleteNotification } from '@/lib/notification/service'; + +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const deletedNotification = await deleteNotification(params.id, session.user.id); + + if (!deletedNotification) { + return NextResponse.json( + { error: 'Notification not found' }, + { status: 404 } + ); + } + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Error deleting notification:', error); + return NextResponse.json( + { error: 'Failed to delete notification' }, + { status: 500 } + ); + } +}
\ No newline at end of file diff --git a/app/api/notifications/[id]/read/route.ts b/app/api/notifications/[id]/read/route.ts new file mode 100644 index 00000000..ba894fad --- /dev/null +++ b/app/api/notifications/[id]/read/route.ts @@ -0,0 +1,34 @@ +// app/api/notifications/[id]/read/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import { markNotificationAsRead } from '@/lib/notification/service'; + +export async function PATCH( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const notification = await markNotificationAsRead(params.id, session.user.id); + + if (!notification) { + return NextResponse.json( + { error: 'Notification not found' }, + { status: 404 } + ); + } + + return NextResponse.json({ success: true, notification }); + } catch (error) { + console.error('Error marking notification as read:', error); + return NextResponse.json( + { error: 'Failed to mark notification as read' }, + { status: 500 } + ); + } +}
\ No newline at end of file diff --git a/app/api/notifications/read-all/route.ts b/app/api/notifications/read-all/route.ts new file mode 100644 index 00000000..f53bbbbf --- /dev/null +++ b/app/api/notifications/read-all/route.ts @@ -0,0 +1,26 @@ +// app/api/notifications/read-all/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" + +export async function PATCH(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const notifications = await markAllNotificationsAsRead(session.user.id); + + return NextResponse.json({ + success: true, + updatedCount: notifications.length + }); + } catch (error) { + console.error('Error marking all notifications as read:', error); + return NextResponse.json( + { error: 'Failed to mark all notifications as read' }, + { status: 500 } + ); + } +} diff --git a/app/api/notifications/route.ts b/app/api/notifications/route.ts new file mode 100644 index 00000000..cab0b74e --- /dev/null +++ b/app/api/notifications/route.ts @@ -0,0 +1,37 @@ +// app/api/notifications/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { getUserNotifications,getUnreadNotificationCount } from '@/lib/notification/service'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" + + +export async function GET(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { searchParams } = new URL(request.url); + const limit = parseInt(searchParams.get('limit') || '20'); + const offset = parseInt(searchParams.get('offset') || '0'); + const unreadOnly = searchParams.get('unreadOnly') === 'true'; + + const [notifications, unreadCount] = await Promise.all([ + getUserNotifications(session.user.id, { limit, offset, unreadOnly }), + getUnreadNotificationCount(session.user.id) + ]); + + return NextResponse.json({ + notifications, + unreadCount, + hasMore: notifications.length === limit + }); + } catch (error) { + console.error('Error fetching notifications:', error); + return NextResponse.json( + { error: 'Failed to fetch notifications' }, + { status: 500 } + ); + } +}
\ No newline at end of file diff --git a/app/api/notifications/stats/route.ts b/app/api/notifications/stats/route.ts new file mode 100644 index 00000000..2e99eb3c --- /dev/null +++ b/app/api/notifications/stats/route.ts @@ -0,0 +1,28 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import realtimeNotificationService from '@/lib/realtime/RealtimeNotificationService'; +import notificationManager from '@/lib/realtime/NotificationManager'; + +export async function GET(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.roles || !session.user.roles.includes('admin')) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); + } + + const stats = { + connectedClients: realtimeNotificationService.getConnectedClientCount(), + dbConnectionStatus: notificationManager.getConnectionStatus(), + timestamp: new Date().toISOString() + }; + + return NextResponse.json(stats); + } catch (error) { + console.error('Error fetching notification stats:', error); + return NextResponse.json( + { error: 'Failed to fetch stats' }, + { status: 500 } + ); + } +}
\ No newline at end of file diff --git a/app/api/notifications/stream/route.ts b/app/api/notifications/stream/route.ts new file mode 100644 index 00000000..7ca6aab2 --- /dev/null +++ b/app/api/notifications/stream/route.ts @@ -0,0 +1,54 @@ +// app/api/notifications/stream/route.ts +import { NextRequest } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import realtimeNotificationService from '@/lib/realtime/RealtimeNotificationService'; + + +export async function GET(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return new Response('Unauthorized', { status: 401 }); + } + + const userId = session.user.id; + const encoder = new TextEncoder(); + + const stream = new ReadableStream({ + start(controller) { + // 클라이언트 등록 + const clientId = realtimeNotificationService.addClient(userId, controller); + + console.log(`SSE stream started for user ${userId} (${clientId})`); + + // 초기 연결 확인 메시지 + controller.enqueue(encoder.encode("data: {\"type\":\"connected\"}\n\n")); + + // 연결 해제 처리 + request.signal.addEventListener('abort', () => { + console.log(`SSE stream ended for user ${userId} (${clientId})`); + realtimeNotificationService.removeClient(userId, controller); + controller.close(); + }); + }, + + cancel() { + console.log(`SSE stream cancelled for user ${userId}`); + realtimeNotificationService.removeClient(userId, controller); + } + }); + + return new Response(stream, { + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache, no-transform', + 'Connection': 'keep-alive', + 'X-Accel-Buffering': 'no', // Nginx 버퍼링 비활성화 + }, + }); + } catch (error) { + console.error('Error creating SSE stream:', error); + return new Response('Internal Server Error', { status: 500 }); + } + }
\ No newline at end of file |
