diff --git a/app/apps/server/bridges/scheduler.ts b/app/apps/server/bridges/scheduler.ts index fc3c519539b32..6831861b5dbae 100644 --- a/app/apps/server/bridges/scheduler.ts +++ b/app/apps/server/bridges/scheduler.ts @@ -10,8 +10,15 @@ import { SchedulerBridge } from '@rocket.chat/apps-engine/server/bridges/Schedul import { AppServerOrchestrator } from '../orchestrator'; -function _callProcessor(processor: Function): (job: { attrs?: { data: object } }) => object { - return (job): Function => processor(job?.attrs?.data || {}); +function _callProcessor(processor: Function): (job: { attrs?: { data: object } }) => void { + return (job): void => { + const data = job?.attrs?.data || {}; + + // This field is for internal use, no need to leak to app processor + delete (data as any).appId; + + processor(data); + }; } /** @@ -106,7 +113,7 @@ export class AppSchedulerBridge extends SchedulerBridge { this.orch.debugLog(`The App ${ appId } is scheduling an onetime job`, job); try { await this.startScheduler(); - await this.scheduler.schedule(job.when, job.id, job.data || {}); + await this.scheduler.schedule(job.when, job.id, this.decorateJobData(job.data, appId)); } catch (e) { this.orch.getRocketChatLogger().error(e); } @@ -135,7 +142,7 @@ export class AppSchedulerBridge extends SchedulerBridge { this.orch.debugLog(`The App ${ appId } is scheduling a recurring job`, id); try { await this.startScheduler(); - await this.scheduler.every(interval, id, data || {}, { skipImmediate }); + await this.scheduler.every(interval, id, this.decorateJobData(data, appId), { skipImmediate }); } catch (e) { this.orch.getRocketChatLogger().error(e); } @@ -183,4 +190,8 @@ export class AppSchedulerBridge extends SchedulerBridge { this.isConnected = true; } } + + private decorateJobData(jobData: object | undefined, appId: string): object { + return Object.assign({}, jobData, { appId }); + } } diff --git a/app/apps/server/orchestrator.js b/app/apps/server/orchestrator.js index b72d7aacb6d65..50f67ae497c58 100644 --- a/app/apps/server/orchestrator.js +++ b/app/apps/server/orchestrator.js @@ -176,6 +176,27 @@ export const Apps = new AppServerOrchestrator(); settings.addGroup('General', function() { this.section('Apps', function() { + this.add('Apps_Logs_TTL', '30_days', { + type: 'select', + values: [ + { + key: '7_days', + i18nLabel: 'Apps_Logs_TTL_7days', + }, + { + key: '14_days', + i18nLabel: 'Apps_Logs_TTL_14days', + }, + { + key: '30_days', + i18nLabel: 'Apps_Logs_TTL_30days', + }, + ], + public: true, + hidden: false, + alert: 'Apps_Logs_TTL_Alert', + }); + this.add('Apps_Framework_enabled', true, { type: 'boolean', hidden: false, @@ -207,6 +228,34 @@ settings.get('Apps_Framework_enabled', (key, isEnabled) => { } }); +settings.get('Apps_Logs_TTL', (key, value) => { + if (!Apps.isInitialized()) { + return; + } + + let expireAfterSeconds = 0; + + switch (value) { + case '7_days': + expireAfterSeconds = 604800; + break; + case '14_days': + expireAfterSeconds = 1209600; + break; + case '30_days': + expireAfterSeconds = 2592000; + break; + } + + if (!expireAfterSeconds) { + return; + } + + const model = Apps._logModel; + + model.resetTTLIndex(expireAfterSeconds); +}); + Meteor.startup(function _appServerOrchestrator() { Apps.initialize(); diff --git a/app/models/server/models/apps-logs-model.js b/app/models/server/models/apps-logs-model.js index 529f4cdfa25b7..60b38956e0dea 100644 --- a/app/models/server/models/apps-logs-model.js +++ b/app/models/server/models/apps-logs-model.js @@ -11,4 +11,9 @@ export class AppsLogsModel extends Base { remove(query) { return this._db.originals.remove(query); } + + resetTTLIndex(expireAfterSeconds) { + this.tryDropIndex({ _updatedAt: 1 }); + this.tryEnsureIndex({ _updatedAt: 1 }, { expireAfterSeconds }); + } } diff --git a/app/models/server/models/apps-persistence-model.js b/app/models/server/models/apps-persistence-model.js index bad009b7faeb0..da178a390c327 100644 --- a/app/models/server/models/apps-persistence-model.js +++ b/app/models/server/models/apps-persistence-model.js @@ -3,5 +3,12 @@ import { Base } from './_Base'; export class AppsPersistenceModel extends Base { constructor() { super('apps_persistence'); + + this.tryEnsureIndex({ appId: 1 }); + } + + // Bypass trash collection + remove(query) { + return this._db.originals.remove(query); } } diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 9994c1ef53fcc..40914afd74f4f 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -463,6 +463,11 @@ "Apps_License_Message_publicKey": "There has been an error trying to decrypt the license. Please sync your workspace in the Connectivity Services and try again", "Apps_License_Message_renewal": "License has expired and needs to be renewed", "Apps_License_Message_seats": "License does not have enough seats to accommodate the current amount of active users. Please increase the number of seats", + "Apps_Logs_TTL": "Number of days to keep logs from apps stored", + "Apps_Logs_TTL_7days": "7 days", + "Apps_Logs_TTL_14days": "14 days", + "Apps_Logs_TTL_30days": "30 days", + "Apps_Logs_TTL_Alert": "Depending on the size of the Logs collection, changing this setting may cause slowness for some moments", "Apps_Marketplace_Deactivate_App_Prompt": "Do you really want to disable this app?", "Apps_Marketplace_Login_Required_Description": "Purchasing apps from the Rocket.Chat Marketplace requires registering your workspace and logging in.", "Apps_Marketplace_Login_Required_Title": "Marketplace Login Required", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 9bbfa753a8009..2ad01cad4bfeb 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -406,6 +406,11 @@ "Apps_License_Message_publicKey": "Ocorreu um erro ao tentar descriptografar a licença. Por favor, sincronize seu workspace em Connectivity Services", "Apps_License_Message_renewal": "A licença expirou e precisa ser renovada", "Apps_License_Message_seats": "A licença não comporta a quantidade atual de usuários ativos. Por favor, aumente o número de seats contratados", + "Apps_Logs_TTL": "Número de dias para manter logs de apps", + "Apps_Logs_TTL_7days": "7 dias", + "Apps_Logs_TTL_14days": "14 dias", + "Apps_Logs_TTL_30days": "30 dias", + "Apps_Logs_TTL_Alert": "Dependendo do tamanho da coleção de Logs, a alteração desta configuração pode causar lentidão no sistema por alguns momentos", "Apps_Marketplace_Deactivate_App_Prompt": "Você quer mesmo desativar este aplicativo?", "Apps_Marketplace_Login_Required_Title": "Login do Marketplace Obrigatório", "Apps_Marketplace_Modify_App_Subscription": "Modificar a Subscrição", @@ -3702,4 +3707,4 @@ "Your_question": "A sua pergunta", "Your_server_link": "O link do seu servidor", "Your_workspace_is_ready": "O seu espaço de trabalho está pronto a usar 🎉" -} \ No newline at end of file +}