| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 | const postCatchError = Meteor.wrapAsync((url, options, resolve) => {  HTTP.post(url, options, (err, res) => {    if (err) {      resolve(null, err.response);    } else {      resolve(null, res);    }  });});const Lock = {  _lock: {},  _timer: {},  echoDelay: 500, // echo should be happening much faster  normalDelay: 1e3, // normally user typed comment will be much slower  ECHO: 2,  NORMAL: 1,  NULL: 0,  has(id, value) {    const existing = this._lock[id];    let ret = this.NULL;    if (existing) {      ret = existing === value ? this.ECHO : this.NORMAL;    }    return ret;  },  clear(id, delay) {    const previous = this._timer[id];    if (previous) {      Meteor.clearTimeout(previous);    }    this._timer[id] = Meteor.setTimeout(() => this.unset(id), delay);  },  set(id, value) {    const state = this.has(id, value);    let delay = this.normalDelay;    if (state === this.ECHO) {      delay = this.echoDelay;    }    if (!value) {      // user commented, we set a lock      value = 1;    }    this._lock[id] = value;    this.clear(id, delay); // always auto reset the locker after delay  },  unset(id) {    delete this._lock[id];  },};const webhooksAtbts = (process.env.WEBHOOKS_ATTRIBUTES &&  process.env.WEBHOOKS_ATTRIBUTES.split(',')) || [  'cardId',  'listId',  'oldListId',  'boardId',  'comment',  'user',  'card',  'commentId',  'swimlaneId',];const responseFunc = data => {  const paramCommentId = data.commentId;  const paramCardId = data.cardId;  const paramBoardId = data.boardId;  const newComment = data.comment;  if (paramCardId && paramBoardId && newComment) {    // only process data with the cardid, boardid and comment text, TODO can expand other functions here to react on returned data    const comment = CardComments.findOne({      _id: paramCommentId,      cardId: paramCardId,      boardId: paramBoardId,    });    const board = Boards.findOne(paramBoardId);    const card = Cards.findOne(paramCardId);    if (board && card) {      if (comment) {        Lock.set(comment._id, newComment);        CardComments.direct.update(comment._id, {          $set: {            text: newComment,          },        });      }    } else {      const userId = data.userId;      if (userId) {        const inserted = CardComments.direct.insert({          text: newComment,          userId,          cardId,          boardId,        });        Lock.set(inserted._id, newComment);      }    }  }};Meteor.methods({  outgoingWebhooks(integration, description, params) {    check(integration, Object);    check(description, String);    check(params, Object);    this.unblock();    // label activity did not work yet, see wekan/models/activities.js    const quoteParams = _.clone(params);    const clonedParams = _.clone(params);    [      'card',      'list',      'oldList',      'board',      'oldBoard',      'comment',      'checklist',      'swimlane',      'oldSwimlane',      'label',      'attachment',    ].forEach(key => {      if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;    });    const userId = params.userId ? params.userId : integrations[0].userId;    const user = Users.findOne(userId);    const text = `${params.user} ${TAPi18n.__(      description,      quoteParams,      user.getLanguage(),    )}\n${params.url}`;    if (text.length === 0) return;    const value = {      text: `${text}`,    };    webhooksAtbts.forEach(key => {      if (params[key]) value[key] = params[key];    });    value.description = description;    //integrations.forEach(integration => {    const is2way = integration.type === Integrations.Const.TWOWAY;    const token = integration.token || '';    const headers = {      'Content-Type': 'application/json',    };    if (token) headers['X-Wekan-Token'] = token;    const options = {      headers,      data: is2way ? { description, ...clonedParams } : value,    };    const url = integration.url;    if (is2way) {      const cid = params.commentId;      const comment = params.comment;      const lockState = cid && Lock.has(cid, comment);      if (cid && lockState !== Lock.NULL) {        // it's a comment  and there is a previous lock        return;      } else if (cid) {        Lock.set(cid, comment); // set a lock here      }    }    const response = postCatchError(url, options);    if (      response &&      response.statusCode &&      response.statusCode >= 200 &&      response.statusCode < 300    ) {      if (is2way) {        const data = response.data; // only an JSON encoded response will be actioned        if (data) {          try {            responseFunc(data);          } catch (e) {            throw new Meteor.Error('error-process-data');          }        }      }      return response; // eslint-disable-line consistent-return    } else {      throw new Meteor.Error('error-invalid-webhook-response');    }    //});  },});
 |