turnaround issue brevo emailing
This commit is contained in:
		@@ -3,6 +3,7 @@ const path = require("path");
 | 
			
		||||
const fs = require("fs-extra");
 | 
			
		||||
const axios = require("axios");
 | 
			
		||||
const dayjs = require("dayjs");
 | 
			
		||||
const Mustache = require('mustache');
 | 
			
		||||
const Checkjson = require(`./Checkjson.js`);
 | 
			
		||||
//const smtp = require("smtp-client");
 | 
			
		||||
const nodemailer = require("nodemailer");
 | 
			
		||||
@@ -212,18 +213,27 @@ Notifications.manageemail = (data, template, tribe) => {
 | 
			
		||||
   *  data optionaly can contain Cc,Bcc as array of emails and attachments as array of filename (into the server)
 | 
			
		||||
   *  Await the 1st email
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  //console.log(data)
 | 
			
		||||
  if (!data.emailsto || data.emailsto.length == 0) {
 | 
			
		||||
    return {
 | 
			
		||||
      status: 406,
 | 
			
		||||
      ref: "Notifications",
 | 
			
		||||
      msg: "emailsmissing",
 | 
			
		||||
      msg: "emailstomissing",
 | 
			
		||||
      data: data,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
  if (typeof data.emailsto === "string") data.emailsto = [data.emailsto];
 | 
			
		||||
 
 | 
			
		||||
  if (!fs.existsSync(path.resolve(template))){
 | 
			
		||||
    return {
 | 
			
		||||
      status: 404,
 | 
			
		||||
      ref: "Notification",
 | 
			
		||||
      msg: "templatenotfound",
 | 
			
		||||
      data: { template:path.resolve(template) },
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const tplemail = require(path.resolve(template));
 | 
			
		||||
  let sendit;
 | 
			
		||||
  let sendit={status:200,ref:"Notifications",msg:"successfullsend"};
 | 
			
		||||
  data.emailsto.forEach(async (e, i) => {
 | 
			
		||||
    if (Checkjson.testformat(e, "email")) {
 | 
			
		||||
      const dat = {};
 | 
			
		||||
@@ -233,16 +243,17 @@ Notifications.manageemail = (data, template, tribe) => {
 | 
			
		||||
      dat.text = Mustache.render(tplemail.text, data);
 | 
			
		||||
      dat.Cc = data.Cc ? tplemail.Cc.push(data.Cc) : tplemail.Cc;
 | 
			
		||||
      dat.Bcc = data.Bcc ? tplemail.Bcc.push(data.Bcc) : tplemail.Bcc;
 | 
			
		||||
      dat.attachments = tplemail.attachments.push(data.attachments);
 | 
			
		||||
      const sendit = Notifications.sendmail(data, tribe);
 | 
			
		||||
      if (data.attachments){
 | 
			
		||||
        data.attachments.forEach(a=>tplemail.attachments.push(a))
 | 
			
		||||
      }
 | 
			
		||||
      if (i == 0) {
 | 
			
		||||
        sendit = await Notifications.sendmail(data, tribe);
 | 
			
		||||
        if (sendit.status != 200) return sendit;
 | 
			
		||||
        sendit = await Notifications.sendmail(dat, tribe);
 | 
			
		||||
        if (sendit.status != 200) return {status:406,ref:"Notifications",msg:"emailnotsent"};
 | 
			
		||||
      } else {
 | 
			
		||||
        Notifications.sendmail(data, tribe);
 | 
			
		||||
        Notifications.sendmail(dat, tribe);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // not a well formated email
 | 
			
		||||
      // not a well formated email @todo add a log file to follow it
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return sendit;
 | 
			
		||||
@@ -250,6 +261,17 @@ Notifications.manageemail = (data, template, tribe) => {
 | 
			
		||||
 | 
			
		||||
Notifications.sendmail = async (data, tribe) => {
 | 
			
		||||
  /**
 | 
			
		||||
   *    * in conf global or in /itm/{tribe}.json must have valid parameter emailcontact must be authorized by the smtp
 | 
			
		||||
   *  "emailcontact": "noreply@smatchit.io",
 | 
			
		||||
   *  "smtp": {
 | 
			
		||||
   *    "host": "smtp-relay.brevo.com",
 | 
			
		||||
   *    "port": 587,
 | 
			
		||||
   *    "secure": false,
 | 
			
		||||
   *    "auth": {
 | 
			
		||||
   *     "user": "xx",
 | 
			
		||||
   *    "pass": "yy"
 | 
			
		||||
   *    }
 | 
			
		||||
   *   }
 | 
			
		||||
   * See https://nodemailer.com/message/ for available fields to add
 | 
			
		||||
   * @param {string} [data.from] an email authorized by smtp used priority from header xtribe
 | 
			
		||||
   * @param {string} data.to list of email separate by ,
 | 
			
		||||
@@ -265,7 +287,7 @@ Notifications.sendmail = async (data, tribe) => {
 | 
			
		||||
   *  {filename:'img.png',path:"data:text/svg;base64.aGVsbG8gd29ybGQ="}
 | 
			
		||||
   *
 | 
			
		||||
   * @example data
 | 
			
		||||
   * {"to":"wall-ants.ndda.fr",
 | 
			
		||||
   * {"to":"wall-ants@ndda.fr",
 | 
			
		||||
   *  "subject":"Test",
 | 
			
		||||
   *  "html":"<h1>test welcome</h1>",
 | 
			
		||||
   *  "text":"test welcome",
 | 
			
		||||
@@ -305,13 +327,18 @@ Notifications.sendmail = async (data, tribe) => {
 | 
			
		||||
  const conftribfile = `../../itm/${tribe}.json`;
 | 
			
		||||
  if (fs.existsSync(conftribfile)) {
 | 
			
		||||
    const conftrib = fs.readJSONSync(conftribfile);
 | 
			
		||||
    if (!conftrib.emailcontact){
 | 
			
		||||
      return {
 | 
			
		||||
        status: 428,
 | 
			
		||||
        ref: "Notifications",
 | 
			
		||||
        msg: "missingemailcontactinconf",
 | 
			
		||||
        data: { tribe },
 | 
			
		||||
      };  
 | 
			
		||||
    }
 | 
			
		||||
    confsmtp = conftrib.smtp;
 | 
			
		||||
    if (!data.from) data.from = conftrib.emailcontact;
 | 
			
		||||
    if (!data.from || data.from == conf.emailcontact) data.from = conftrib.emailcontact;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //const client = smtp.connect(confsmtp);
 | 
			
		||||
  const transporter = await nodemailer.createTransport(confsmtp);
 | 
			
		||||
  //@todo add attachments management
 | 
			
		||||
  if (data.filelist) {
 | 
			
		||||
    data.attachments = [];
 | 
			
		||||
    let missingfile = [];
 | 
			
		||||
@@ -331,6 +358,7 @@ Notifications.sendmail = async (data, tribe) => {
 | 
			
		||||
  }
 | 
			
		||||
  //console.log("data:", data);
 | 
			
		||||
  const res = await transporter.sendMail(data);
 | 
			
		||||
  //console.log(res)
 | 
			
		||||
  if (
 | 
			
		||||
    res.accepted &&
 | 
			
		||||
    data.to.split(",").reduce((acc, m) => acc && res.accepted.includes(m), true)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										221
									
								
								models/PagansPeter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								models/PagansPeter.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,221 @@
 | 
			
		||||
const glob = require("glob");
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const dayjs = require("dayjs");
 | 
			
		||||
const fs = require("fs-extra");
 | 
			
		||||
const axios = require("axios");
 | 
			
		||||
const Mustache = require("mustache");
 | 
			
		||||
const openpgp = require("openpgp");
 | 
			
		||||
const Notifications = require("./Notifications.js");
 | 
			
		||||
const Odmdb = require("./Odmdb.js");
 | 
			
		||||
 | 
			
		||||
const conf = require(`../../../conf.json`);
 | 
			
		||||
const currentmod = "Pagans";
 | 
			
		||||
const log = conf.api.activelog.includes(currentmod);
 | 
			
		||||
/**
 | 
			
		||||
 * Pagan Management numeric Identity and Person  (Person = Pagan Id + tribe)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
const Pagans = {};
 | 
			
		||||
 | 
			
		||||
Pagans.create=()=>{
 | 
			
		||||
//from router.post("/", checkHeaders, isAuthenticated, async (req, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Pagans.joinpersontotribe=()=>{
 | 
			
		||||
// from router.post("/", checkHeaders, isAuthenticated, async (req, res)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 *  Remove authentification token after a logout
 | 
			
		||||
 * @param {string} alias
 | 
			
		||||
 * @param {string} tribe
 | 
			
		||||
 * @param {integer} xdays
 | 
			
		||||
 * @param {string} xhash
 | 
			
		||||
 * @returns {status:200, ref:"Pagans",msg:"logout"}
 | 
			
		||||
 * tmpfs name file has to be on line with the tmpfs create by isAuthenticated
 | 
			
		||||
 *  tmpfs contain profils name for a tribe/
 | 
			
		||||
 */
 | 
			
		||||
Pagans.logout = (alias, tribe, xdays, xhash) => {
 | 
			
		||||
  //console.log(alias, tribe, xdays, xhash);
 | 
			
		||||
  // inline with middleware isAuthenticated.js
 | 
			
		||||
  let tmpfs = `../../tmp/tokens/${alias}_${tribe}_${xdays}`;
 | 
			
		||||
  //max filename in ext4: 255 characters
 | 
			
		||||
  tmpfs += `_${xhash.substring(150, 150 + tmpfs.length - 249)}.json`;
 | 
			
		||||
  fs.remove(tmpfs);
 | 
			
		||||
  if (log) console.log(currentmod, "logout token", tmpfs);
 | 
			
		||||
  return { status: 200, ref: "Pagans", msg: "logout" };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {string} alias a alias that exist or not
 | 
			
		||||
 * @return {object}  { status: 200, ref:"pagans",msg:"aliasexist",data: { alias, publicKey } }
 | 
			
		||||
 *                   { status: 404, ref:"pagans",msg:"aliasdoesnotexist",data: { alias} }
 | 
			
		||||
 *
 | 
			
		||||
 **/
 | 
			
		||||
Pagans.getalias = (alias) => {
 | 
			
		||||
  //bypass Odmdb cause all is public save ressources
 | 
			
		||||
  console.log(path.resolve(`../objects/pagans/itm/${alias}.json`));
 | 
			
		||||
  if (fs.existsSync(`../objects/pagans/itm/${alias}.json`)) {
 | 
			
		||||
    return {
 | 
			
		||||
      status: 200,
 | 
			
		||||
      ref: "Pagans",
 | 
			
		||||
      msg: "aliasexist",
 | 
			
		||||
      data: fs.readJSONSync(`../objects/pagans/itm/${alias}.json`),
 | 
			
		||||
    };
 | 
			
		||||
  } else {
 | 
			
		||||
    return {
 | 
			
		||||
      status: 404,
 | 
			
		||||
      ref: "Pagans",
 | 
			
		||||
      msg: "aliasdoesnotexist",
 | 
			
		||||
      data: { alias },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Send email with alias's keys to email or person alias person.recovery.email
 | 
			
		||||
 *
 | 
			
		||||
 * If email or pubkey is undefined then get data from tribe/person(alias)
 | 
			
		||||
 * Send email with keys
 | 
			
		||||
 * @param {object} data
 | 
			
		||||
 * @param {string} data.alias
 | 
			
		||||
 * @param {pgpPrivate} [data.privatekey]
 | 
			
		||||
 * @param {string} [data.passphrase]
 | 
			
		||||
 * @param {string} data.tribe
 | 
			
		||||
 * @param {pgpPublic} [data.publickey]
 | 
			
		||||
 * @param {string} [data.email]
 | 
			
		||||
 * @param {string} data.lg
 | 
			
		||||
 */
 | 
			
		||||
Pagans.sendmailkey = (data) => {
 | 
			
		||||
  if (log)
 | 
			
		||||
    console.log(
 | 
			
		||||
      currentmod,
 | 
			
		||||
      data.alias,
 | 
			
		||||
      "-",
 | 
			
		||||
      data.privatekey ? data.privatekey.substring(0, 10) : "",
 | 
			
		||||
      "-",
 | 
			
		||||
      data.tribe,
 | 
			
		||||
      "-",
 | 
			
		||||
      data.passphrase,
 | 
			
		||||
      "-",
 | 
			
		||||
      data.publickey ? data.publickey.substring(0, 10) : "",
 | 
			
		||||
      "-",
 | 
			
		||||
      data.email,
 | 
			
		||||
      "-",
 | 
			
		||||
      data.lg
 | 
			
		||||
    );
 | 
			
		||||
  const person = {
 | 
			
		||||
    alias: data.alias,
 | 
			
		||||
    privatekey: data.privatekey,
 | 
			
		||||
    tribe: data.tribe,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (!data.publickey || !data.email || !data.privatekey) {
 | 
			
		||||
    const personfile = `../../${data.tribe}/objects/persons/itm/${data.alias}.json`;
 | 
			
		||||
    if (!fs.existsSync(personfile)) {
 | 
			
		||||
      return {
 | 
			
		||||
        status: 404,
 | 
			
		||||
        ref: "Pagans",
 | 
			
		||||
        msg: "persondoesnotexist",
 | 
			
		||||
        data: { alias: data.alias, tribe: data.tribe },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    const persondata = fs.readJsonSync(personfile);
 | 
			
		||||
    if (!persondata.recoveryauth) {
 | 
			
		||||
      return {
 | 
			
		||||
        status: 404,
 | 
			
		||||
        ref: "Pagans",
 | 
			
		||||
        msg: "personhasnorecoveryauth",
 | 
			
		||||
        data: { alias: data.alias, tribe: data.tribe, email: data.email },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    person.email = persondata.recoveryauth.email;
 | 
			
		||||
    person.publickey = persondata.recoveryauth.publickey;
 | 
			
		||||
    person.privatekey = persondata.recoveryauth.privatekey;
 | 
			
		||||
    person.passphrase = persondata.recoveryauth.passphrase;
 | 
			
		||||
  } else {
 | 
			
		||||
    person.email = data.email;
 | 
			
		||||
    person.passphrase = data.passphrase;
 | 
			
		||||
    person.publickey = data.publickey;
 | 
			
		||||
  }
 | 
			
		||||
  person.avecpassphrase = person.passphrase != "";
 | 
			
		||||
  let tplfile = `../../${data.tribe}/template/createidentity_${data.lg}.js`;
 | 
			
		||||
  if (!fs.existsSync(tplfile)) {
 | 
			
		||||
    tplfile = `../template/createidentity_${data.lg}.js`;
 | 
			
		||||
    if (!fs.existsSync(tplfile)) {
 | 
			
		||||
      return {
 | 
			
		||||
        status: 406,
 | 
			
		||||
        ref: "Pagans",
 | 
			
		||||
        msg: "templatedoesnotexist",
 | 
			
		||||
        data: { tplfile },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const tplemail = require(path.resolve(tplfile));
 | 
			
		||||
  /*
 | 
			
		||||
  Remove from attachments for less user confusing
 | 
			
		||||
     {
 | 
			
		||||
        filename:`${person.alias}_publickey.txt`,
 | 
			
		||||
        content: person.publickey,
 | 
			
		||||
        contentType:"text/plain"
 | 
			
		||||
      },
 | 
			
		||||
  */
 | 
			
		||||
  const maildata = {
 | 
			
		||||
    from: tplemail.sender,
 | 
			
		||||
    to: person.email,
 | 
			
		||||
    subject: Mustache.render(tplemail.subject, person),
 | 
			
		||||
    html: Mustache.render(tplemail.html, person),
 | 
			
		||||
    text: Mustache.render(tplemail.text, person),
 | 
			
		||||
    attachments: [
 | 
			
		||||
      {
 | 
			
		||||
        filename: `${person.alias}_privatekey.txt`,
 | 
			
		||||
        content: person.privatekey,
 | 
			
		||||
        contentType: "text/plain",
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  };
 | 
			
		||||
  return Notifications.sendmail(maildata, data.tribe);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Pagans.authenticatedetachedSignature = async (
 | 
			
		||||
  alias,
 | 
			
		||||
  pubK,
 | 
			
		||||
  detachedSignature,
 | 
			
		||||
  message
 | 
			
		||||
) => {
 | 
			
		||||
  /**
 | 
			
		||||
   *  Check that a message was signed with a privateKey from a publicKey
 | 
			
		||||
   *  This is not necessary if isAuthenticated, but can be usefull to double check
 | 
			
		||||
   *  @TODO finish it and implement it also in  /apxpagan.js for browser
 | 
			
		||||
   *  @alias {string} alias link to the publicKey
 | 
			
		||||
   *  @pubK {string} publiKey text format
 | 
			
		||||
   *  @detachedSignature  {string} a detachedsignatured get from apx.detachedSignature
 | 
			
		||||
   *  @message {string} the message signed
 | 
			
		||||
   *  @return {boolean} true the message was signed by alias
 | 
			
		||||
   *                    false the message was not signed by alias
 | 
			
		||||
   */
 | 
			
		||||
  const publicKey = await openpgp.readKey({ armoredKey: pubK });
 | 
			
		||||
  const msg = await openpgp.createMessage({ text: message });
 | 
			
		||||
  const signature = await openpgp.readSignature({
 | 
			
		||||
    armoredSignature: detachedSignature, // parse detached signature
 | 
			
		||||
  });
 | 
			
		||||
  const verificationResult = await openpgp.verify({
 | 
			
		||||
    msg, // Message object
 | 
			
		||||
    signature,
 | 
			
		||||
    verificationKeys: publicKey,
 | 
			
		||||
  });
 | 
			
		||||
  const { verified, keyID } = verificationResult.signatures[0];
 | 
			
		||||
  try {
 | 
			
		||||
    await verified; // throws on invalid signature
 | 
			
		||||
    if (log) console.log(currentmod, "Signed by key id " + keyID.toHex());
 | 
			
		||||
    return KeyId.toHex().alias == alias;
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    if (log)
 | 
			
		||||
      console.log(currentmod, "Signature could not be verified: " + e.message);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = Pagans;
 | 
			
		||||
@@ -44,13 +44,24 @@ router.get("/messages/:alias/:tribeId", (req, res) => {
 | 
			
		||||
/**
 | 
			
		||||
 * @api {POST} adminapi/notifications/sendmail/:tribe/:template -Send personnalize emails
 | 
			
		||||
 * @apiName Sendmail
 | 
			
		||||
 * @apiDescription Send personnalize email with data from template store in ../../{tribe}/template/{template}.json
 | 
			
		||||
 * @apiDescription Send personnalize email with data from template store in ../../{tribe}/template/{template}.json and smtp in conf global or in /itm/{tribe}.json that must have valid parameter emailcontact must be authorized by the smtp
 | 
			
		||||
   *  "emailcontact": "noreply@smatchit.io",
 | 
			
		||||
   *  "smtp": {
 | 
			
		||||
   *    "host": "smtp-relay.brevo.com",
 | 
			
		||||
   *    "port": 587,
 | 
			
		||||
   *    "secure": false,
 | 
			
		||||
   *    "auth": {
 | 
			
		||||
   *     "user": "xx",
 | 
			
		||||
   *    "pass": "yy"
 | 
			
		||||
   *    }
 | 
			
		||||
   *   }
 | 
			
		||||
 | 
			
		||||
 * @apiGroup Notifications
 | 
			
		||||
 *
 | 
			
		||||
 * @apiParam {string} template
 | 
			
		||||
 * @apiParam {string} tribe
 | 
			
		||||
 * @apiBody {array} emails to send (array of valid email)
 | 
			
		||||
 * @apiBody {object} Data to personnalize template
 | 
			
		||||
 * @apiBody {object} data to personnalize template
 | 
			
		||||
 *
 | 
			
		||||
 * @apiSuccess {object}  notif content
 | 
			
		||||
 * @apiSuccessExample {json} Success-Response:
 | 
			
		||||
@@ -59,24 +70,19 @@ router.get("/messages/:alias/:tribeId", (req, res) => {
 | 
			
		||||
 * bouture
 | 
			
		||||
 */
 | 
			
		||||
router.post(
 | 
			
		||||
  "/sendmail/:tribe/:template/:await",
 | 
			
		||||
  "/sendmail/:tribe/:template",
 | 
			
		||||
  checkHeaders,
 | 
			
		||||
  isAuthenticated,
 | 
			
		||||
  (req, res) => {
 | 
			
		||||
    const pathtpl = `../../${req.params.tribe}/${req.params.template}.js`;
 | 
			
		||||
    if (fs.existsSync(pathtpl)) {
 | 
			
		||||
      const sendemail = Notification.manageemail(req.body, pathtpl,req.params.tribe);
 | 
			
		||||
      res.status(sendemail.status).send(sendemail);
 | 
			
		||||
    } else {
 | 
			
		||||
      res
 | 
			
		||||
        .status(404)
 | 
			
		||||
        .send({
 | 
			
		||||
          status: 404,
 | 
			
		||||
          ref: "Notification",
 | 
			
		||||
          msg: "templatenotfound",
 | 
			
		||||
          data: { pathtpl },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  async (req, res) => {
 | 
			
		||||
    const data = req.body.data;
 | 
			
		||||
    data.emailsto = req.body.emails;
 | 
			
		||||
    const pathtpl = `../../${req.params.tribe}/template/${req.params.template}_${req.session.header.xlang}.js`;
 | 
			
		||||
    const sendemail = await Notifications.manageemail(
 | 
			
		||||
      data,
 | 
			
		||||
      pathtpl,
 | 
			
		||||
      req.params.tribe
 | 
			
		||||
    );
 | 
			
		||||
    res.status(sendemail.status).send(sendemail);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -99,14 +105,12 @@ router.post(
 | 
			
		||||
router.post("/registeranonymous", checkHeaders, (req, res) => {
 | 
			
		||||
  //console.log("list registration ",req.body)
 | 
			
		||||
  if (!req.body.typekey || !["email", "telephone"].includes(req.body.typekey)) {
 | 
			
		||||
    return res
 | 
			
		||||
      .status(406)
 | 
			
		||||
      .json({
 | 
			
		||||
        status: 406,
 | 
			
		||||
        ref: "Notifications",
 | 
			
		||||
        msg: "typekeyunknown",
 | 
			
		||||
        data: { typekey: req.body.typekey },
 | 
			
		||||
      });
 | 
			
		||||
    return res.status(406).json({
 | 
			
		||||
      status: 406,
 | 
			
		||||
      ref: "Notifications",
 | 
			
		||||
      msg: "typekeyunknown",
 | 
			
		||||
      data: { typekey: req.body.typekey },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  const key = req.body.contactpoint
 | 
			
		||||
    ? req.body.contactpoint
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user