analytics.js•11 kB
/**
* Analytics routes
* Handles operations related to Facebook ad analytics
*/
const express = require('express');
const router = express.Router();
const { protect, checkFacebookToken } = require('../middleware/auth');
const { validate, schemas } = require('../middleware/validator');
const { sendSuccess, sendError } = require('../utils/responseFormatter');
const { NotFoundError, ValidationError } = require('../utils/errorTypes');
const analyticsService = require('../services/analyticsService');
const logger = require('../utils/logger');
/**
* @route GET /api/analytics/overview
* @desc Get account overview analytics
* @access Private
*/
router.get('/overview', protect, validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get account overview
const overview = await analytics.getAccountOverview(startDate, endDate);
return sendSuccess(res, overview, 'Account overview retrieved successfully');
} catch (error) {
logger.error(`Error retrieving account overview: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/campaigns
* @desc Get analytics for all campaigns
* @access Private
*/
router.get('/campaigns', protect, validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for all campaigns
const analyticsData = await analytics.getCampaignAnalytics(null, startDate, endDate);
return sendSuccess(res, analyticsData, 'Campaign analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving campaign analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/campaigns/:id
* @desc Get analytics for a specific campaign
* @access Private
*/
router.get('/campaigns/:id', protect, validate(schemas.idParam, 'params'), validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for the campaign
const analyticsData = await analytics.getCampaignAnalytics(req.params.id, startDate, endDate);
return sendSuccess(res, analyticsData, 'Campaign analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving campaign analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/ad-sets
* @desc Get analytics for all ad sets
* @access Private
*/
router.get('/ad-sets', protect, validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for all ad sets
const analyticsData = await analytics.getAdSetAnalytics(null, startDate, endDate);
return sendSuccess(res, analyticsData, 'Ad set analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving ad set analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/ad-sets/:id
* @desc Get analytics for a specific ad set
* @access Private
*/
router.get('/ad-sets/:id', protect, validate(schemas.idParam, 'params'), validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for the ad set
const analyticsData = await analytics.getAdSetAnalytics(req.params.id, startDate, endDate);
return sendSuccess(res, analyticsData, 'Ad set analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving ad set analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/ads
* @desc Get analytics for all ads
* @access Private
*/
router.get('/ads', protect, validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for all ads
const analyticsData = await analytics.getAdAnalytics(null, startDate, endDate);
return sendSuccess(res, analyticsData, 'Ad analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving ad analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/ads/:id
* @desc Get analytics for a specific ad
* @access Private
*/
router.get('/ads/:id', protect, validate(schemas.idParam, 'params'), validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get start and end dates from query params
const startDate = new Date(req.query.startDate);
const endDate = new Date(req.query.endDate);
// Get analytics data for the ad
const analyticsData = await analytics.getAdAnalytics(req.params.id, startDate, endDate);
return sendSuccess(res, analyticsData, 'Ad analytics retrieved successfully');
} catch (error) {
logger.error(`Error retrieving ad analytics: ${error.message}`);
return next(error);
}
});
/**
* @route POST /api/analytics/fetch
* @desc Fetch and store analytics for all entities
* @access Private
*/
router.post('/fetch', protect, checkFacebookToken, validate(schemas.analytics.query, 'query'), async (req, res, next) => {
try {
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get time range from query params
const timeRange = req.query.timeRange || 'last_30_days';
// Get entity type from query params
const entityType = req.query.entityType;
const entityId = req.query.entityId;
// Fetch analytics based on entity type
let result;
if (entityType === 'campaign' && entityId) {
result = await analytics.fetchCampaignAnalytics(entityId, timeRange);
} else if (entityType === 'adset' && entityId) {
result = await analytics.fetchAdSetAnalytics(entityId, timeRange);
} else if (entityType === 'ad' && entityId) {
result = await analytics.fetchAdAnalytics(entityId, timeRange);
} else {
return next(new ValidationError('Invalid entity type or entity ID'));
}
return sendSuccess(res, result, `${entityType} analytics fetched successfully`);
} catch (error) {
logger.error(`Error fetching analytics: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/comparison
* @desc Get performance comparison between two time periods
* @access Private
*/
router.get('/comparison', protect, async (req, res, next) => {
try {
// Validate required parameters
const { entityId, entityType, currentStartDate, currentEndDate, previousStartDate, previousEndDate } = req.query;
if (!entityId || !entityType || !currentStartDate || !currentEndDate || !previousStartDate || !previousEndDate) {
return next(new ValidationError('Missing required parameters'));
}
// Validate entity type
if (!['campaign', 'adset', 'ad'].includes(entityType)) {
return next(new ValidationError('Invalid entity type'));
}
// Create analytics service
const analytics = analyticsService.createForUser(req.user);
// Get performance comparison
const comparison = await analytics.getPerformanceComparison(
entityId,
entityType,
new Date(currentStartDate),
new Date(currentEndDate),
new Date(previousStartDate),
new Date(previousEndDate)
);
return sendSuccess(res, comparison, 'Performance comparison retrieved successfully');
} catch (error) {
logger.error(`Error retrieving performance comparison: ${error.message}`);
return next(error);
}
});
/**
* @route GET /api/analytics/metrics
* @desc Get available metrics for analytics
* @access Private
*/
router.get('/metrics', protect, (req, res) => {
const metrics = {
performance: [
{ id: 'impressions', name: 'Impressions', description: 'Number of times your ads were displayed' },
{ id: 'reach', name: 'Reach', description: 'Number of unique people who saw your ads' },
{ id: 'frequency', name: 'Frequency', description: 'Average number of times each person saw your ad' },
{ id: 'clicks', name: 'Clicks', description: 'Number of clicks on your ads' },
{ id: 'ctr', name: 'CTR', description: 'Click-through rate (clicks ÷ impressions)' },
{ id: 'cpc', name: 'CPC', description: 'Cost per click (spend ÷ clicks)' },
{ id: 'cpm', name: 'CPM', description: 'Cost per 1,000 impressions (spend ÷ impressions × 1,000)' }
],
conversion: [
{ id: 'conversions', name: 'Conversions', description: 'Number of conversions' },
{ id: 'costPerConversion', name: 'Cost per Conversion', description: 'Average cost per conversion (spend ÷ conversions)' },
{ id: 'conversionRate', name: 'Conversion Rate', description: 'Percentage of clicks that resulted in conversions (conversions ÷ clicks)' },
{ id: 'roas', name: 'ROAS', description: 'Return on ad spend (conversion value ÷ spend)' }
],
engagement: [
{ id: 'engagement', name: 'Engagement', description: 'Total number of actions people took on your ads' },
{ id: 'videoViews', name: 'Video Views', description: 'Number of times your video ads were viewed' },
{ id: 'videoWatchTime', name: 'Video Watch Time', description: 'Total time spent watching your video ads' }
]
};
return sendSuccess(res, metrics, 'Analytics metrics retrieved successfully');
});
module.exports = router;