import {Controller, Get, Middleware} from '@overnightjs/core'; import type {NextFunction, Request, Response} from 'express'; import {ActivityType} from '@plunk/types'; import {requireAuth, requireEmailVerified} from '../middleware/auth.js'; import {ActivityService} from '../services/ActivityService.js'; import {CatchAsync} from '../utils/asyncHandler.js'; @Controller('activity') export class Activity { /** * GET /activity * Get unified activity feed for the project * * Query params: * - limit: number (default 50, max 100) * - cursor: string (pagination cursor: timestamp_id) * - types: ActivityType[] (filter by activity types) * - contactId: string (filter by contact) * - startDate: ISO date string * - endDate: ISO date string */ @Get('') @Middleware([requireAuth, requireEmailVerified]) @CatchAsync public async getActivities(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth; const limit = Math.min(parseInt(req.query.limit as string) || 50, 100); const cursor = req.query.cursor as string | undefined; const contactId = req.query.contactId as string | undefined; 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; // Parse types filter (comma-separated) let types: ActivityType[] | undefined; if (req.query.types) { const typesParam = req.query.types as string; types = typesParam .split(',') .filter(t => Object.values(ActivityType).includes(t as ActivityType)) as ActivityType[]; } const result = await ActivityService.getActivities( auth.projectId, limit, cursor, types, contactId, startDate, endDate, ); return res.status(200).json(result); } /** * GET /activity/stats * Get activity statistics for the project * * Query params: * - startDate: ISO date string (defaults to 30 days ago) * - endDate: ISO date string (defaults to now) */ @Get('stats') @Middleware([requireAuth, requireEmailVerified]) @CatchAsync public async getStats(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth; 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 ActivityService.getStats(auth.projectId, startDate, endDate); return res.status(200).json(stats); } /** * GET /activity/recent-count * Get count of recent activities (for real-time updates) * * Query params: * - minutes: number (default 5) */ @Get('recent-count') @Middleware([requireAuth, requireEmailVerified]) @CatchAsync public async getRecentCount(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth; const minutes = Math.min(parseInt(req.query.minutes as string) || 5, 60); // Max 60 minutes const count = await ActivityService.getRecentActivityCount(auth.projectId, minutes); return res.status(200).json({count, minutes}); } /** * GET /activity/types * Get available activity types (for UI filters) */ @Get('types') @Middleware([requireAuth, requireEmailVerified]) @CatchAsync public async getTypes(_req: Request, res: Response, _next: NextFunction) { const types = Object.values(ActivityType); return res.status(200).json({types}); } /** * GET /activity/upcoming * Get upcoming scheduled activities (campaigns and workflow emails) * * Query params: * - limit: number (default 50, max 100) * - daysAhead: number (default 30, max 90) */ @Get('upcoming') @Middleware([requireAuth, requireEmailVerified]) @CatchAsync public async getUpcoming(req: Request, res: Response, _next: NextFunction) { const auth = res.locals.auth; const limit = Math.min(parseInt(req.query.limit as string) || 50, 100); const daysAhead = Math.min(parseInt(req.query.daysAhead as string) || 30, 90); const activities = await ActivityService.getUpcomingActivities(auth.projectId, limit, daysAhead); return res.status(200).json({activities}); } }