From 27bbf7938fd5fda5de6620556e1270c832d6f5b6 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Tue, 30 Dec 2025 13:06:12 -0300 Subject: [PATCH 1/4] feat: add env to disable statistic reporting --- .../statistics/server/functions/sendUsageReport.ts | 13 ++++++++++--- apps/meteor/server/cron/usageReport.ts | 10 +++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts index c39276be6f357..082e1f9135cfe 100644 --- a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts @@ -34,6 +34,10 @@ async function sendStats(logger: Logger, cronStatistics: IStats): Promise { + // Even when disabled, we still generate statistics locally to avoid breaking + // internal processes, such as restriction checks for air-gapped workspaces. + const shouldSendToCollector = process.env.RC_DISABLE_STATISTICS_REPORTING?.toLowerCase() !== 'true'; + return tracerSpan('generateStatistics', {}, async () => { const last = await Statistics.findLast(); if (last) { @@ -48,13 +52,16 @@ export async function sendUsageReport(logger: Logger): Promise { - const name = 'Generate and save statistics'; + // The actual send suppression happens inside `sendUsageReport`, but since this + // is the entry point, we log a warning here when reporting is disabled. + const shouldSendToCollector = process.env.RC_DISABLE_STATISTICS_REPORTING?.toLowerCase() !== 'true'; + if (!shouldSendToCollector) { + logger.warn( + 'Statistics reporting disabled via environment variable (RC_DISABLE_STATISTICS_REPORTING). This may impact product improvements.', + ); + } const statsToken = await sendUsageReport(logger); await sendUsageReportAndComputeRestriction(statsToken); + const name = 'Generate and save statistics'; const now = new Date(); return cronJobs.add(name, `12 ${now.getHours()} * * *`, async () => { From 9f61a7d6bbd5a50bd05d0e67cca8337e394bd619 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Tue, 30 Dec 2025 19:09:05 -0300 Subject: [PATCH 2/4] test: add sendUsageReport unit tests --- apps/meteor/.mocharc.js | 1 + .../server/functions/sendUsageReport.spec.ts | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 apps/meteor/app/statistics/server/functions/sendUsageReport.spec.ts diff --git a/apps/meteor/.mocharc.js b/apps/meteor/.mocharc.js index f9f780024f964..cf4b306318cfc 100644 --- a/apps/meteor/.mocharc.js +++ b/apps/meteor/.mocharc.js @@ -28,5 +28,6 @@ module.exports = { 'tests/unit/server/**/*.spec.ts', 'app/api/server/lib/**/*.spec.ts', 'app/file-upload/server/**/*.spec.ts', + 'app/statistics/server/**/*.spec.ts', ], }; diff --git a/apps/meteor/app/statistics/server/functions/sendUsageReport.spec.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.spec.ts new file mode 100644 index 0000000000000..2e9da97aa733f --- /dev/null +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.spec.ts @@ -0,0 +1,63 @@ +import { expect } from 'chai'; +import { describe, it, beforeEach, afterEach } from 'mocha'; +import proxyquire from 'proxyquire'; +import sinon from 'sinon'; + +const sandbox = sinon.createSandbox(); + +const mocks = { + Statistics: { + findLast: sandbox.stub(), + updateOne: sandbox.stub(), + }, + statistics: { + save: sandbox.stub(), + }, + serverFetch: sandbox.stub(), + getWorkspaceAccessToken: sandbox.stub().resolves('workspace-token'), + Meteor: { + absoluteUrl: sandbox.stub().returns('http://localhost:3000/'), + }, + logger: { + error: sandbox.stub(), + }, +}; + +const { sendUsageReport } = proxyquire.noCallThru().load('./sendUsageReport', { + '@rocket.chat/models': { Statistics: mocks.Statistics }, + '@rocket.chat/server-fetch': { serverFetch: mocks.serverFetch }, + '..': { statistics: mocks.statistics }, + '../../../cloud/server': { getWorkspaceAccessToken: mocks.getWorkspaceAccessToken }, + 'meteor/meteor': { Meteor: mocks.Meteor }, +}); + +describe('sendUsageReport', () => { + beforeEach(() => { + sandbox.resetHistory(); + }); + + afterEach(() => { + delete process.env.RC_DISABLE_STATISTICS_REPORTING; + }); + + it('should save statistics locally and not send to collector when RC_DISABLE_STATISTICS_REPORTING is true', async () => { + process.env.RC_DISABLE_STATISTICS_REPORTING = 'true'; + + const result = await sendUsageReport(mocks.logger); + + expect(mocks.statistics.save.called).to.be.true; + expect(mocks.serverFetch.called).to.be.false; + expect(result).to.be.undefined; + }); + + it('should save statistics locally and send to collector when RC_DISABLE_STATISTICS_REPORTING is false', async () => { + process.env.RC_DISABLE_STATISTICS_REPORTING = 'false'; + + const result = await sendUsageReport(mocks.logger); + + expect(mocks.statistics.save.called).to.be.true; + expect(mocks.serverFetch.calledOnce).to.be.true; + expect(mocks.serverFetch.calledWith('https://collector.rocket.chat/', sinon.match({ method: 'POST' }))).to.be.true; + expect(result).to.be.undefined; + }); +}); From 81bec2014ae1ea99aeac3f6ebe6a709fd8885641 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Wed, 31 Dec 2025 09:44:02 -0300 Subject: [PATCH 3/4] fix: add missing return on sendUsageReport --- apps/meteor/app/statistics/server/functions/sendUsageReport.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts index 082e1f9135cfe..7027bd03861e8 100644 --- a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts @@ -55,6 +55,8 @@ export async function sendUsageReport(logger: Logger): Promise Date: Wed, 7 Jan 2026 14:38:51 -0300 Subject: [PATCH 4/4] chore: add RC_DISABLE_STATISTICS_REPORTING check to a common function --- .../app/statistics/server/functions/sendUsageReport.ts | 3 ++- apps/meteor/server/cron/usageReport.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts index 7027bd03861e8..a5bc91a639621 100644 --- a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts @@ -6,6 +6,7 @@ import { tracerSpan } from '@rocket.chat/tracing'; import { Meteor } from 'meteor/meteor'; import { statistics } from '..'; +import { shouldReportStatistics } from '../../../../server/cron/usageReport'; import { getWorkspaceAccessToken } from '../../../cloud/server'; async function sendStats(logger: Logger, cronStatistics: IStats): Promise { @@ -36,7 +37,7 @@ async function sendStats(logger: Logger, cronStatistics: IStats): Promise { // Even when disabled, we still generate statistics locally to avoid breaking // internal processes, such as restriction checks for air-gapped workspaces. - const shouldSendToCollector = process.env.RC_DISABLE_STATISTICS_REPORTING?.toLowerCase() !== 'true'; + const shouldSendToCollector = shouldReportStatistics(); return tracerSpan('generateStatistics', {}, async () => { const last = await Statistics.findLast(); diff --git a/apps/meteor/server/cron/usageReport.ts b/apps/meteor/server/cron/usageReport.ts index b1b0888121676..c4afec47de1fc 100644 --- a/apps/meteor/server/cron/usageReport.ts +++ b/apps/meteor/server/cron/usageReport.ts @@ -13,11 +13,12 @@ export const sendUsageReportAndComputeRestriction = async (statsToken?: string) void AirGappedRestriction.computeRestriction(token); }; +export const shouldReportStatistics = () => process.env.RC_DISABLE_STATISTICS_REPORTING?.toLowerCase() !== 'true'; + export async function usageReportCron(logger: Logger): Promise { // The actual send suppression happens inside `sendUsageReport`, but since this // is the entry point, we log a warning here when reporting is disabled. - const shouldSendToCollector = process.env.RC_DISABLE_STATISTICS_REPORTING?.toLowerCase() !== 'true'; - if (!shouldSendToCollector) { + if (!shouldReportStatistics()) { logger.warn( 'Statistics reporting disabled via environment variable (RC_DISABLE_STATISTICS_REPORTING). This may impact product improvements.', );