Added Chat Page
This commit is contained in:
149
app/api/conversations/[id]/events/route.ts
Normal file
149
app/api/conversations/[id]/events/route.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
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',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user