converted rocketchat-integrations coffee to js (#6502)
* converted rocketchat-integrations coffee to js * fix * fix lint and suggestionspull/6542/head
parent
da0fe37298
commit
bc5878a42a
@ -1,294 +0,0 @@ |
||||
vm = Npm.require('vm') |
||||
moment = require('moment') |
||||
|
||||
compiledScripts = {} |
||||
|
||||
buildSandbox = (store = {}) -> |
||||
sandbox = |
||||
_: _ |
||||
s: s |
||||
console: console, |
||||
moment: moment, |
||||
Store: |
||||
set: (key, val) -> |
||||
return store[key] = val |
||||
get: (key) -> |
||||
return store[key] |
||||
HTTP: (method, url, options) -> |
||||
try |
||||
return {} = |
||||
result: HTTP.call method, url, options |
||||
catch e |
||||
return {} = |
||||
error: e |
||||
|
||||
Object.keys(RocketChat.models).filter((k) -> |
||||
return !k.startsWith('_') |
||||
).forEach (k) => |
||||
sandbox[k] = RocketChat.models[k] |
||||
|
||||
return {} = |
||||
store: store, |
||||
sandbox: sandbox |
||||
|
||||
getIntegrationScript = (integration) -> |
||||
compiledScript = compiledScripts[integration._id] |
||||
if compiledScript? and +compiledScript._updatedAt is +integration._updatedAt |
||||
return compiledScript.script |
||||
|
||||
script = integration.scriptCompiled |
||||
vmScript = undefined |
||||
sandboxItems = buildSandbox() |
||||
|
||||
try |
||||
logger.incoming.info 'Will evaluate script of Trigger', integration.name |
||||
logger.incoming.debug script |
||||
|
||||
vmScript = vm.createScript script, 'script.js' |
||||
|
||||
vmScript.runInNewContext sandboxItems.sandbox |
||||
|
||||
if sandboxItems.sandbox.Script? |
||||
compiledScripts[integration._id] = |
||||
script: new sandboxItems.sandbox.Script() |
||||
store: sandboxItems.store |
||||
_updatedAt: integration._updatedAt |
||||
|
||||
return compiledScripts[integration._id].script |
||||
catch e |
||||
logger.incoming.error '[Error evaluating Script in Trigger', integration.name, ':]' |
||||
logger.incoming.error script.replace(/^/gm, ' ') |
||||
logger.incoming.error "[Stack:]" |
||||
logger.incoming.error e.stack.replace(/^/gm, ' ') |
||||
throw RocketChat.API.v1.failure 'error-evaluating-script' |
||||
|
||||
if not sandboxItems.sandbox.Script? |
||||
logger.incoming.error '[Class "Script" not in Trigger', integration.name, ']' |
||||
throw RocketChat.API.v1.failure 'class-script-not-found' |
||||
|
||||
|
||||
Api = new Restivus |
||||
enableCors: true |
||||
apiPath: 'hooks/' |
||||
auth: |
||||
user: -> |
||||
payloadKeys = Object.keys @bodyParams |
||||
payloadIsWrapped = @bodyParams?.payload? and payloadKeys.length == 1 |
||||
|
||||
if payloadIsWrapped and @request.headers['content-type'] is 'application/x-www-form-urlencoded' |
||||
try |
||||
@bodyParams = JSON.parse @bodyParams.payload |
||||
catch e |
||||
return { |
||||
error: { |
||||
statusCode: 400 |
||||
body: { |
||||
success: false |
||||
error: e.message |
||||
} |
||||
} |
||||
} |
||||
|
||||
@integration = RocketChat.models.Integrations.findOne |
||||
_id: @request.params.integrationId |
||||
token: decodeURIComponent @request.params.token |
||||
|
||||
if not @integration? |
||||
logger.incoming.info "Invalid integration id", @request.params.integrationId, "or token", @request.params.token |
||||
return |
||||
|
||||
user = RocketChat.models.Users.findOne |
||||
_id: @integration.userId |
||||
|
||||
return user: user |
||||
|
||||
|
||||
createIntegration = (options, user) -> |
||||
logger.incoming.info 'Add integration', options.name |
||||
logger.incoming.debug options |
||||
|
||||
Meteor.runAsUser user._id, => |
||||
switch options['event'] |
||||
when 'newMessageOnChannel' |
||||
options.data ?= {} |
||||
|
||||
if options.data.channel_name? and options.data.channel_name.indexOf('#') is -1 |
||||
options.data.channel_name = '#' + options.data.channel_name |
||||
|
||||
Meteor.call 'addOutgoingIntegration', |
||||
username: 'rocket.cat' |
||||
urls: [options.target_url] |
||||
name: options.name |
||||
channel: options.data.channel_name |
||||
triggerWords: options.data.trigger_words |
||||
|
||||
when 'newMessageToUser' |
||||
if options.data.username.indexOf('@') is -1 |
||||
options.data.username = '@' + options.data.username |
||||
|
||||
Meteor.call 'addOutgoingIntegration', |
||||
username: 'rocket.cat' |
||||
urls: [options.target_url] |
||||
name: options.name |
||||
channel: options.data.username |
||||
triggerWords: options.data.trigger_words |
||||
|
||||
return RocketChat.API.v1.success() |
||||
|
||||
|
||||
removeIntegration = (options, user) -> |
||||
logger.incoming.info 'Remove integration' |
||||
logger.incoming.debug options |
||||
|
||||
integrationToRemove = RocketChat.models.Integrations.findOne urls: options.target_url |
||||
Meteor.runAsUser user._id, => |
||||
Meteor.call 'deleteOutgoingIntegration', integrationToRemove._id |
||||
|
||||
return RocketChat.API.v1.success() |
||||
|
||||
|
||||
executeIntegrationRest = -> |
||||
logger.incoming.info 'Post integration:', @integration.name |
||||
logger.incoming.debug '@urlParams:', @urlParams |
||||
logger.incoming.debug '@bodyParams:', @bodyParams |
||||
|
||||
if @integration.enabled isnt true |
||||
return {} = |
||||
statusCode: 503 |
||||
body: 'Service Unavailable' |
||||
|
||||
defaultValues = |
||||
channel: @integration.channel |
||||
alias: @integration.alias |
||||
avatar: @integration.avatar |
||||
emoji: @integration.emoji |
||||
|
||||
if @integration.scriptEnabled is true and @integration.scriptCompiled? and @integration.scriptCompiled.trim() isnt '' |
||||
script = undefined |
||||
try |
||||
script = getIntegrationScript(@integration) |
||||
catch e |
||||
logger.incoming.warn e |
||||
return RocketChat.API.v1.failure e.message |
||||
|
||||
request = |
||||
url: |
||||
hash: @request._parsedUrl.hash |
||||
search: @request._parsedUrl.search |
||||
query: @queryParams |
||||
pathname: @request._parsedUrl.pathname |
||||
path: @request._parsedUrl.path |
||||
url_raw: @request.url |
||||
url_params: @urlParams |
||||
content: @bodyParams |
||||
content_raw: @request._readableState?.buffer?.toString() |
||||
headers: @request.headers |
||||
user: |
||||
_id: @user._id |
||||
name: @user.name |
||||
username: @user.username |
||||
|
||||
try |
||||
sandboxItems = buildSandbox(compiledScripts[@integration._id].store) |
||||
sandbox = sandboxItems.sandbox |
||||
sandbox.script = script |
||||
sandbox.request = request |
||||
|
||||
result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, { timeout: 3000 }) |
||||
|
||||
if result?.error? |
||||
return RocketChat.API.v1.failure result.error |
||||
|
||||
@bodyParams = result?.content |
||||
|
||||
logger.incoming.debug '[Process Incoming Request result of Trigger', @integration.name, ':]' |
||||
logger.incoming.debug 'result', @bodyParams |
||||
catch e |
||||
logger.incoming.error '[Error running Script in Trigger', @integration.name, ':]' |
||||
logger.incoming.error @integration.scriptCompiled.replace(/^/gm, ' ') |
||||
logger.incoming.error "[Stack:]" |
||||
logger.incoming.error e.stack.replace(/^/gm, ' ') |
||||
return RocketChat.API.v1.failure 'error-running-script' |
||||
|
||||
if not @bodyParams? |
||||
return RocketChat.API.v1.failure 'body-empty' |
||||
|
||||
@bodyParams.bot = |
||||
i: @integration._id |
||||
|
||||
try |
||||
message = processWebhookMessage @bodyParams, @user, defaultValues |
||||
|
||||
if _.isEmpty message |
||||
return RocketChat.API.v1.failure 'unknown-error' |
||||
|
||||
return RocketChat.API.v1.success() |
||||
catch e |
||||
return RocketChat.API.v1.failure e.error |
||||
|
||||
|
||||
addIntegrationRest = -> |
||||
return createIntegration @bodyParams, @user |
||||
|
||||
|
||||
removeIntegrationRest = -> |
||||
return removeIntegration @bodyParams, @user |
||||
|
||||
|
||||
integrationSampleRest = -> |
||||
logger.incoming.info 'Sample Integration' |
||||
|
||||
return {} = |
||||
statusCode: 200 |
||||
body: [ |
||||
token: Random.id(24) |
||||
channel_id: Random.id() |
||||
channel_name: 'general' |
||||
timestamp: new Date |
||||
user_id: Random.id() |
||||
user_name: 'rocket.cat' |
||||
text: 'Sample text 1' |
||||
trigger_word: 'Sample' |
||||
, |
||||
token: Random.id(24) |
||||
channel_id: Random.id() |
||||
channel_name: 'general' |
||||
timestamp: new Date |
||||
user_id: Random.id() |
||||
user_name: 'rocket.cat' |
||||
text: 'Sample text 2' |
||||
trigger_word: 'Sample' |
||||
, |
||||
token: Random.id(24) |
||||
channel_id: Random.id() |
||||
channel_name: 'general' |
||||
timestamp: new Date |
||||
user_id: Random.id() |
||||
user_name: 'rocket.cat' |
||||
text: 'Sample text 3' |
||||
trigger_word: 'Sample' |
||||
] |
||||
|
||||
|
||||
integrationInfoRest = -> |
||||
logger.incoming.info 'Info integration' |
||||
|
||||
return {} = |
||||
statusCode: 200 |
||||
body: |
||||
success: true |
||||
|
||||
Api.addRoute ':integrationId/:userId/:token', authRequired: true, {post: executeIntegrationRest, get: executeIntegrationRest} |
||||
Api.addRoute ':integrationId/:token', authRequired: true, {post: executeIntegrationRest, get: executeIntegrationRest} |
||||
|
||||
Api.addRoute 'sample/:integrationId/:userId/:token', authRequired: true, {get: integrationSampleRest} |
||||
Api.addRoute 'sample/:integrationId/:token', authRequired: true, {get: integrationSampleRest} |
||||
|
||||
Api.addRoute 'info/:integrationId/:userId/:token', authRequired: true, {get: integrationInfoRest} |
||||
Api.addRoute 'info/:integrationId/:token', authRequired: true, {get: integrationInfoRest} |
||||
|
||||
Api.addRoute 'add/:integrationId/:userId/:token', authRequired: true, {post: addIntegrationRest} |
||||
Api.addRoute 'add/:integrationId/:token', authRequired: true, {post: addIntegrationRest} |
||||
|
||||
Api.addRoute 'remove/:integrationId/:userId/:token', authRequired: true, {post: removeIntegrationRest} |
||||
Api.addRoute 'remove/:integrationId/:token', authRequired: true, {post: removeIntegrationRest} |
||||
@ -0,0 +1,332 @@ |
||||
/* globals Api Meteor Restivus logger processWebhookMessage*/ |
||||
// TODO: remove globals
|
||||
import vm from 'vm'; |
||||
import moment from 'moment'; |
||||
|
||||
const compiledScripts = {}; |
||||
function buildSandbox(store = {}) { |
||||
const sandbox = { |
||||
_, |
||||
s, |
||||
console, |
||||
moment, |
||||
Store: { |
||||
set(key, val) { |
||||
return store[key] = val; |
||||
}, |
||||
get(key) { |
||||
return store[key]; |
||||
} |
||||
}, |
||||
HTTP(method, url, options) { |
||||
try { |
||||
return { |
||||
result: HTTP.call(method, url, options) |
||||
}; |
||||
} catch (error) { |
||||
return { |
||||
error |
||||
}; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
Object.keys(RocketChat.models).filter((k) => !k.startsWith('_')).forEach((k) => sandbox[k] = RocketChat.models[k]); |
||||
return { store, sandbox }; |
||||
} |
||||
|
||||
function getIntegrationScript(integration) { |
||||
const compiledScript = compiledScripts[integration._id]; |
||||
if ((compiledScript != null) && +compiledScript._updatedAt === +integration._updatedAt) { |
||||
return compiledScript.script; |
||||
} |
||||
const script = integration.scriptCompiled; |
||||
const {sandbox, store} = buildSandbox(); |
||||
try { |
||||
logger.incoming.info('Will evaluate script of Trigger', integration.name); |
||||
logger.incoming.debug(script); |
||||
const vmScript = vm.createScript(script, 'script.js'); |
||||
vmScript.runInNewContext(sandbox); |
||||
if (sandbox.Script != null) { |
||||
compiledScripts[integration._id] = { |
||||
script: new sandbox.Script(), |
||||
store, |
||||
_updatedAt: integration._updatedAt |
||||
}; |
||||
return compiledScripts[integration._id].script; |
||||
} |
||||
} catch ({stack}) { |
||||
logger.incoming.error('[Error evaluating Script in Trigger', integration.name, ':]'); |
||||
logger.incoming.error(script.replace(/^/gm, ' ')); |
||||
logger.incoming.error('[Stack:]'); |
||||
logger.incoming.error(stack.replace(/^/gm, ' ')); |
||||
throw RocketChat.API.v1.failure('error-evaluating-script'); |
||||
} |
||||
if (sandbox.Script == null) { |
||||
logger.incoming.error('[Class "Script" not in Trigger', integration.name, ']'); |
||||
throw RocketChat.API.v1.failure('class-script-not-found'); |
||||
} |
||||
} |
||||
|
||||
Api = new Restivus({ |
||||
enableCors: true, |
||||
apiPath: 'hooks/', |
||||
auth: { |
||||
user() { |
||||
const payloadKeys = Object.keys(this.bodyParams); |
||||
const payloadIsWrapped = (this.bodyParams && this.bodyParams.payload) && payloadKeys.length === 1; |
||||
if (payloadIsWrapped && this.request.headers['content-type'] === 'application/x-www-form-urlencoded') { |
||||
try { |
||||
this.bodyParams = JSON.parse(this.bodyParams.payload); |
||||
} catch ({message}) { |
||||
return { |
||||
error: { |
||||
statusCode: 400, |
||||
body: { |
||||
success: false, |
||||
error: message |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
this.integration = RocketChat.models.Integrations.findOne({ |
||||
_id: this.request.params.integrationId, |
||||
token: decodeURIComponent(this.request.params.token) |
||||
}); |
||||
if (this.integration == null) { |
||||
logger.incoming.info('Invalid integration id', this.request.params.integrationId, 'or token', this.request.params.token); |
||||
return; |
||||
} |
||||
const user = RocketChat.models.Users.findOne({ |
||||
_id: this.integration.userId |
||||
}); |
||||
return {user}; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
function createIntegration(options, user) { |
||||
logger.incoming.info('Add integration', options.name); |
||||
logger.incoming.debug(options); |
||||
Meteor.runAsUser(user._id, function() { |
||||
switch (options['event']) { |
||||
case 'newMessageOnChannel': |
||||
if (options.data == null) { |
||||
options.data = {}; |
||||
} |
||||
if ((options.data.channel_name != null) && options.data.channel_name.indexOf('#') === -1) { |
||||
options.data.channel_name = `#${ options.data.channel_name }`; |
||||
} |
||||
return Meteor.call('addOutgoingIntegration', { |
||||
username: 'rocket.cat', |
||||
urls: [options.target_url], |
||||
name: options.name, |
||||
channel: options.data.channel_name, |
||||
triggerWords: options.data.trigger_words |
||||
}); |
||||
case 'newMessageToUser': |
||||
if (options.data.username.indexOf('@') === -1) { |
||||
options.data.username = `@${ options.data.username }`; |
||||
} |
||||
return Meteor.call('addOutgoingIntegration', { |
||||
username: 'rocket.cat', |
||||
urls: [options.target_url], |
||||
name: options.name, |
||||
channel: options.data.username, |
||||
triggerWords: options.data.trigger_words |
||||
}); |
||||
} |
||||
}); |
||||
return RocketChat.API.v1.success(); |
||||
} |
||||
|
||||
function removeIntegration(options, user) { |
||||
logger.incoming.info('Remove integration'); |
||||
logger.incoming.debug(options); |
||||
const integrationToRemove = RocketChat.models.Integrations.findOne({ |
||||
urls: options.target_url |
||||
}); |
||||
Meteor.runAsUser(user._id, () => { |
||||
return Meteor.call('deleteOutgoingIntegration', integrationToRemove._id); |
||||
}); |
||||
return RocketChat.API.v1.success(); |
||||
} |
||||
|
||||
function executeIntegrationRest() { |
||||
logger.incoming.info('Post integration:', this.integration.name); |
||||
logger.incoming.debug('@urlParams:', this.urlParams); |
||||
logger.incoming.debug('@bodyParams:', this.bodyParams); |
||||
if (this.integration.enabled !== true) { |
||||
return { |
||||
statusCode: 503, |
||||
body: 'Service Unavailable' |
||||
}; |
||||
} |
||||
const defaultValues = { |
||||
channel: this.integration.channel, |
||||
alias: this.integration.alias, |
||||
avatar: this.integration.avatar, |
||||
emoji: this.integration.emoji |
||||
}; |
||||
if (this.integration.scriptEnabled === true && this.integration.scriptCompiled && this.integration.scriptCompiled.trim() !== '') { |
||||
let script; |
||||
try { |
||||
script = getIntegrationScript(this.integration); |
||||
} catch (e) { |
||||
logger.incoming.warn(e); |
||||
return RocketChat.API.v1.failure(e.message); |
||||
} |
||||
const request = { |
||||
url: { |
||||
hash: this.request._parsedUrl.hash, |
||||
search: this.request._parsedUrl.search, |
||||
query: this.queryParams, |
||||
pathname: this.request._parsedUrl.pathname, |
||||
path: this.request._parsedUrl.path |
||||
}, |
||||
url_raw: this.request.url, |
||||
url_params: this.urlParams, |
||||
content: this.bodyParams, |
||||
content_raw: this.request._readableState && this.request._readableState.buffer && this.request._readableState.buffer.toString(), |
||||
headers: this.request.headers, |
||||
user: { |
||||
_id: this.user._id, |
||||
name: this.user.name, |
||||
username: this.user.username |
||||
} |
||||
}; |
||||
try { |
||||
const {sandbox} = buildSandbox(compiledScripts[this.integration._id].store); |
||||
sandbox.script = script; |
||||
sandbox.request = request; |
||||
const result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, { |
||||
timeout: 3000 |
||||
}); |
||||
if (result && result.error) { |
||||
return RocketChat.API.v1.failure(result.error); |
||||
} |
||||
this.bodyParams = result && result.content; |
||||
logger.incoming.debug('[Process Incoming Request result of Trigger', this.integration.name, ':]'); |
||||
logger.incoming.debug('result', this.bodyParams); |
||||
} catch ({stack}) { |
||||
logger.incoming.error('[Error running Script in Trigger', this.integration.name, ':]'); |
||||
logger.incoming.error(this.integration.scriptCompiled.replace(/^/gm, ' ')); |
||||
logger.incoming.error('[Stack:]'); |
||||
logger.incoming.error(stack.replace(/^/gm, ' ')); |
||||
return RocketChat.API.v1.failure('error-running-script'); |
||||
} |
||||
} |
||||
if (this.bodyParams == null) { |
||||
return RocketChat.API.v1.failure('body-empty'); |
||||
} |
||||
this.bodyParams.bot = { |
||||
i: this.integration._id |
||||
}; |
||||
try { |
||||
const message = processWebhookMessage(this.bodyParams, this.user, defaultValues); |
||||
if (_.isEmpty(message)) { |
||||
return RocketChat.API.v1.failure('unknown-error'); |
||||
} |
||||
return RocketChat.API.v1.success(); |
||||
} catch ({error}) { |
||||
return RocketChat.API.v1.failure(error); |
||||
} |
||||
} |
||||
|
||||
function addIntegrationRest() { |
||||
return createIntegration(this.bodyParams, this.user); |
||||
} |
||||
|
||||
function removeIntegrationRest() { |
||||
return removeIntegration(this.bodyParams, this.user); |
||||
} |
||||
|
||||
function integrationSampleRest() { |
||||
logger.incoming.info('Sample Integration'); |
||||
return { |
||||
statusCode: 200, |
||||
body: [ |
||||
{ |
||||
token: Random.id(24), |
||||
channel_id: Random.id(), |
||||
channel_name: 'general', |
||||
timestamp: new Date, |
||||
user_id: Random.id(), |
||||
user_name: 'rocket.cat', |
||||
text: 'Sample text 1', |
||||
trigger_word: 'Sample' |
||||
}, { |
||||
token: Random.id(24), |
||||
channel_id: Random.id(), |
||||
channel_name: 'general', |
||||
timestamp: new Date, |
||||
user_id: Random.id(), |
||||
user_name: 'rocket.cat', |
||||
text: 'Sample text 2', |
||||
trigger_word: 'Sample' |
||||
}, { |
||||
token: Random.id(24), |
||||
channel_id: Random.id(), |
||||
channel_name: 'general', |
||||
timestamp: new Date, |
||||
user_id: Random.id(), |
||||
user_name: 'rocket.cat', |
||||
text: 'Sample text 3', |
||||
trigger_word: 'Sample' |
||||
} |
||||
] |
||||
}; |
||||
} |
||||
|
||||
function integrationInfoRest() { |
||||
logger.incoming.info('Info integration'); |
||||
return { |
||||
statusCode: 200, |
||||
body: { |
||||
success: true |
||||
} |
||||
}; |
||||
} |
||||
|
||||
Api.addRoute(':integrationId/:userId/:token', { authRequired: true }, { |
||||
post: executeIntegrationRest, |
||||
get: executeIntegrationRest |
||||
}); |
||||
|
||||
Api.addRoute(':integrationId/:token', { authRequired: true }, { |
||||
post: executeIntegrationRest, |
||||
get: executeIntegrationRest |
||||
}); |
||||
|
||||
Api.addRoute('sample/:integrationId/:userId/:token', { authRequired: true }, { |
||||
get: integrationSampleRest |
||||
}); |
||||
|
||||
Api.addRoute('sample/:integrationId/:token', { authRequired: true }, { |
||||
get: integrationSampleRest |
||||
}); |
||||
|
||||
Api.addRoute('info/:integrationId/:userId/:token', { authRequired: true }, { |
||||
get: integrationInfoRest |
||||
}); |
||||
|
||||
Api.addRoute('info/:integrationId/:token', { authRequired: true }, { |
||||
get: integrationInfoRest |
||||
}); |
||||
|
||||
Api.addRoute('add/:integrationId/:userId/:token', { authRequired: true }, { |
||||
post: addIntegrationRest |
||||
}); |
||||
|
||||
Api.addRoute('add/:integrationId/:token', { authRequired: true }, { |
||||
post: addIntegrationRest |
||||
}); |
||||
|
||||
Api.addRoute('remove/:integrationId/:userId/:token', { authRequired: true }, { |
||||
post: removeIntegrationRest |
||||
}); |
||||
|
||||
Api.addRoute('remove/:integrationId/:token', { authRequired: true }, { |
||||
post: removeIntegrationRest |
||||
}); |
||||
Loading…
Reference in new issue