import {Controller, Delete, Get, Middleware, Post} from '@overnightjs/core'; import type {NextFunction, Request, Response} from 'express'; import type {AuthResponse} from '../middleware/auth.js'; import {requireAuth} from '../middleware/auth.js'; import {EventService} from '../services/EventService.js'; import {CatchAsync} from '../utils/asyncHandler.js'; @Controller('events') export class Events { /** * POST /events/track * Track a custom event (can trigger workflows) */ @Post('track') @Middleware([requireAuth]) @CatchAsync public async track(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const {name, contactId, emailId, data} = req.body; if (!name) { return res.status(400).json({error: 'Event name is required'}); } const event = await EventService.trackEvent(auth.projectId!, name, contactId, emailId, data); return res.status(201).json(event); } /** * GET /events * List events for the project */ @Get('') @Middleware([requireAuth]) @CatchAsync public async list(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const eventName = req.query.eventName as string | undefined; const limit = parseInt(req.query.limit as string) || 100; const events = await EventService.getProjectEvents(auth.projectId!, eventName, limit); return res.status(200).json({events}); } /** * GET /events/stats * Get event statistics */ @Get('stats') @Middleware([requireAuth]) @CatchAsync public async stats(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const startDate = req.query.startDate ? new Date(req.query.startDate as string) : undefined; const endDate = req.query.endDate ? new Date(req.query.endDate as string) : undefined; const stats = await EventService.getEventStats(auth.projectId!, startDate, endDate); return res.status(200).json(stats); } /** * GET /events/contact/:contactId * Get events for a specific contact */ @Get('contact/:contactId') @Middleware([requireAuth]) @CatchAsync public async getContactEvents(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const contactId = req.params.contactId; const limit = parseInt(req.query.limit as string) || 50; if (!contactId) { return res.status(400).json({error: 'Contact ID is required'}); } const events = await EventService.getContactEvents(auth.projectId!, contactId, limit); return res.status(200).json({events}); } /** * GET /events/names * Get unique event names for the project */ @Get('names') @Middleware([requireAuth]) @CatchAsync public async getEventNames(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const eventNames = await EventService.getUniqueEventNames(auth.projectId!); return res.status(200).json({eventNames}); } /** * GET /events/:eventName/usage * Check if an event is used in segments/workflows and get usage statistics * Returns information about where the event is used and whether it can be safely deleted */ @Get(':eventName/usage') @Middleware([requireAuth]) @CatchAsync public async getEventUsage(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const eventName = req.params.eventName; if (!eventName) { return res.status(400).json({error: 'Event name is required'}); } try { const usage = await EventService.getEventUsage(auth.projectId!, eventName); return res.status(200).json(usage); } catch (error) { console.error('[EVENTS] Failed to get event usage:', error); return res.status(500).json({ error: error instanceof Error ? error.message : 'Failed to get event usage', }); } } /** * DELETE /events/:eventName * Delete all events with a specific name * Only works if the event is not used in any segments or workflows */ @Delete(':eventName') @Middleware([requireAuth]) @CatchAsync public async deleteEvent(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth as AuthResponse; const eventName = req.params.eventName; if (!eventName) { return res.status(400).json({error: 'Event name is required'}); } try { const result = await EventService.deleteEvent(auth.projectId!, eventName); return res.status(200).json(result); } catch (error) { console.error('[EVENTS] Failed to delete event:', error); return res.status(error instanceof Error && error.message.includes('Cannot delete') ? 400 : 500).json({ error: error instanceof Error ? error.message : 'Failed to delete event', }); } } }