Files
MAD-Platform/app/api/conversations/[id]/events/route.ts

150 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-01-21 18:13:35 +01:00
import { NextRequest } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getCurrentUser } from '@/lib/auth';
// Server-Sent Events pour les notifications en temps réel
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const user = await getCurrentUser();
if (!user) {
return new Response('Non autorisé', { status: 401 });
}
const conversation = await prisma.conversation.findUnique({
where: { id: params.id },
include: {
participants: true,
},
});
if (!conversation) {
return new Response('Conversation non trouvée', { status: 404 });
}
// Vérifier que l'utilisateur est participant
const isParticipant = conversation.participants.some((p) => p.userId === user.id);
if (!isParticipant) {
return new Response('Accès non autorisé', { status: 403 });
}
// Créer un stream SSE
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
let isClosed = false;
// Fonction pour envoyer un événement
const sendEvent = (data: any) => {
if (isClosed) return;
try {
const message = `data: ${JSON.stringify(data)}\n\n`;
controller.enqueue(encoder.encode(message));
} catch (error) {
// Le controller est fermé, arrêter le polling
isClosed = true;
clearInterval(pollInterval);
}
};
// Envoyer un événement de connexion
sendEvent({ type: 'connected', conversationId: params.id });
// Polling pour vérifier les nouveaux messages
let lastMessageId: string | null = null;
const pollInterval = setInterval(async () => {
if (isClosed) {
clearInterval(pollInterval);
return;
}
try {
const lastMessage = await prisma.message.findFirst({
where: {
conversationId: params.id,
...(lastMessageId ? { id: { gt: lastMessageId } } : {}),
},
orderBy: {
createdAt: 'desc',
},
include: {
sender: {
select: {
id: true,
email: true,
name: true,
},
},
files: true,
},
});
if (lastMessage) {
lastMessageId = lastMessage.id;
sendEvent({
type: 'new_message',
message: lastMessage,
});
}
// Vérifier les mises à jour de conversation
const updatedConversation = await prisma.conversation.findUnique({
where: { id: params.id },
include: {
participants: {
include: {
user: {
select: {
id: true,
email: true,
name: true,
},
},
},
},
},
});
if (updatedConversation) {
sendEvent({
type: 'conversation_updated',
conversation: updatedConversation,
});
}
} catch (error) {
// Si c'est une erreur de controller fermé, arrêter le polling
if (error instanceof Error && error.message.includes('closed')) {
isClosed = true;
clearInterval(pollInterval);
} else {
console.error('Erreur lors du polling:', error);
}
}
}, 1000); // Poll toutes les secondes
// Nettoyer lors de la fermeture
const cleanup = () => {
isClosed = true;
clearInterval(pollInterval);
try {
controller.close();
} catch (e) {
// Ignorer les erreurs de fermeture
}
};
// Gérer la fermeture de la connexion
request.signal?.addEventListener('abort', cleanup);
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}