const express = require('express'); const { prisma } = require('../utils/db'); const { authenticate } = require('../middleware/auth'); const router = express.Router(); // GET /api/events - List all events router.get('/', authenticate, async (req, res, next) => { try { const userId = req.user.id; // Fetch all events with participation info const events = await prisma.event.findMany({ select: { id: true, slug: true, name: true, location: true, startDate: true, endDate: true, worldsdcId: true, description: true, createdAt: true, participants: { where: { userId: userId, }, select: { joinedAt: true, }, }, _count: { select: { participants: true, }, }, }, }); // Transform data and add isJoined flag const eventsWithJoinedStatus = events.map(event => ({ id: event.id, slug: event.slug, name: event.name, location: event.location, startDate: event.startDate, endDate: event.endDate, worldsdcId: event.worldsdcId, participantsCount: event._count.participants, description: event.description, createdAt: event.createdAt, isJoined: event.participants.length > 0, joinedAt: event.participants[0]?.joinedAt || null, })); // Sort: joined events first, then by start date eventsWithJoinedStatus.sort((a, b) => { // First, sort by joined status (joined events first) if (a.isJoined && !b.isJoined) return -1; if (!a.isJoined && b.isJoined) return 1; // Then sort by start date return new Date(a.startDate) - new Date(b.startDate); }); res.json({ success: true, count: eventsWithJoinedStatus.length, data: eventsWithJoinedStatus, }); } catch (error) { next(error); } }); // GET /api/events/:slug - Get event by slug router.get('/:slug', async (req, res, next) => { try { const { slug } = req.params; const event = await prisma.event.findUnique({ where: { slug: slug, }, include: { chatRooms: true, _count: { select: { matches: true, }, }, }, }); if (!event) { return res.status(404).json({ success: false, error: 'Event not found', }); } res.json({ success: true, data: event, }); } catch (error) { next(error); } }); // GET /api/events/:slug/messages - Get event chat messages with pagination router.get('/:slug/messages', authenticate, async (req, res, next) => { try { const { slug } = req.params; const { before, limit = 20 } = req.query; // Find event by slug const event = await prisma.event.findUnique({ where: { slug }, select: { id: true }, }); if (!event) { return res.status(404).json({ success: false, error: 'Event not found', }); } // Find event chat room const chatRoom = await prisma.chatRoom.findFirst({ where: { eventId: event.id, type: 'event', }, }); if (!chatRoom) { return res.status(404).json({ success: false, error: 'Chat room not found', }); } // Build query with pagination const where = { roomId: chatRoom.id }; if (before) { where.id = { lt: parseInt(before) }; } const messages = await prisma.message.findMany({ where, include: { user: { select: { id: true, username: true, avatar: true, }, }, }, orderBy: { createdAt: 'desc' }, take: parseInt(limit), }); // Return in chronological order (oldest first) res.json({ success: true, data: messages.reverse().map(msg => ({ id: msg.id, roomId: msg.roomId, userId: msg.user.id, username: msg.user.username, avatar: msg.user.avatar, content: msg.content, type: msg.type, createdAt: msg.createdAt, })), hasMore: messages.length === parseInt(limit), }); } catch (error) { next(error); } }); // POST /api/events/checkin/:token - Check-in to event using QR code token router.post('/checkin/:token', authenticate, async (req, res, next) => { try { const { token } = req.params; const userId = req.user.id; // Find check-in token const checkinToken = await prisma.eventCheckinToken.findUnique({ where: { token }, include: { event: true, }, }); if (!checkinToken) { return res.status(404).json({ success: false, error: 'Invalid check-in token', }); } const event = checkinToken.event; // Validate dates (only in production) const isProduction = process.env.NODE_ENV === 'production'; if (isProduction) { const now = new Date(); const validFrom = new Date(event.startDate); validFrom.setDate(validFrom.getDate() - 1); const validUntil = new Date(event.endDate); validUntil.setDate(validUntil.getDate() + 1); if (now < validFrom || now > validUntil) { return res.status(400).json({ success: false, error: 'Check-in is not available for this event at this time', }); } } // Check if user is already participating const existingParticipant = await prisma.eventParticipant.findUnique({ where: { userId_eventId: { userId, eventId: event.id, }, }, }); if (existingParticipant) { // User already checked in - return event info return res.json({ success: true, alreadyCheckedIn: true, data: { event: { id: event.id, slug: event.slug, name: event.name, location: event.location, startDate: event.startDate, endDate: event.endDate, }, joinedAt: existingParticipant.joinedAt, }, }); } // Add user to event participants const participant = await prisma.eventParticipant.create({ data: { userId, eventId: event.id, }, }); // Update participants count await prisma.event.update({ where: { id: event.id }, data: { participantsCount: { increment: 1, }, }, }); res.json({ success: true, alreadyCheckedIn: false, data: { event: { id: event.id, slug: event.slug, name: event.name, location: event.location, startDate: event.startDate, endDate: event.endDate, }, joinedAt: participant.joinedAt, }, }); } catch (error) { next(error); } }); // GET /api/events/:slug/details - Get event details with check-in token and participants router.get('/:slug/details', authenticate, async (req, res, next) => { try { const { slug } = req.params; // Find event by slug with participants const event = await prisma.event.findUnique({ where: { slug }, include: { participants: { include: { user: { select: { id: true, username: true, avatar: true, firstName: true, lastName: true, }, }, }, orderBy: { joinedAt: 'desc', }, }, }, }); if (!event) { return res.status(404).json({ success: false, error: 'Event not found', }); } // Find or create check-in token (on-demand generation) let checkinToken = await prisma.eventCheckinToken.findUnique({ where: { eventId: event.id }, }); if (!checkinToken) { checkinToken = await prisma.eventCheckinToken.create({ data: { eventId: event.id, }, }); } // Calculate valid dates (startDate - 1 day to endDate + 1 day) const validFrom = new Date(event.startDate); validFrom.setDate(validFrom.getDate() - 1); const validUntil = new Date(event.endDate); validUntil.setDate(validUntil.getDate() + 1); // Build check-in URL const baseUrl = process.env.FRONTEND_URL || 'http://localhost:8080'; const checkinUrl = `${baseUrl}/events/checkin/${checkinToken.token}`; res.json({ success: true, data: { event: { id: event.id, slug: event.slug, name: event.name, location: event.location, startDate: event.startDate, endDate: event.endDate, description: event.description, }, checkin: { token: checkinToken.token, url: checkinUrl, validFrom, validUntil, }, participants: event.participants.map(p => ({ userId: p.user.id, username: p.user.username, avatar: p.user.avatar, firstName: p.user.firstName, lastName: p.user.lastName, joinedAt: p.joinedAt, })), stats: { totalParticipants: event.participants.length, }, }, }); } catch (error) { next(error); } }); // DELETE /api/events/:slug/leave - Leave an event (remove from participants) router.delete('/:slug/leave', authenticate, async (req, res, next) => { try { const { slug } = req.params; const userId = req.user.id; // Find event by slug const event = await prisma.event.findUnique({ where: { slug }, }); if (!event) { return res.status(404).json({ success: false, error: 'Event not found', }); } // Check if user is participating const participant = await prisma.eventParticipant.findUnique({ where: { userId_eventId: { userId, eventId: event.id, }, }, }); if (!participant) { return res.status(400).json({ success: false, error: 'You are not a participant of this event', }); } // Remove from participants await prisma.eventParticipant.delete({ where: { userId_eventId: { userId, eventId: event.id, }, }, }); // Update participants count await prisma.event.update({ where: { id: event.id }, data: { participantsCount: { decrement: 1, }, }, }); res.json({ success: true, message: 'Successfully left the event', }); } catch (error) { next(error); } }); module.exports = router;