diff --git a/packages/rocketchat-api/server/api.js b/packages/rocketchat-api/server/api.js index c3157e44fde..b6153d7a50c 100644 --- a/packages/rocketchat-api/server/api.js +++ b/packages/rocketchat-api/server/api.js @@ -58,45 +58,52 @@ class API extends Restivus { }; } - addRoute(route, options, endpoints) { + addRoute(routes, options, endpoints) { //Note: required if the developer didn't provide options if (typeof endpoints === 'undefined') { endpoints = options; options = {}; } - //Note: This is required due to Restivus calling `addRoute` in the constructor of itself - if (this.helperMethods) { - Object.keys(endpoints).forEach((method) => { - if (typeof endpoints[method] === 'function') { - endpoints[method] = { action: endpoints[method] }; - } + //Allow for more than one route using the same option and endpoints + if (!_.isArray(routes)) { + routes = [routes]; + } - //Add a try/catch for each much - const originalAction = endpoints[method].action; - endpoints[method].action = function() { - this.logger.debug(`${this.request.method.toUpperCase()}: ${this.request.url}`); - let result; - try { - result = originalAction.apply(this); - } catch (e) { - this.logger.debug(`${method} ${route} threw an error:`, e); - return RocketChat.API.v1.failure(e.message, e.error); + routes.forEach((route) => { + //Note: This is required due to Restivus calling `addRoute` in the constructor of itself + if (this.helperMethods) { + Object.keys(endpoints).forEach((method) => { + if (typeof endpoints[method] === 'function') { + endpoints[method] = { action: endpoints[method] }; } - return result ? result : RocketChat.API.v1.success(); - }; - - for (const [name, helperMethod] of this.helperMethods) { - endpoints[method][name] = helperMethod; - } + //Add a try/catch for each much + const originalAction = endpoints[method].action; + endpoints[method].action = function() { + this.logger.debug(`${this.request.method.toUpperCase()}: ${this.request.url}`); + let result; + try { + result = originalAction.apply(this); + } catch (e) { + this.logger.debug(`${method} ${route} threw an error:`, e); + return RocketChat.API.v1.failure(e.message, e.error); + } + + return result ? result : RocketChat.API.v1.success(); + }; + + for (const [name, helperMethod] of this.helperMethods) { + endpoints[method][name] = helperMethod; + } - //Allow the endpoints to make usage of the logger which respects the user's settings - endpoints[method].logger = this.logger; - }); - } + //Allow the endpoints to make usage of the logger which respects the user's settings + endpoints[method].logger = this.logger; + }); + } - super.addRoute(route, options, endpoints); + super.addRoute(route, options, endpoints); + }); } } diff --git a/packages/rocketchat-api/server/settings.js b/packages/rocketchat-api/server/settings.js index 238f4d0d11d..449a2463d2c 100644 --- a/packages/rocketchat-api/server/settings.js +++ b/packages/rocketchat-api/server/settings.js @@ -3,5 +3,6 @@ RocketChat.settings.addGroup('General', function() { this.add('API_Upper_Count_Limit', 100, { type: 'int', public: false }); this.add('API_Default_Count', 50, { type: 'int', public: false }); this.add('API_Allow_Infinite_Count', true, { type: 'boolean', public: false }); + this.add('API_Enable_Direct_Message_History_EndPoint', false, { type: 'boolean', public: false }); }); }); diff --git a/packages/rocketchat-api/server/v1/im.js b/packages/rocketchat-api/server/v1/im.js index 78f2762cab6..f184d6d8e18 100644 --- a/packages/rocketchat-api/server/v1/im.js +++ b/packages/rocketchat-api/server/v1/im.js @@ -12,7 +12,7 @@ function findDirectMessageRoomById(roomId, userId) { return roomSub; } -RocketChat.API.v1.addRoute('im.close', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.close', 'im.close'], { authRequired: true }, { post: function() { const findResult = findDirectMessageRoomById(this.bodyParams.roomId, this.userId); @@ -33,7 +33,7 @@ RocketChat.API.v1.addRoute('im.close', { authRequired: true }, { } }); -RocketChat.API.v1.addRoute('im.history', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.history', 'im.history'], { authRequired: true }, { get: function() { const findResult = findDirectMessageRoomById(this.queryParams.roomId, this.userId); @@ -78,8 +78,47 @@ RocketChat.API.v1.addRoute('im.history', { authRequired: true }, { } }); +RocketChat.API.v1.addRoute(['dm.messages.others', 'im.messages.others'], { authRequired: true }, { + get: function() { + if (RocketChat.settings.get('API_Enable_Direct_Message_History_EndPoint') !== true) { + throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/im.messages.others' }); + } + + if (!RocketChat.authz.hasPermission(this.userId, 'view-room-administration')) { + return RocketChat.API.v1.unauthorized(); + } + + const roomId = this.queryParams.roomId; + if (!roomId || !roomId.trim()) { + throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" is required'); + } + + const room = RocketChat.models.Rooms.findOneById(roomId); + if (!room || room.t !== 'd') { + throw new Meteor.Error('error-room-not-found', `No direct message room found by the id of: ${roomId}`); + } + + const { offset, count } = this.getPaginationItems(); + const { sort, fields, query } = this.parseJsonQuery(); + const ourQuery = Object.assign({}, query, { rid: room._id }); + + const msgs = RocketChat.models.Messages.find(ourQuery, { + sort: sort ? sort : { ts: -1 }, + skip: offset, + limit: count, + fields: Object.assign({}, fields, RocketChat.API.v1.defaultFieldsToExclude) + }).fetch(); + + return RocketChat.API.v1.success({ + messages: msgs, + offset, + count: msgs.length, + total: RocketChat.models.Messages.find(ourQuery).count() + }); + } +}); -RocketChat.API.v1.addRoute('im.list', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.list', 'im.list'], { authRequired: true }, { get: function() { const { offset, count } = this.getPaginationItems(); const { sort, fields } = this.parseJsonQuery(); @@ -102,7 +141,7 @@ RocketChat.API.v1.addRoute('im.list', { authRequired: true }, { } }); -RocketChat.API.v1.addRoute('im.list.everyone', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.list.everyone', 'im.list.everyone'], { authRequired: true }, { get: function() { if (!RocketChat.authz.hasPermission(this.userId, 'view-room-administration')) { return RocketChat.API.v1.unauthorized(); @@ -129,7 +168,7 @@ RocketChat.API.v1.addRoute('im.list.everyone', { authRequired: true }, { } }); -RocketChat.API.v1.addRoute('im.open', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.open', 'im.open'], { authRequired: true }, { post: function() { const findResult = findDirectMessageRoomById(this.bodyParams.roomId, this.userId); @@ -150,7 +189,7 @@ RocketChat.API.v1.addRoute('im.open', { authRequired: true }, { } }); -RocketChat.API.v1.addRoute('im.setTopic', { authRequired: true }, { +RocketChat.API.v1.addRoute(['dm.setTopic', 'im.setTopic'], { authRequired: true }, { post: function() { if (!this.bodyParams.topic || !this.bodyParams.topic.trim()) { return RocketChat.API.v1.failure('The bodyParam "topic" is required'); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index d294a2b5829..441f4003a85 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -170,6 +170,8 @@ "API_EmbedIgnoredHosts_Description": "Comma-separated list of hosts or CIDR addresses, eg. localhost, 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16", "API_EmbedSafePorts": "Safe Ports", "API_EmbedSafePorts_Description": "Comma-separated list of ports allowed for previewing.", + "API_Enable_Direct_Message_History_EndPoint": "Enable Direct Message History Endpoint", + "API_Enable_Direct_Message_History_EndPoint_Description": "This enables the `/api/v1/im.history.others` which allows the viewing of direct messages sent by other users that the caller is not part of.", "API_GitHub_Enterprise_URL": "Server URL", "API_GitHub_Enterprise_URL_Description": "Example: http://domain.com (excluding trailing slash)", "API_Gitlab_URL": "GitLab URL",