Files
marketplace/server/src/socket/index.ts
delta-lynx-89e8 d09c998d51 QA fixes: real listing creation, profile save, favorites, missing pages
- SellItemPage: real file upload + API listing creation + activate
- CreateProfilePage: save profile via PUT /users/profile
- ProductDetailPage: wire edit/delete/message buttons, show edit for owner
- ListingCard: persist favorites via API, show real images
- Footer: connect newsletter subscribe to API
- Router: add /dashboard/listings and /dashboard/saved routes
- Backend: add GET /listings/favorites endpoint
- New pages: MyListingsPage, SavedItemsPage
- Fix unused imports causing build failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:30:03 -08:00

126 lines
4.1 KiB
TypeScript

import type { Server as HTTPServer } from 'http';
import { Server } from 'socket.io';
import { verifyAccessToken } from '../utils/jwt.js';
import { prisma } from '../config/database.js';
// Online status tracking
const onlineUsers = new Map<string, Set<string>>();
export function setupSocket(httpServer: HTTPServer) {
const io = new Server(httpServer, {
cors: {
origin: process.env['CLIENT_URL'] || 'http://localhost:5173',
credentials: true,
},
});
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) return next(new Error('Authentication required'));
try {
const payload = verifyAccessToken(token);
socket.data.userId = payload.userId;
next();
} catch {
next(new Error('Invalid token'));
}
});
io.on('connection', (socket) => {
const userId = socket.data.userId;
socket.join(`user:${userId}`);
// Track online status
if (!onlineUsers.has(userId)) {
onlineUsers.set(userId, new Set());
}
onlineUsers.get(userId)!.add(socket.id);
// Broadcast online status
socket.broadcast.emit('user_online', { userId });
socket.on('join_conversation', (conversationId: string) => {
socket.join(`conversation:${conversationId}`);
});
socket.on('leave_conversation', (conversationId: string) => {
socket.leave(`conversation:${conversationId}`);
});
socket.on('send_message', async (data: { conversationId: string; content: string; offerAmount?: number }) => {
try {
const message = await prisma.message.create({
data: {
content: data.content,
senderId: userId,
conversationId: data.conversationId,
offerAmount: data.offerAmount,
},
include: { sender: { select: { id: true, fullName: true, avatar: true } } },
});
await prisma.conversation.update({
where: { id: data.conversationId },
data: { updatedAt: new Date() },
});
io.to(`conversation:${data.conversationId}`).emit('new_message', message);
const conversation = await prisma.conversation.findUnique({ where: { id: data.conversationId } });
if (conversation) {
const recipientId = conversation.user1Id === userId ? conversation.user2Id : conversation.user1Id;
io.to(`user:${recipientId}`).emit('message_notification', { conversationId: data.conversationId, message });
// Create notification
const sender = await prisma.user.findUnique({ where: { id: userId }, select: { fullName: true } });
const notification = await prisma.notification.create({
data: {
userId: recipientId,
type: 'NEW_MESSAGE',
title: 'New Message',
body: `${sender?.fullName || 'Someone'} sent you a message`,
data: { conversationId: data.conversationId },
},
});
io.to(`user:${recipientId}`).emit('new_notification', notification);
}
} catch (error) {
socket.emit('error', { message: 'Failed to send message' });
}
});
socket.on('typing', (conversationId: string) => {
socket.to(`conversation:${conversationId}`).emit('user_typing', { userId, conversationId });
});
socket.on('stop_typing', (conversationId: string) => {
socket.to(`conversation:${conversationId}`).emit('user_stop_typing', { userId, conversationId });
});
socket.on('mark_read', async (conversationId: string) => {
await prisma.message.updateMany({
where: { conversationId, senderId: { not: userId }, isRead: false },
data: { isRead: true },
});
});
socket.on('get_online_users', () => {
const users = Array.from(onlineUsers.keys());
socket.emit('online_users', users);
});
socket.on('disconnect', () => {
const userSockets = onlineUsers.get(userId);
if (userSockets) {
userSockets.delete(socket.id);
if (userSockets.size === 0) {
onlineUsers.delete(userId);
socket.broadcast.emit('user_offline', { userId });
}
}
});
});
return io;
}