Files
plunk/apps/api/src/controllers/Events.ts
T
2025-12-04 12:46:39 +01:00

155 lines
4.8 KiB
TypeScript

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',
});
}
}
}