const axios = require("axios"); const { prisma } = require("./shared/prisma"); const { updateSettings } = require("../../services/settingsService"); const METRICS_API_URL = process.env.METRICS_API_URL || "https://metrics.patchmon.cloud"; /** * Metrics Reporting Automation * Sends anonymous usage metrics every 24 hours */ class MetricsReporting { constructor(queueManager) { this.queueManager = queueManager; this.queueName = "metrics-reporting"; } /** * Process metrics reporting job */ async process(_job, silent = false) { const startTime = Date.now(); if (!silent) console.log("📊 Starting metrics reporting..."); try { // Fetch fresh settings directly from database (bypass cache) const settings = await prisma.settings.findFirst({ orderBy: { updated_at: "desc" }, }); // Check if metrics are enabled if (settings.metrics_enabled !== true) { if (!silent) console.log("📊 Metrics reporting is disabled"); return { success: false, reason: "disabled" }; } // Check if we have an anonymous ID if (!settings.metrics_anonymous_id) { if (!silent) console.log("📊 No anonymous ID found, skipping metrics"); return { success: false, reason: "no_id" }; } // Get host count const hostCount = await prisma.hosts.count(); // Get version const packageJson = require("../../../package.json"); const version = packageJson.version; // Prepare metrics data const metricsData = { anonymous_id: settings.metrics_anonymous_id, host_count: hostCount, version, }; if (!silent) console.log( `📊 Sending metrics: ${hostCount} hosts, version ${version}`, ); // Send to metrics API try { const response = await axios.post( `${METRICS_API_URL}/metrics/submit`, metricsData, { timeout: 10000, headers: { "Content-Type": "application/json", }, }, ); // Update last sent timestamp await updateSettings(settings.id, { metrics_last_sent: new Date(), }); const executionTime = Date.now() - startTime; if (!silent) console.log( `✅ Metrics sent successfully in ${executionTime}ms:`, response.data, ); return { success: true, data: response.data, hostCount, version, executionTime, }; } catch (apiError) { const executionTime = Date.now() - startTime; if (!silent) console.error( `❌ Failed to send metrics to API after ${executionTime}ms:`, apiError.message, ); return { success: false, reason: "api_error", error: apiError.message, executionTime, }; } } catch (error) { const executionTime = Date.now() - startTime; if (!silent) console.error( `❌ Error in metrics reporting after ${executionTime}ms:`, error.message, ); // Don't throw on silent mode, just return failure if (silent) { return { success: false, reason: "error", error: error.message, executionTime, }; } throw error; } } /** * Schedule recurring metrics reporting (daily at 2 AM) */ async schedule() { const job = await this.queueManager.queues[this.queueName].add( "metrics-reporting", {}, { repeat: { cron: "0 2 * * *" }, // Daily at 2 AM jobId: "metrics-reporting-recurring", }, ); console.log("✅ Metrics reporting scheduled (daily at 2 AM)"); return job; } /** * Trigger manual metrics reporting */ async triggerManual() { const job = await this.queueManager.queues[this.queueName].add( "metrics-reporting-manual", {}, { priority: 1 }, ); console.log("✅ Manual metrics reporting triggered"); return job; } /** * Send metrics immediately (silent mode) * Used for automatic sending on server startup */ async sendSilent() { try { const result = await this.process({ name: "startup-silent" }, true); return result; } catch (error) { // Silent failure on startup return { success: false, reason: "error", error: error.message }; } } } module.exports = MetricsReporting;