128 lines
4.2 KiB
TypeScript
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});
|
|
}
|
|
}
|