From d2fba22e65d01bb21bff8e17ad0d993fccdffda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Thom=C3=A9?= Date: Wed, 23 Dec 2020 16:01:09 -0300 Subject: [PATCH 1/4] add currently running instance --- app/api/server/v1/instances.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/api/server/v1/instances.ts b/app/api/server/v1/instances.ts index 68efb9345d704..b6e12d083b94a 100644 --- a/app/api/server/v1/instances.ts +++ b/app/api/server/v1/instances.ts @@ -1,6 +1,7 @@ import { getInstances } from '../../../../server/stream/streamBroadcast'; import { hasPermission } from '../../../authorization/server'; import { API } from '../api'; +import InstanceStatus from '../../../models/server/models/InstanceStatus'; API.v1.addRoute('instances.get', { authRequired: true }, { get() { @@ -8,6 +9,17 @@ API.v1.addRoute('instances.get', { authRequired: true }, { return API.v1.unauthorized(); } - return API.v1.success({ instances: getInstances() }); + const connectedInstances = getInstances(); + const connectedInstancesIds = connectedInstances.map((x) => x.instanceRecord._id); + const currentInstanceStatus = InstanceStatus.find({ _id: { $nin: connectedInstancesIds } }).fetch(); + + const instances = { + current: { + instanceRecord: currentInstanceStatus, + }, + connections: connectedInstances, + }; + + return API.v1.success({ instances }); }, }); From 4dae7c2b0eb6c05967007f3258eafdfd4380d5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Thom=C3=A9?= Date: Wed, 23 Dec 2020 16:10:43 -0300 Subject: [PATCH 2/4] add tests --- tests/end-to-end/api/00-miscellaneous.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js index bca5fbd432ef2..553d089ef261e 100644 --- a/tests/end-to-end/api/00-miscellaneous.js +++ b/tests/end-to-end/api/00-miscellaneous.js @@ -446,4 +446,22 @@ describe('miscellaneous', function() { .end(done); }); }); + + describe('/instances.get', () => { + it('should return available instances', (done) => { + request.get(api('instances.get')) + .set(credentials) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('instances'); + + const { instances } = res.body; + + expect(instances).to.have.property('current'); + expect(instances).to.have.property('connections').and.to.be.an('array'); + }) + .end(done); + }); + }); }); From b581ff5917fc5252405de0aa3e00f829cc411d66 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 28 Dec 2020 20:49:41 -0300 Subject: [PATCH 3/4] Add more data to instances --- app/api/server/v1/instances.ts | 27 +++--- server/main.js | 1 + server/startup/instance.js | 33 +++++++ server/startup/presence.js | 12 --- server/stream/streamBroadcast.js | 111 +++++++++++++---------- tests/end-to-end/api/00-miscellaneous.js | 17 +++- 6 files changed, 126 insertions(+), 75 deletions(-) create mode 100644 server/startup/instance.js diff --git a/app/api/server/v1/instances.ts b/app/api/server/v1/instances.ts index b6e12d083b94a..e6586a7c12a73 100644 --- a/app/api/server/v1/instances.ts +++ b/app/api/server/v1/instances.ts @@ -1,7 +1,8 @@ -import { getInstances } from '../../../../server/stream/streamBroadcast'; +import { getInstanceConnection } from '../../../../server/stream/streamBroadcast'; import { hasPermission } from '../../../authorization/server'; import { API } from '../api'; import InstanceStatus from '../../../models/server/models/InstanceStatus'; +import { IInstanceStatus } from '../../../../definition/IInstanceStatus'; API.v1.addRoute('instances.get', { authRequired: true }, { get() { @@ -9,17 +10,19 @@ API.v1.addRoute('instances.get', { authRequired: true }, { return API.v1.unauthorized(); } - const connectedInstances = getInstances(); - const connectedInstancesIds = connectedInstances.map((x) => x.instanceRecord._id); - const currentInstanceStatus = InstanceStatus.find({ _id: { $nin: connectedInstancesIds } }).fetch(); + const instances = InstanceStatus.find().fetch(); - const instances = { - current: { - instanceRecord: currentInstanceStatus, - }, - connections: connectedInstances, - }; - - return API.v1.success({ instances }); + return API.v1.success({ + instances: instances.map((instance: IInstanceStatus) => { + const connection = getInstanceConnection(instance); + if (connection) { + delete connection.instanceRecord; + } + return { + ...instance, + connection, + }; + }), + }); }, }); diff --git a/server/main.js b/server/main.js index 2c1155f6e4d76..d00699b9735db 100644 --- a/server/main.js +++ b/server/main.js @@ -10,6 +10,7 @@ import './startup/migrations'; import './startup/appcache'; import './startup/cron'; import './startup/initialData'; +import './startup/instance'; import './startup/presence'; import './startup/serverRunning'; import './configuration/accounts_meld'; diff --git a/server/startup/instance.js b/server/startup/instance.js new file mode 100644 index 0000000000000..5dcebcb2be5b7 --- /dev/null +++ b/server/startup/instance.js @@ -0,0 +1,33 @@ +import os from 'os'; + +import { Meteor } from 'meteor/meteor'; +import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; + +import { startStreamBroadcast } from '../stream/streamBroadcast'; + +export function getInstances() { + InstanceStatus.find().fetch(); +} + +Meteor.startup(function() { + const instance = { + host: process.env.INSTANCE_IP ? String(process.env.INSTANCE_IP).trim() : 'localhost', + port: String(process.env.PORT).trim(), + os: { + type: os.type(), + platform: os.platform(), + arch: os.arch(), + release: os.release(), + uptime: os.uptime(), + loadavg: os.loadavg(), + totalmem: os.totalmem(), + freemem: os.freemem(), + cpus: os.cpus().length, + }, + nodeVersion: process.version, + }; + + InstanceStatus.registerInstance('rocket.chat', instance); + + return startStreamBroadcast(); +}); diff --git a/server/startup/presence.js b/server/startup/presence.js index 193dfc3bb1176..bcad81c80bb36 100644 --- a/server/startup/presence.js +++ b/server/startup/presence.js @@ -1,22 +1,10 @@ import { Meteor } from 'meteor/meteor'; -import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; import { UserPresence } from 'meteor/konecty:user-presence'; import InstanceStatusModel from '../../app/models/server/models/InstanceStatus'; import UsersSessionsModel from '../../app/models/server/models/UsersSessions'; Meteor.startup(function() { - const instance = { - host: 'localhost', - port: String(process.env.PORT).trim(), - }; - - if (process.env.INSTANCE_IP) { - instance.host = String(process.env.INSTANCE_IP).trim(); - } - - InstanceStatus.registerInstance('rocket.chat', instance); - UserPresence.start(); const startMonitor = typeof process.env.DISABLE_PRESENCE_MONITOR === 'undefined' diff --git a/server/stream/streamBroadcast.js b/server/stream/streamBroadcast.js index 27f45c7b1a361..9872597fac68a 100644 --- a/server/stream/streamBroadcast.js +++ b/server/stream/streamBroadcast.js @@ -97,6 +97,9 @@ function startMatrixBroadcast() { _dontPrintErrors: LoggerManager.logLevel < 2, }); + // remove not relevant info from instance record + delete record.extraInformation.os; + connections[instance].instanceRecord = record; connections[instance].instanceId = record._id; @@ -147,43 +150,6 @@ function startMatrixBroadcast() { InstanceStatusModel.find(query, options).fetch().forEach(matrixBroadCastActions.added); } -Meteor.methods({ - broadcastAuth(remoteId, selfId) { - check(selfId, String); - check(remoteId, String); - - const query = { - _id: remoteId, - }; - - if (selfId === InstanceStatus.id() && remoteId !== InstanceStatus.id() && InstanceStatus.getCollection().findOne(query)) { - this.connection.broadcastAuth = true; - } - - return this.connection.broadcastAuth === true; - }, - - stream(streamName, eventName, args) { - if (!this.connection) { - return 'self-not-authorized'; - } - - if (this.connection.broadcastAuth !== true) { - return 'not-authorized'; - } - - const instance = StreamerCentral.instances[streamName]; - if (!instance) { - return 'stream-not-exists'; - } - - if (instance.serverOnly) { - instance.__emit(eventName, ...args); - } else { - StreamerCentral.instances[streamName]._emit(eventName, args); - } - }, -}); function startStreamCastBroadcast(value) { const instance = 'StreamCast'; @@ -237,7 +203,7 @@ function startStreamCastBroadcast(value) { return connection.subscribe('stream'); } -function startStreamBroadcast() { +export function startStreamBroadcast() { if (!process.env.INSTANCE_IP) { process.env.INSTANCE_IP = 'localhost'; } @@ -314,24 +280,75 @@ function startStreamBroadcast() { }); } -export function getInstances() { - return Object.keys(connections).map((address) => { - const conn = connections[address]; - return Object.assign({ address, currentStatus: conn._stream.currentStatus }, _.pick(conn, 'instanceRecord', 'broadcastAuth')); - }); +function getConnection(address) { + const conn = connections[address]; + if (!conn) { + return; + } + + const { + instanceRecord, + broadcastAuth, + } = conn; + + return { + address, + currentStatus: conn._stream.currentStatus, + instanceRecord, + broadcastAuth, + }; } -Meteor.startup(function() { - return startStreamBroadcast(); -}); +export function getInstanceConnection(instance) { + const subPath = getURL('', { cdn: false, full: false }); + const address = `${ instance.extraInformation.host }:${ instance.extraInformation.port }${ subPath }`; + + return getConnection(address); +} Meteor.methods({ + broadcastAuth(remoteId, selfId) { + check(selfId, String); + check(remoteId, String); + + const query = { + _id: remoteId, + }; + + if (selfId === InstanceStatus.id() && remoteId !== InstanceStatus.id() && InstanceStatus.getCollection().findOne(query)) { + this.connection.broadcastAuth = true; + } + + return this.connection.broadcastAuth === true; + }, + + stream(streamName, eventName, args) { + if (!this.connection) { + return 'self-not-authorized'; + } + + if (this.connection.broadcastAuth !== true) { + return 'not-authorized'; + } + + const instance = StreamerCentral.instances[streamName]; + if (!instance) { + return 'stream-not-exists'; + } + + if (instance.serverOnly) { + instance.__emit(eventName, ...args); + } else { + StreamerCentral.instances[streamName]._emit(eventName, args); + } + }, + 'instances/get'() { if (!hasPermission(Meteor.userId(), 'view-statistics')) { throw new Meteor.Error('error-action-not-allowed', 'List instances is not allowed', { method: 'instances/get', }); } - return getInstances(); + return Object.keys(connections).map(getConnection); }, }); diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js index 553d089ef261e..e6bd9293b7a71 100644 --- a/tests/end-to-end/api/00-miscellaneous.js +++ b/tests/end-to-end/api/00-miscellaneous.js @@ -454,12 +454,21 @@ describe('miscellaneous', function() { .expect(200) .expect((res) => { expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('instances'); + expect(res.body).to.have.property('instances').and.to.be.an('array').with.lengthOf(1); - const { instances } = res.body; + const { instances: [instance] } = res.body; - expect(instances).to.have.property('current'); - expect(instances).to.have.property('connections').and.to.be.an('array'); + expect(instance).to.have.property('_id'); + expect(instance).to.have.property('extraInformation'); + expect(instance).to.have.property('name'); + expect(instance).to.have.property('pid'); + + const { extraInformation } = instance; + + expect(extraInformation).to.have.property('host'); + expect(extraInformation).to.have.property('port'); + expect(extraInformation).to.have.property('os').and.to.have.property('cpus').to.be.a('number'); + expect(extraInformation).to.have.property('nodeVersion'); }) .end(done); }); From 0cc2aafecd34bb5e73294514814ca2c6d23b713d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 28 Dec 2020 21:22:55 -0300 Subject: [PATCH 4/4] Remove unused underscore --- server/stream/streamBroadcast.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/stream/streamBroadcast.js b/server/stream/streamBroadcast.js index 9872597fac68a..3496519828217 100644 --- a/server/stream/streamBroadcast.js +++ b/server/stream/streamBroadcast.js @@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor'; import { UserPresence } from 'meteor/konecty:user-presence'; import { InstanceStatus } from 'meteor/konecty:multiple-instances-status'; import { check } from 'meteor/check'; -import _ from 'underscore'; import { DDP } from 'meteor/ddp'; import { DDPCommon } from 'meteor/ddp-common';