summaryrefslogtreecommitdiff
path: root/app/api/notifications
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-18 07:52:02 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-18 07:52:02 +0000
commit48a2255bfc45ffcfb0b39ffefdd57cbacf8b36df (patch)
tree0c88b7c126138233875e8d372a4e999e49c38a62 /app/api/notifications
parent2ef02e27dbe639876fa3b90c30307dda183545ec (diff)
(대표님) 파일관리변경, 클라IP추적, 실시간알림, 미들웨어변경, 알림API
Diffstat (limited to 'app/api/notifications')
-rw-r--r--app/api/notifications/[id]/deleate/route.ts34
-rw-r--r--app/api/notifications/[id]/read/route.ts34
-rw-r--r--app/api/notifications/read-all/route.ts26
-rw-r--r--app/api/notifications/route.ts37
-rw-r--r--app/api/notifications/stats/route.ts28
-rw-r--r--app/api/notifications/stream/route.ts54
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