recoder-code
Version:
🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!
367 lines • 14 kB
JavaScript
"use strict";
/**
* Analytics Service - Handles tracking and analytics for packages
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyticsService = void 0;
const Package_1 = require("../entities/Package");
const Download_1 = require("../entities/Download");
const User_1 = require("../entities/User");
const database_1 = require("../database");
class AnalyticsService {
constructor() {
this.logger = {
info: (msg, ...args) => console.log(`[INFO] ${msg}`, ...args),
error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),
warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args)
};
this.packageRepo = database_1.AppDataSource.getRepository(Package_1.Package);
this.downloadRepo = database_1.AppDataSource.getRepository(Download_1.Download);
this.userRepo = database_1.AppDataSource.getRepository(User_1.User);
}
async getGlobalAnalytics() {
try {
const [totalPackages, activePackages, deprecatedPackages, totalUsers, activeUsers, totalDownloads, popularPackages, trendingPackages, recentlyUpdated] = await Promise.all([
this.packageRepo.count(),
this.packageRepo.count({ where: { status: Package_1.PackageStatus.ACTIVE } }),
this.packageRepo.count({ where: { status: Package_1.PackageStatus.DEPRECATED } }),
this.userRepo.count(),
this.userRepo.count({ where: { is_active: true } }),
this.getTotalDownloads(),
this.getPopularPackages(10),
this.getTrendingPackages(10),
this.getRecentlyUpdatedPackages(10)
]);
return {
downloads: {
total: totalDownloads,
daily: await this.getDailyDownloads(),
weekly: await this.getWeeklyDownloads(),
monthly: await this.getMonthlyDownloads(),
yearly: await this.getYearlyDownloads()
},
packages: {
total: totalPackages,
active: activePackages,
deprecated: deprecatedPackages
},
users: {
total: totalUsers,
active: activeUsers
},
trends: {
popularPackages,
trendingPackages,
recentlyUpdated
}
};
}
catch (error) {
this.logger.error('Failed to get global analytics:', error);
throw error;
}
}
async getPackageAnalytics(packageName) {
try {
const pkg = await this.packageRepo.findOne({
where: { name: packageName },
relations: ['versions']
});
if (!pkg) {
return null;
}
const [totalDownloads, dailyDownloads, weeklyDownloads, monthlyDownloads, dependentsCount, qualityScore] = await Promise.all([
this.getPackageDownloads(pkg.id),
this.getPackageDailyDownloads(pkg.id),
this.getPackageWeeklyDownloads(pkg.id),
this.getPackageMonthlyDownloads(pkg.id),
this.getPackageDependents(pkg.id),
this.getPackageQualityScore(pkg.id)
]);
const popularVersions = await this.getPopularVersions(pkg.id);
return {
packageId: pkg.id,
packageName: pkg.name,
downloads: {
total: totalDownloads,
daily: dailyDownloads,
weekly: weeklyDownloads,
monthly: monthlyDownloads
},
versions: {
total: pkg.versions.length,
latest: pkg.latest_version || '',
popular: popularVersions
},
dependencies: {
count: pkg.stats?.dependencies || 0,
dependents: dependentsCount
},
quality: {
score: qualityScore,
metrics: pkg.quality_metrics || {}
}
};
}
catch (error) {
this.logger.error(`Failed to get analytics for package ${packageName}:`, error);
throw error;
}
}
async recordDownload(packageId, version, userId, ip) {
try {
// Record download event
const download = this.downloadRepo.create({
package_id: packageId,
version_id: packageId,
user_id: userId,
ip_address: ip || 'unknown',
user_agent: '',
date: new Date(),
date_only: new Date().toISOString().split('T')[0]
});
await this.downloadRepo.save(download);
// Update package download count
await this.packageRepo.increment({ id: packageId }, 'download_count', 1);
this.logger.info(`Recorded download: ${packageId}@${version}`);
}
catch (error) {
this.logger.error('Failed to record download:', error);
throw error;
}
}
async getTotalDownloads() {
const result = await this.downloadRepo.count();
return result;
}
async getDailyDownloads() {
const today = new Date();
today.setHours(0, 0, 0, 0);
return this.downloadRepo.createQueryBuilder('download')
.where('download.date >= :today', { today })
.getCount();
}
async getWeeklyDownloads() {
const weekAgo = new Date();
weekAgo.setDate(weekAgo.getDate() - 7);
return this.downloadRepo.createQueryBuilder('download')
.where('download.date >= :weekAgo', { weekAgo })
.getCount();
}
async getMonthlyDownloads() {
const monthAgo = new Date();
monthAgo.setMonth(monthAgo.getMonth() - 1);
return this.downloadRepo.createQueryBuilder('download')
.where('download.date >= :monthAgo', { monthAgo })
.getCount();
}
async getYearlyDownloads() {
const yearAgo = new Date();
yearAgo.setFullYear(yearAgo.getFullYear() - 1);
return this.downloadRepo.createQueryBuilder('download')
.where('download.date >= :yearAgo', { yearAgo })
.getCount();
}
async getPopularPackages(limit) {
return this.packageRepo.find({
order: { download_count: 'DESC' },
take: limit,
relations: ['owner']
});
}
async getTrendingPackages(limit) {
// Get packages with highest download growth in the last week
return this.packageRepo.createQueryBuilder('package')
.leftJoinAndSelect('package.owner', 'owner')
.orderBy('package.download_count', 'DESC')
.take(limit)
.getMany();
}
async getRecentlyUpdatedPackages(limit) {
return this.packageRepo.find({
order: { updated_at: 'DESC' },
take: limit,
relations: ['owner']
});
}
async getPackageDownloads(packageId) {
return this.downloadRepo.count({
where: { package_id: packageId }
});
}
async getPackageDailyDownloads(packageId) {
// Implementation would aggregate downloads by day
return {};
}
async getPackageWeeklyDownloads(packageId) {
// Implementation would aggregate downloads by week
return {};
}
async getPackageMonthlyDownloads(packageId) {
// Implementation would aggregate downloads by month
return {};
}
async getPackageDependents(packageId) {
// Implementation would count packages that depend on this package
return 0;
}
async getPackageQualityScore(packageId) {
const pkg = await this.packageRepo.findOne({
where: { id: packageId }
});
if (!pkg?.quality_metrics) {
return 0;
}
// Calculate overall quality score from metrics
const metrics = pkg.quality_metrics;
return ((metrics.code_quality || 0) +
(metrics.documentation || 0) +
(metrics.testing || 0) +
(metrics.popularity || 0) +
(metrics.maintenance || 0)) / 5;
}
async getPopularVersions(packageId) {
// Implementation would aggregate downloads by version
return [];
}
async updatePackageQuality(packageId, metrics) {
try {
await this.packageRepo.update(packageId, {
quality_metrics: metrics
});
this.logger.info(`Updated quality metrics for package ${packageId}`);
}
catch (error) {
this.logger.error(`Failed to update quality metrics for package ${packageId}:`, error);
throw error;
}
}
async getDownloadTrends(packageId, period) {
try {
// Implementation would calculate download trends for the specified period
return {};
}
catch (error) {
this.logger.error(`Failed to get download trends for package ${packageId}:`, error);
throw error;
}
}
async trackPackageView(packageId, userId) {
try {
// Track package view for analytics
this.logger.info(`Package viewed: ${packageId} by user ${userId || 'anonymous'}`);
// Implementation would:
// - Record the view in analytics database
// - Update view counts
// - Track user engagement
}
catch (error) {
this.logger.error(`Failed to track package view for ${packageId}:`, error);
// Don't throw error for analytics failures
}
}
async trackVersionView(packageId, version, userId) {
try {
// Track package version view for analytics
this.logger.info(`Package version viewed: ${packageId}@${version} by user ${userId || 'anonymous'}`);
// Implementation would:
// - Record the version view in analytics database
// - Update version-specific view counts
// - Track version popularity
}
catch (error) {
this.logger.error(`Failed to track version view for ${packageId}@${version}:`, error);
// Don't throw error for analytics failures
}
}
async trackDownload(packageId, version, userId) {
try {
// Track package download for analytics
this.logger.info(`Package downloaded: ${packageId}@${version} by user ${userId || 'anonymous'}`);
// Implementation would:
// - Record the download in analytics database
// - Update download counts
// - Track download trends
}
catch (error) {
this.logger.error(`Failed to track download for ${packageId}@${version}:`, error);
// Don't throw error for analytics failures
}
}
async getPackageStats(packageId) {
try {
const pkg = await this.packageRepo.findOne({
where: { id: packageId },
relations: ['versions']
});
if (!pkg) {
return null;
}
return {
packageId: pkg.id,
packageName: pkg.name,
downloads: {
total: pkg.download_count || 0,
daily: {},
weekly: {},
monthly: {}
},
versions: {
total: pkg.versions?.length || 0,
latest: pkg.latest_version || '',
popular: []
},
dependencies: {
count: pkg.stats?.dependencies || 0,
dependents: 0
},
quality: {
score: await this.getPackageQualityScore(pkg.id),
metrics: pkg.quality_metrics || {}
}
};
}
catch (error) {
this.logger.error(`Failed to get package stats for ${packageId}:`, error);
return null;
}
}
async getDownloadStats(packageId, period) {
try {
// Get download statistics for a package over a period
const downloads = await this.downloadRepo.createQueryBuilder('download')
.where('download.package_id = :packageId', { packageId })
.andWhere('download.date >= :startDate', {
startDate: this.getStartDateForPeriod(period)
})
.getCount();
return {
packageId,
period,
downloads,
breakdown: {}
};
}
catch (error) {
this.logger.error(`Failed to get download stats for ${packageId}:`, error);
return { packageId, period, downloads: 0, breakdown: {} };
}
}
getStartDateForPeriod(period) {
const now = new Date();
switch (period) {
case 'day':
return new Date(now.getTime() - 24 * 60 * 60 * 1000);
case 'week':
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
case 'month':
return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
case 'year':
return new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
default:
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
}
}
}
exports.AnalyticsService = AnalyticsService;
//# sourceMappingURL=AnalyticsService.js.map