Add gorgias component and services
This commit is contained in:
338
examples DO NOT USE OR EDIT/EXAMPLE ONLY analytics.service.js
Normal file
338
examples DO NOT USE OR EDIT/EXAMPLE ONLY analytics.service.js
Normal file
@@ -0,0 +1,338 @@
|
||||
// services/analytics.service.js
|
||||
const { BetaAnalyticsDataClient } = require('@google-analytics/data');
|
||||
const Analytics = require('../models/analytics.model');
|
||||
const { createClient } = require('redis');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
class AnalyticsService {
|
||||
constructor() {
|
||||
// Initialize Redis client
|
||||
this.redis = createClient({
|
||||
url: process.env.REDIS_URL
|
||||
});
|
||||
|
||||
this.redis.on('error', err => logger.error('Redis Client Error:', err));
|
||||
this.redis.connect().catch(err => logger.error('Redis connection error:', err));
|
||||
|
||||
// Initialize GA4 client
|
||||
this.analyticsClient = new BetaAnalyticsDataClient({
|
||||
credentials: JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS_JSON)
|
||||
});
|
||||
|
||||
this.propertyId = process.env.GA_PROPERTY_ID;
|
||||
}
|
||||
|
||||
async getBasicMetrics(params = {}) {
|
||||
const cacheKey = `analytics:basic_metrics:${JSON.stringify(params)}`;
|
||||
logger.info(`Fetching basic metrics with params:`, params);
|
||||
|
||||
try {
|
||||
// Try Redis first
|
||||
const cachedData = await this.redis.get(cacheKey);
|
||||
if (cachedData) {
|
||||
logger.info('Analytics metrics found in Redis cache');
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
|
||||
// Check MongoDB using new findValidCache method
|
||||
const mongoData = await Analytics.findValidCache('basic_metrics', params);
|
||||
|
||||
if (mongoData) {
|
||||
logger.info('Analytics metrics found in MongoDB');
|
||||
const formattedData = mongoData.formatResponse();
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('basic_metrics')
|
||||
});
|
||||
return formattedData;
|
||||
}
|
||||
|
||||
// Fetch fresh data from GA4
|
||||
logger.info('Fetching fresh metrics data from GA4');
|
||||
const [response] = await this.analyticsClient.runReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dateRanges: [{
|
||||
startDate: params.startDate || '7daysAgo',
|
||||
endDate: 'today'
|
||||
}],
|
||||
dimensions: [{ name: 'date' }],
|
||||
metrics: [
|
||||
{ name: 'activeUsers' },
|
||||
{ name: 'newUsers' },
|
||||
{ name: 'averageSessionDuration' },
|
||||
{ name: 'screenPageViews' },
|
||||
{ name: 'bounceRate' },
|
||||
{ name: 'conversions' }
|
||||
],
|
||||
returnPropertyQuota: true
|
||||
});
|
||||
|
||||
// Create new Analytics document with fresh data
|
||||
const analyticsDoc = await Analytics.create({
|
||||
type: 'basic_metrics',
|
||||
params,
|
||||
data: response,
|
||||
quotaInfo: response.propertyQuota
|
||||
});
|
||||
|
||||
const formattedData = analyticsDoc.formatResponse();
|
||||
|
||||
// Save to Redis
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('basic_metrics')
|
||||
});
|
||||
|
||||
return formattedData;
|
||||
} catch (error) {
|
||||
logger.error('Error fetching analytics metrics:', {
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getRealTimeBasicData() {
|
||||
const cacheKey = 'analytics:realtime:basic';
|
||||
logger.info('Fetching realtime basic data');
|
||||
|
||||
try {
|
||||
// Try Redis first
|
||||
const [cachedData, lastUpdated] = await Promise.all([
|
||||
this.redis.get(cacheKey),
|
||||
this.redis.get(`${cacheKey}:lastUpdated`)
|
||||
]);
|
||||
|
||||
if (cachedData) {
|
||||
logger.info('Realtime basic data found in Redis cache:', cachedData);
|
||||
return {
|
||||
...JSON.parse(cachedData),
|
||||
lastUpdated: lastUpdated ? new Date(lastUpdated).toISOString() : new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch fresh data
|
||||
logger.info(`Fetching fresh realtime data from GA4 server`);
|
||||
const [userResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
metrics: [{ name: 'activeUsers' }],
|
||||
returnPropertyQuota: true
|
||||
});
|
||||
logger.info('GA4 user response:', userResponse);
|
||||
|
||||
const [fiveMinResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
metrics: [{ name: 'activeUsers' }],
|
||||
minuteRanges: [{ startMinutesAgo: 5, endMinutesAgo: 0 }]
|
||||
});
|
||||
|
||||
const [timeSeriesResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dimensions: [{ name: 'minutesAgo' }],
|
||||
metrics: [{ name: 'activeUsers' }]
|
||||
});
|
||||
|
||||
// Create new Analytics document
|
||||
const analyticsDoc = await Analytics.create({
|
||||
type: 'realtime_basic',
|
||||
data: {
|
||||
userResponse,
|
||||
fiveMinResponse,
|
||||
timeSeriesResponse,
|
||||
quotaInfo: {
|
||||
projectHourly: userResponse.propertyQuota.tokensPerProjectPerHour,
|
||||
daily: userResponse.propertyQuota.tokensPerDay,
|
||||
serverErrors: userResponse.propertyQuota.serverErrorsPerProjectPerHour,
|
||||
thresholdedRequests: userResponse.propertyQuota.potentiallyThresholdedRequestsPerHour
|
||||
}
|
||||
},
|
||||
quotaInfo: userResponse.propertyQuota
|
||||
});
|
||||
|
||||
const formattedData = analyticsDoc.formatResponse();
|
||||
|
||||
// Save to Redis
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('realtime_basic')
|
||||
});
|
||||
|
||||
return formattedData;
|
||||
} catch (error) {
|
||||
logger.error('Detailed error in getRealTimeBasicData:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
code: error.code,
|
||||
response: error.response?.data
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getRealTimeDetailedData() {
|
||||
const cacheKey = 'analytics:realtime:detailed';
|
||||
logger.info('Fetching realtime detailed data');
|
||||
|
||||
try {
|
||||
// Check Redis first
|
||||
const cachedData = await this.redis.get(cacheKey);
|
||||
if (cachedData) {
|
||||
logger.info('Realtime detailed data found in Redis cache');
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
|
||||
// Fetch fresh data from GA4
|
||||
const [pageResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dimensions: [{ name: 'unifiedScreenName' }],
|
||||
metrics: [{ name: 'screenPageViews' }],
|
||||
orderBy: [{ metric: { metricName: 'screenPageViews' }, desc: true }],
|
||||
limit: 25
|
||||
});
|
||||
|
||||
const [eventResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dimensions: [{ name: 'eventName' }],
|
||||
metrics: [{ name: 'eventCount' }],
|
||||
orderBy: [{ metric: { metricName: 'eventCount' }, desc: true }],
|
||||
limit: 25
|
||||
});
|
||||
|
||||
const [deviceResponse] = await this.analyticsClient.runRealtimeReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dimensions: [{ name: 'deviceCategory' }],
|
||||
metrics: [{ name: 'activeUsers' }],
|
||||
orderBy: [{ metric: { metricName: 'activeUsers' }, desc: true }],
|
||||
limit: 10,
|
||||
returnPropertyQuota: true
|
||||
});
|
||||
|
||||
// Create new Analytics document
|
||||
const analyticsDoc = await Analytics.create({
|
||||
type: 'realtime_detailed',
|
||||
data: {
|
||||
pageResponse,
|
||||
eventResponse,
|
||||
sourceResponse: deviceResponse
|
||||
},
|
||||
quotaInfo: deviceResponse.propertyQuota
|
||||
});
|
||||
|
||||
const formattedData = analyticsDoc.formatResponse();
|
||||
|
||||
// Save to Redis
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('realtime_detailed')
|
||||
});
|
||||
|
||||
return formattedData;
|
||||
} catch (error) {
|
||||
logger.error('Error fetching realtime detailed data:', {
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getUserBehavior(params = {}) {
|
||||
const cacheKey = `analytics:user_behavior:${JSON.stringify(params)}`;
|
||||
const timeRange = params.timeRange || '7';
|
||||
|
||||
logger.info('Fetching user behavior data', { params });
|
||||
|
||||
try {
|
||||
// Try Redis first
|
||||
const cachedData = await this.redis.get(cacheKey);
|
||||
if (cachedData) {
|
||||
logger.info('User behavior data found in Redis cache');
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
|
||||
// Check MongoDB using new findValidCache method
|
||||
const mongoData = await Analytics.findValidCache('user_behavior', params);
|
||||
|
||||
if (mongoData) {
|
||||
logger.info('User behavior data found in MongoDB');
|
||||
const formattedData = mongoData.formatResponse();
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('user_behavior')
|
||||
});
|
||||
return formattedData;
|
||||
}
|
||||
|
||||
// Fetch fresh data from GA4
|
||||
logger.info('Fetching fresh user behavior data from GA4');
|
||||
|
||||
const [pageResponse] = await this.analyticsClient.runReport({
|
||||
property: `properties/${this.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
|
||||
});
|
||||
|
||||
const [deviceResponse] = await this.analyticsClient.runReport({
|
||||
property: `properties/${this.propertyId}`,
|
||||
dateRanges: [{ startDate: `${timeRange}daysAgo`, endDate: 'today' }],
|
||||
dimensions: [{ name: 'deviceCategory' }],
|
||||
metrics: [
|
||||
{ name: 'screenPageViews' },
|
||||
{ name: 'sessions' }
|
||||
]
|
||||
});
|
||||
|
||||
const [sourceResponse] = await this.analyticsClient.runReport({
|
||||
property: `properties/${this.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
|
||||
});
|
||||
|
||||
// Create new Analytics document
|
||||
const analyticsDoc = await Analytics.create({
|
||||
type: 'user_behavior',
|
||||
params,
|
||||
data: {
|
||||
pageResponse,
|
||||
deviceResponse,
|
||||
sourceResponse
|
||||
},
|
||||
quotaInfo: sourceResponse.propertyQuota
|
||||
});
|
||||
|
||||
const formattedData = analyticsDoc.formatResponse();
|
||||
|
||||
// Save to Redis
|
||||
await this.redis.set(cacheKey, JSON.stringify(formattedData), {
|
||||
EX: Analytics.getCacheDuration('user_behavior')
|
||||
});
|
||||
|
||||
return formattedData;
|
||||
} catch (error) {
|
||||
logger.error('Error fetching user behavior data:', {
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AnalyticsService();
|
||||
Reference in New Issue
Block a user