const express = require('express'); const { BetaAnalyticsDataClient } = require('@google-analytics/data'); const router = express.Router(); const logger = require('../utils/logger'); // Initialize GA4 client const analyticsClient = new BetaAnalyticsDataClient({ credentials: JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS_JSON) }); const propertyId = process.env.GA_PROPERTY_ID; // Cache durations const CACHE_DURATIONS = { REALTIME_BASIC: 60, // 1 minute REALTIME_DETAILED: 300, // 5 minutes BASIC_METRICS: 3600, // 1 hour USER_BEHAVIOR: 3600 // 1 hour }; // Basic metrics endpoint router.get('/metrics', async (req, res) => { try { const { startDate = '7daysAgo' } = req.query; const cacheKey = `analytics:basic_metrics:${startDate}`; // Check Redis cache const cachedData = await req.redisClient.get(cacheKey); if (cachedData) { logger.info('Returning cached basic metrics data'); return res.json({ success: true, data: JSON.parse(cachedData) }); } // Fetch from GA4 const [response] = await analyticsClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate, endDate: 'today' }], dimensions: [{ name: 'date' }], metrics: [ { name: 'activeUsers' }, { name: 'newUsers' }, { name: 'averageSessionDuration' }, { name: 'screenPageViews' }, { name: 'bounceRate' }, { name: 'conversions' } ], returnPropertyQuota: true }); // Cache the response await req.redisClient.set(cacheKey, JSON.stringify(response), { EX: CACHE_DURATIONS.BASIC_METRICS }); res.json({ success: true, data: response }); } catch (error) { logger.error('Error fetching basic metrics:', error); res.status(500).json({ success: false, error: error.message }); } }); // Realtime basic data endpoint router.get('/realtime/basic', async (req, res) => { try { const cacheKey = 'analytics:realtime:basic'; // Check Redis cache const cachedData = await req.redisClient.get(cacheKey); if (cachedData) { logger.info('Returning cached realtime basic data'); return res.json({ success: true, data: JSON.parse(cachedData) }); } // Fetch active users const [userResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, metrics: [{ name: 'activeUsers' }], returnPropertyQuota: true }); // Fetch last 5 minutes const [fiveMinResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, metrics: [{ name: 'activeUsers' }], minuteRanges: [{ startMinutesAgo: 5, endMinutesAgo: 0 }] }); // Fetch time series data const [timeSeriesResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, dimensions: [{ name: 'minutesAgo' }], metrics: [{ name: 'activeUsers' }] }); const response = { userResponse, fiveMinResponse, timeSeriesResponse, quotaInfo: { projectHourly: userResponse.propertyQuota.tokensPerProjectPerHour, daily: userResponse.propertyQuota.tokensPerDay, serverErrors: userResponse.propertyQuota.serverErrorsPerProjectPerHour, thresholdedRequests: userResponse.propertyQuota.potentiallyThresholdedRequestsPerHour } }; // Cache the response await req.redisClient.set(cacheKey, JSON.stringify(response), { EX: CACHE_DURATIONS.REALTIME_BASIC }); res.json({ success: true, data: response }); } catch (error) { logger.error('Error fetching realtime basic data:', error); res.status(500).json({ success: false, error: error.message }); } }); // Realtime detailed data endpoint router.get('/realtime/detailed', async (req, res) => { try { const cacheKey = 'analytics:realtime:detailed'; // Check Redis cache const cachedData = await req.redisClient.get(cacheKey); if (cachedData) { logger.info('Returning cached realtime detailed data'); return res.json({ success: true, data: JSON.parse(cachedData) }); } // Fetch current pages const [pageResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, dimensions: [{ name: 'unifiedScreenName' }], metrics: [{ name: 'screenPageViews' }], orderBy: [{ metric: { metricName: 'screenPageViews' }, desc: true }], limit: 25 }); // Fetch events const [eventResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, dimensions: [{ name: 'eventName' }], metrics: [{ name: 'eventCount' }], orderBy: [{ metric: { metricName: 'eventCount' }, desc: true }], limit: 25 }); // Fetch device categories const [deviceResponse] = await analyticsClient.runRealtimeReport({ property: `properties/${propertyId}`, dimensions: [{ name: 'deviceCategory' }], metrics: [{ name: 'activeUsers' }], orderBy: [{ metric: { metricName: 'activeUsers' }, desc: true }], limit: 10, returnPropertyQuota: true }); const response = { pageResponse, eventResponse, sourceResponse: deviceResponse }; // Cache the response await req.redisClient.set(cacheKey, JSON.stringify(response), { EX: CACHE_DURATIONS.REALTIME_DETAILED }); res.json({ success: true, data: response }); } catch (error) { logger.error('Error fetching realtime detailed data:', error); res.status(500).json({ success: false, error: error.message }); } }); // User behavior endpoint router.get('/user-behavior', async (req, res) => { try { const { timeRange = '30' } = req.query; const cacheKey = `analytics:user_behavior:${timeRange}`; // Check Redis cache const cachedData = await req.redisClient.get(cacheKey); if (cachedData) { logger.info('Returning cached user behavior data'); return res.json({ success: true, data: JSON.parse(cachedData) }); } // Fetch page data const [pageResponse] = await analyticsClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate: `${timeRange}daysAgo`, endDate: 'today' }], dimensions: [{ name: 'pagePath' }], metrics: [ { name: 'screenPageViews' }, { name: 'averageSessionDuration' }, { name: 'bounceRate' }, { name: 'sessions' } ], orderBy: [{ metric: { metricName: 'screenPageViews' }, desc: true }], limit: 25 }); // Fetch device data const [deviceResponse] = await analyticsClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate: `${timeRange}daysAgo`, endDate: 'today' }], dimensions: [{ name: 'deviceCategory' }], metrics: [ { name: 'screenPageViews' }, { name: 'sessions' } ] }); // Fetch source data const [sourceResponse] = await analyticsClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate: `${timeRange}daysAgo`, endDate: 'today' }], dimensions: [{ name: 'sessionSource' }], metrics: [ { name: 'sessions' }, { name: 'conversions' } ], orderBy: [{ metric: { metricName: 'sessions' }, desc: true }], limit: 25, returnPropertyQuota: true }); const response = { pageResponse, deviceResponse, sourceResponse }; // Cache the response await req.redisClient.set(cacheKey, JSON.stringify(response), { EX: CACHE_DURATIONS.USER_BEHAVIOR }); res.json({ success: true, data: response }); } catch (error) { logger.error('Error fetching user behavior data:', error); res.status(500).json({ success: false, error: error.message }); } }); module.exports = router;