feat: implement backend API for event management (task 13)
- Create eventController with CRUD operations and registration endpoints - Implement eventService with Prisma database operations - Add event routes with authentication middleware - Support event creation, listing, filtering, registration/unregistration - Include spatial data handling for event locations - Add participant count tracking and registration status checks - Requirements: 4.1, 4.2, 4.3, 4.4
This commit is contained in:
296
server/src/controllers/eventController.ts
Normal file
296
server/src/controllers/eventController.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import { Response } from 'express';
|
||||
import { AuthRequest } from '../middleware/auth';
|
||||
import { eventService } from '../services/eventService';
|
||||
|
||||
export const eventController = {
|
||||
// Get all events with optional filters
|
||||
async getEvents(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const { status, startDate, endDate, latitude, longitude, radius } = req.query;
|
||||
|
||||
const filters: any = {};
|
||||
if (status) filters.status = status as string;
|
||||
if (startDate) filters.startDate = new Date(startDate as string);
|
||||
if (endDate) filters.endDate = new Date(endDate as string);
|
||||
if (latitude && longitude) {
|
||||
filters.latitude = parseFloat(latitude as string);
|
||||
filters.longitude = parseFloat(longitude as string);
|
||||
if (radius) filters.radius = parseFloat(radius as string);
|
||||
}
|
||||
|
||||
const events = await eventService.getEvents(filters);
|
||||
res.json(events);
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching events:', error);
|
||||
res.status(500).json({
|
||||
code: 'FETCH_EVENTS_ERROR',
|
||||
message: 'Failed to fetch events',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Get single event by ID
|
||||
async getEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const event = await eventService.getEventById(id);
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({
|
||||
code: 'EVENT_NOT_FOUND',
|
||||
message: 'Event not found',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
res.json(event);
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching event:', error);
|
||||
res.status(500).json({
|
||||
code: 'FETCH_EVENT_ERROR',
|
||||
message: 'Failed to fetch event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Create new event
|
||||
async createEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'User not authenticated',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const { title, description, latitude, longitude, locationName, scheduledDate, duration } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!title || !description || !latitude || !longitude || !locationName || !scheduledDate || !duration) {
|
||||
return res.status(400).json({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Missing required fields',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const eventData = {
|
||||
organizerId: userId,
|
||||
title,
|
||||
description,
|
||||
latitude: parseFloat(latitude),
|
||||
longitude: parseFloat(longitude),
|
||||
locationName,
|
||||
scheduledDate: new Date(scheduledDate),
|
||||
duration: parseInt(duration),
|
||||
};
|
||||
|
||||
const event = await eventService.createEvent(eventData);
|
||||
res.status(201).json(event);
|
||||
} catch (error: any) {
|
||||
console.error('Error creating event:', error);
|
||||
res.status(500).json({
|
||||
code: 'CREATE_EVENT_ERROR',
|
||||
message: 'Failed to create event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Update event
|
||||
async updateEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'User not authenticated',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const event = await eventService.getEventById(id);
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({
|
||||
code: 'EVENT_NOT_FOUND',
|
||||
message: 'Event not found',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is the organizer
|
||||
if (event.organizerId !== userId) {
|
||||
return res.status(403).json({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'Only the event organizer can update this event',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const updatedEvent = await eventService.updateEvent(id, req.body);
|
||||
res.json(updatedEvent);
|
||||
} catch (error: any) {
|
||||
console.error('Error updating event:', error);
|
||||
res.status(500).json({
|
||||
code: 'UPDATE_EVENT_ERROR',
|
||||
message: 'Failed to update event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Delete event
|
||||
async deleteEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'User not authenticated',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const event = await eventService.getEventById(id);
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({
|
||||
code: 'EVENT_NOT_FOUND',
|
||||
message: 'Event not found',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is the organizer
|
||||
if (event.organizerId !== userId) {
|
||||
return res.status(403).json({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'Only the event organizer can delete this event',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
await eventService.deleteEvent(id);
|
||||
res.status(204).send();
|
||||
} catch (error: any) {
|
||||
console.error('Error deleting event:', error);
|
||||
res.status(500).json({
|
||||
code: 'DELETE_EVENT_ERROR',
|
||||
message: 'Failed to delete event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Register for event
|
||||
async registerForEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'User not authenticated',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const registration = await eventService.registerForEvent(id, userId);
|
||||
res.status(201).json(registration);
|
||||
} catch (error: any) {
|
||||
console.error('Error registering for event:', error);
|
||||
|
||||
if (error.message.includes('already registered')) {
|
||||
return res.status(409).json({
|
||||
code: 'ALREADY_REGISTERED',
|
||||
message: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
code: 'REGISTER_EVENT_ERROR',
|
||||
message: 'Failed to register for event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Unregister from event
|
||||
async unregisterFromEvent(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'User not authenticated',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
await eventService.unregisterFromEvent(id, userId);
|
||||
res.status(204).send();
|
||||
} catch (error: any) {
|
||||
console.error('Error unregistering from event:', error);
|
||||
res.status(500).json({
|
||||
code: 'UNREGISTER_EVENT_ERROR',
|
||||
message: 'Failed to unregister from event',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Get event registrations
|
||||
async getEventRegistrations(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const registrations = await eventService.getEventRegistrations(id);
|
||||
res.json(registrations);
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching event registrations:', error);
|
||||
res.status(500).json({
|
||||
code: 'FETCH_REGISTRATIONS_ERROR',
|
||||
message: 'Failed to fetch event registrations',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Check registration status
|
||||
async getRegistrationStatus(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
if (!userId) {
|
||||
return res.json({ registered: false });
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const isRegistered = await eventService.isUserRegistered(id, userId);
|
||||
res.json({ registered: isRegistered });
|
||||
} catch (error: any) {
|
||||
console.error('Error checking registration status:', error);
|
||||
res.status(500).json({
|
||||
code: 'CHECK_REGISTRATION_ERROR',
|
||||
message: 'Failed to check registration status',
|
||||
details: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user