Files
plunk/apps/api/src/controllers/Activity.ts
T

128 lines
4.2 KiB
TypeScript

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