From ffa03496bf5b53001e88d75c4fe98afb0a78a276 Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Sat, 14 Dec 2024 17:49:50 +0100 Subject: [PATCH] Adding events for auto reload on data updating. --- .../database/[databaseId]/page.tsx | 1 + app/api/agent/[agentId]/backup/route.ts | 3 + app/api/agent/[agentId]/restore/route.ts | 5 + app/api/agent/[agentId]/status/route.ts | 4 +- app/api/events/route.ts | 146 ++++++++++++++++++ middleware.ts | 1 + .../Projects/Database/DatabaseTabs.tsx | 25 ++- src/types/events.ts | 5 + 8 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 app/api/events/route.ts create mode 100644 src/types/events.ts diff --git a/app/(customer)/dashboard/projects/[projectId]/database/[databaseId]/page.tsx b/app/(customer)/dashboard/projects/[projectId]/database/[databaseId]/page.tsx index 94e5b9be..98d78bda 100644 --- a/app/(customer)/dashboard/projects/[projectId]/database/[databaseId]/page.tsx +++ b/app/(customer)/dashboard/projects/[projectId]/database/[databaseId]/page.tsx @@ -77,6 +77,7 @@ export default async function RoutePage(props: PageParams<{ databaseId: string } totalBackups={totalBackups} /> { +// controller.enqueue(`event: modification\n`); +// controller.enqueue(`data: ${JSON.stringify(data)}\n\n`); +// controller.close(); +// }); +// }, +// }), { +// status: 200, +// headers: { +// 'Content-Type': 'text/event-stream', +// 'Cache-Control': 'no-cache', +// Connection: 'keep-alive', +// }, +// }); +// } +// +// export async function POST(request: Request) { +// const data = await request.json(); +// eventEmitter.emit('modification', data); +// return new Response('Event sent', { status: 200 }); +// } +// +// const eventEmitter = new EventEmitter(); +// +// export async function GET(request: Request) { +// return new Response(new ReadableStream({ +// async pull(controller) { +// eventEmitter.on('modification', (data) => { +// console.log('Modification event triggered:', data); +// controller.enqueue(`event: modification\n`); +// controller.enqueue(`data: ${JSON.stringify(data)}\n\n`); +// }); +// +// // Close the controller when the client closes the connection +// request.signal.addEventListener('abort', () => { +// controller.close(); +// }); +// }, +// }), { +// status: 200, +// headers: { +// 'Content-Type': 'text/event-stream', +// 'Cache-Control': 'no-cache', +// Connection: 'keep-alive', +// }, +// }); +// } +// +// export async function POST(request: Request) { +// const data = await request.json(); +// console.log(data); +// eventEmitter.emit('modification', data); +// return new Response('Event sent', { status: 200 }); +// } +// const eventEmitter = new EventEmitter(); +// +// export async function GET(request: Request) { +// console.log('GET request received'); +// return new Response(new ReadableStream({ +// async pull(controller) { +// console.log('Controller created:', controller); +// eventEmitter.on('modification', (data) => { +// console.log('Modification event triggered:', data); +// controller.enqueue(`event: modification\n`); +// controller.enqueue(`data: ${JSON.stringify(data)}\n\n`); +// }); +// // Close the controller when the client closes the connection +// request.signal.addEventListener('abort', () => { +// controller.close(); +// }); +// }, +// }), { +// status: 200, +// headers: { +// 'Content-Type': 'text/event-stream', +// 'Cache-Control': 'no-cache', +// Connection: 'keep-alive', +// }, +// }); +// } +// +// export async function POST(request: Request) { +// console.log('POST request received'); +// const data = await request.json(); +// console.log('Data received:', data); +// eventEmitter.emit('modification', data); +// return new Response('Event sent', { status: 200 }); +// } +import { EventEmitter } from 'events'; + +export const eventEmitter = new EventEmitter(); + +export async function GET(request: Request) { + console.log('GET request received'); + + return new Response( + new ReadableStream({ + start(controller) { + console.log('Stream started'); + + const handleModification = (data: any) => { + console.log('Modification event triggered:', data); + controller.enqueue(`event: modification\n`); + controller.enqueue(`data: ${JSON.stringify(data)}\n\n`); + }; + + eventEmitter.on('modification', handleModification); + + // Handle client disconnect + request.signal.addEventListener('abort', () => { + console.log('Client disconnected'); + controller.close(); + eventEmitter.off('modification', handleModification); + }); + }, + }), + { + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + }, + } + ); +} + +export async function POST(request: Request) { + console.log('POST request received'); + const data = await request.json(); + console.log('Data received:', data); + + // Emit the event to all connected clients + eventEmitter.emit('modification', data); + + return new Response('Event sent', { status: 200 }); +} \ No newline at end of file diff --git a/middleware.ts b/middleware.ts index b9179f50..753c47a2 100644 --- a/middleware.ts +++ b/middleware.ts @@ -41,6 +41,7 @@ function checkRouteExists(pathname) { /^\/api\/agent\/[^/]+\/backup\/?$/, /^\/api\/agent\/[^/]+\/restore\/?$/, /^\/api\/files\/[^/]+\/?$/, + /^\/api\/events\/?$/, ]; return routePatterns.some(pattern => pattern.test(pathname)); } diff --git a/src/components/wrappers/Dashboard/Projects/Database/DatabaseTabs.tsx b/src/components/wrappers/Dashboard/Projects/Database/DatabaseTabs.tsx index a3664afc..791525c2 100644 --- a/src/components/wrappers/Dashboard/Projects/Database/DatabaseTabs.tsx +++ b/src/components/wrappers/Dashboard/Projects/Database/DatabaseTabs.tsx @@ -1,16 +1,39 @@ +"use client" import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs"; import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination"; import {backupColumns} from "@/features/backup/columns"; import {restoreColumns} from "@/features/restore/columns"; -import {Backup, Restauration} from "@prisma/client"; +import {Backup, Database, Restauration} from "@prisma/client"; +import {useEffect} from "react"; +import {useRouter} from "next/navigation"; +import {eventUpdate} from "@/types/events"; export type DatabaseTabsProps = { backups: Backup[] restaurations: Restauration[] isAlreadyRestore: boolean + database: Database } export const DatabaseTabs = (props: DatabaseTabsProps) => { + + const router = useRouter(); + useEffect(() => { + const eventSource = new EventSource('/api/events'); + + eventSource.addEventListener('modification', (event) => { + const data:eventUpdate = JSON.parse(event.data) + if(data.update){ + console.log("update", data.update) + router.refresh() + } + }); + + return () => { + eventSource.close(); + }; + }, []); + return ( diff --git a/src/types/events.ts b/src/types/events.ts new file mode 100644 index 00000000..6908d358 --- /dev/null +++ b/src/types/events.ts @@ -0,0 +1,5 @@ + + +export type eventUpdate = { + update : boolean +} \ No newline at end of file