/* eslint-disable no-unused-vars */ const bcrypt = require('bcrypt') const fs = require('fs-extra') const path = require('path') const jsonfile = require('jsonfile') const glob = require('glob') const Mustache = require('mustache') const jwt = require('jwt-simple') const { DateTime } = require('luxon') const UUID = require('uuid') const Outputs = require('../models/Outputs.js') const config = require('../tribes/townconf.js') const checkdata = require('../nationchains/socialworld/contracts/checkdata.js') const logger = require('../core/logger') /* Message manager * Manage apixtribe message at different level * this means that an object (most often a user or a contact) want to send a question to an other user. To know more http://gitlab.ndda.fr/philc/apixtribe/-/wikis/HOWTONotification */ const Messages = {} Messages.init = () => { console.group('init Message') Messages.aggregate() } Messages.byEmailwithmailjet = (tribeid, msg) => { /* @tribeid requester @msg =[{ To:[{Email,Name}], Cc:[{Email,Name}], Bcc:[{Email,Name}]}], Subject:"Subject", TextPart:"texte content", HTMLPart:"html content" }] If tribeid has a mailjet account it use it if not then it use the apixtribe @mailjetconf = {apikeypub:, apikeypriv:, From:{Email:,Name:}} This send a bunch of messages check the mailjet account used FYI: Basic account is 200 email /days 6000 /month Log is stored into tribeidsender/messages/logs/sent/timestamp.json @todo GUI to manage statistics and notification alert limit sender email */ logger.info('Envoie mailjet') const confclient = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`) let tribeidsender = tribeid if (confclient.smtp && confclient.smtp.mailjet) { mailjetconf = confclient.smtp.mailjet } else { const confapixtribe = fs.readJsonSync(`${config.tribes}/${config.mayorId}/clientconf.json`) if (!(confapixtribe.smtp && confapixtribe.smtp.mailjet)) { return { status: 403, data: { models: 'Messages', info: ['nosmtpmailjet'], moreinfo: 'missing smtpmailjet parameter in apixtribe contact your admin to activate an mailjet account on this server.' } } } tribeidsender = 'apixtribe' mailjetconf = confapixtribe.smtp.mailjet } // add from from setings account const MSG = msg.map(m => { m.From = mailjetconf.From return m }) const mailjet = require('node-mailjet') .connect(mailjetconf.apikeypub, mailjetconf.apikeypriv) const request = mailjet.post('send', { version: 'v3.1' }) .request({ Messages: MSG }) request .then(result => { // store into tribeidsender/messages/logs/sent/timestamp.json const t = Date.now() MSG.result = result.body fs.outputJson(`${config.tribes}/${tribeidsender}/messages/logs/sent/${t}.json`, MSG) logger.info(result.body) }) .catch(err => { const t = Date.now() MSG.result = err fs.outputJson(`${config.tribes}/${tribeidsender}/messages/logs/error/${t}.json`, MSG) logger.info(err.statusCode, err) }) } Messages.buildemail = (tribeid, tplmessage, data) => { /* @tribeid => client concerned by sending email get info from clientconf.json (customization, useradmin:{ EMAIL,LOGIN} , ...) @tplmessage => folder where template is available (ex: apixtribe/referentials/dataManagement/template/order) @data { destemail = email to if destuuid => then it get user EMAIL if exiat subject = mail subject // any relevant information for template message msgcontenthtml = html msg content msgcontenttxt = text msg content ....} @return msg mail part ready to send by mailjet or other smtp */ if (!fs.existsSync(`${config.tribes}/${tribeid}/clientconf.json`)) { return { status: 404, data: { model: 'Messages', info: ['tribeiddoesnotexist'], moreinfo: `${tribeid} does not exist` } } } if (!fs.existsSync(`${config.tribes}/${tplmessage}`)) { return { status: 404, data: { model: 'Messages', info: ['tplmessagedoesnotexist'], moreinfo: `tpl does not exist ${tplmessage}` } } } const clientconf = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`) // add clientconf.customization into data for template data.customization = clientconf.customization // manage receiver const msg = { To: [{ Email: clientconf.useradmin.EMAIL, Name: clientconf.useradmin.LOGIN }] } if (data.destemail) { msg.To.push({ Email: data.destemail }) } if (data.destuuid && fs.exist(`${config.tribes}/${tribeid}/users/${data.destuuid}.json`)) { const uuidconf = fs.readJsonSync(`${config.tribes}/${tribeid}/users/${data.destuuid}.json`) if (uuidconf.EMAIL && uuidconf.EMAIL.length > 4) { msg.To.push({ Email: uuidconf.EMAIL, Name: uuidconf.LOGIN }) } } // logger.info( data ) // email content msg.Subject = `Message from ${tribeid}` if (data.subject) msg.Subject = data.subject msg.TextPart = Mustache.render(fs.readFileSync(`${config.tribes}/${data.tplmessage}/contenttxt.mustache`, 'utf-8'), data) msg.HTMLPart = Mustache.render(fs.readFileSync(`${config.tribes}/${data.tplmessage}/contenthtml.mustache`, 'utf-8'), data) return { status: 200, data: { model: 'Messages', info: ['msgcustomsuccessfull'], msg } } } Messages.postinfo = (data) => { /* Data must have: at least one of desttribeid:,destuuid:,destemail: if an email have to be sent tplmessage:"tribeid/referentials/dataManagement/template/tplname", at least one of contactemail:,contactphone,contactlogin,contactuuid any other usefull keys Data is stored into contacts object .json */ let contact = ''; ['contactemail', 'contactphone', 'contactuuid', 'contactlogin'].forEach(c => { if (data[c]) contact += c + '##' + data[c] + '###' }) logger.info(contact) if (contact === '') { return { status: 404, data: { model: 'Messages', info: ['contactundefine'], moreinfo: 'no contact field found in this form' } } } if (!data.desttribeid) { return { status: 404, data: { model: 'Messages', info: ['tribeidundefine'], moreinfo: 'no desttribeid field found in this form' } } } // save new message in contacts let messages = {} if (fs.existsSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`)) { messages = fs.readJsonSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, 'utf-8') } messages[Date.now()] = data fs.outputJsonSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, messages) // if templatemessage exist then we send alert email // data content have to be consistent with tplmessage to generate a clean email if (data.tplmessage && data.tplmessage !== '' && fs.existsSync(`${config.tribes}/${data.tplmessage}`)) { if (!(data.msgtplhtml && data.msgtpltxt)) { data.msgcontenthtml = `

${data.contactname} - ${contact.replace(/###/g, ' ').replace(/##/g, ':')}

Message: ${data.contactmessage}

` data.msgcontenttxt = `${data.contactname} - ${contact}/nMessage:${data.contactmessage}\n` } else { data.msgcontenthtml = Mustache.render(data.msgtplhtml, data) data.msgcontenttxt = Mustache.render(data.msgtpltxt, data) } const msg = Messages.buildemail(data.desttribeid, data.tplmessage, data) if (msg.status === 200) { Messages.byEmailwithmailjet(data.desttribeid, [msg.data.msg]) } // we get error message eventualy but email feedback sent is not in real time return msg } else { return { status: 404, data: { info: 'missingtpl', model: 'Messages', moreinfo: `missing template ${data.tplmessage}` } } } } Messages.aggregate = () => { // collect each json file and add them to a global.json notifat require level const dest = {} try { glob.sync(`${config.tribes}/**/notif_*.json`) .forEach(f => { // logger.info( 'find ', f ) const repglob = `${path.dirname(f)}/global.json` if (!dest[repglob]) { dest[repglob] = [] } dest[repglob].push(fs.readJsonSync(f, 'utf-8')) fs.removeSync(f) }) // logger.info( dest ) Object.keys(dest) .forEach(g => { let notif = [] if (fs.existsSync(g)) { notif = fs.readJsonSync(g) } ; fs.writeJsonSync(g, notif.concat(dest[g]), 'utf-8') }) } catch (err) { Console.log('ATTENTION, des Messages risquent de disparaitre et ne sont pas traitées.') } } Messages.object = (data, header) => { /* Create or replace an object no check at all here this is only a way to add / replace object if deeper thing have to be checheck or done then a callback:{tribeidplugin,pluginname,functionname} data.descttribeid tribeid to send at least to admin data.tplmessage = folder of emailtemplate */ logger.info('data', data) logger.info(`${config.tribes}/${header.xworkon}/${data.object}`) if (!fs.existsSync(`${config.tribes}/${header.xworkon}/${data.object}`)) { return { status: 404, data: { model: 'Messages', info: ['UnknownObjectfortribeid'], moreinfo: `This object ${data.object} does not exist for this tribeid ${header.xworkon}` } } } if (data.uuid === 0) { data.uuid = UUID.v4() } if (data.callback) { // check from plugin data and add relevant data const Plug = require(`${config.tribes}/${data.callback.tribeid}/plugins/${data.callback.plugins}/Model.js`) const check = Plug[data.callback.function](header.xworkon, data) if (check.status === 200) { data = check.data.data } else { return check } } ; fs.outputJsonSync(`${config.tribes}/${header.xworkon}/${data.object}/${data.uuid}.json`, data) // if templatemessage exist then we try to send alert email if (data.tplmessage && data.tplmessage !== '' && fs.existsSync(`${config.tribes}/${data.tplmessage}`)) { const msg = Messages.buildemail(data.desttribeid, data.tplmessage, data) if (msg.status === 200) { logger.info('WARN EMAIL DESACTIVATED CHANGE TO ACTIVATE in Messages.js') // Messages.byEmailwithmailjet( data.desttribeid, [ msg.data.msg ] ); } // we get error message eventualy but email feedback sent is not in real time see notification alert in case of email not sent. } ; // Sendback data with new information return { status: 200, data: { model: 'Messages', info: ['Successregisterobject'], msg: data } } } Messages.notification = (data, header) => { // check if valid notification if (!req.body.elapseddays) { req.body.elapseddays = 3 } let missingkey = ''; ['uuid', 'title', 'desc', 'icon', 'elapseddays', 'classicon'].forEach(k => { if (!data[k]) missingkey += ` ${k},` if (k === 'classicon' && !(['text-danger', 'text-primary', 'text-success', 'text-warning', 'text-info'].includes(data[k]))) { missingkey += ` classicon is not well set ${data[k]}` } }) if (missingkey !== '') { return { status: 422, data: { model: 'Messages', info: ['InvalidNote'], moreinfo: `invalid notification sent cause missing key ${missingkey}` } } } if (!fs.existsSync(`${config.tribes}/${header.xworkon}/${data.object}`)) { return { status: 404, data: { model: 'Messages', info: ['UnknownObjectfortribeid'], moreinfo: `This object ${data.object} does not exist for this tribeid ${data.tribeid}` } } } // by default store in tmp/notification this is only for sysadmin // user adminapixtribe let notdest = `${config.tmp}` if (data.app) { const destapp = `${config.tribes}/${data.app.split(':')[0]}/spacedev/${data.app.split(':')[1]}` if (fs.existsSync(destapp)) { notdest = destapp } } if (data.plugins) { const destplugin = `${config.tribes}/${data.plugins.split(':')[0]}/plugins/${data.plugins.split(':')[1]}` if (fs.existsSync(destplugin)) { notdest = destplugin } } if (data.object) { const destobject = `${config.tribes}/${data.tribeid}/${data.object}/` if (fs.existsSync(destobject)) { notdest = destobject } } if (!data.time) { data.time = Date.now() } fs.outputJsonSync(`${notdest}/Messages/notif_${data.time}.json`, data) return { status: 200, data: { model: 'Messages', info: ['Successregisternotification'], notif: data } } } Messages.request = (tribeid, uuid, ACCESSRIGHTS, apprequest) => { // list notif for each app // list notif for each tribeid / objects if Owned // list notif for // check uuid notification // Collect all notification and agregate them at relevant level; Messages.aggregate() // for test purpose // const notif = jsonfile.readFileSync( `${config.tribes}/ndda/spacedev/mesa/src/static/components/notification/data_notiflist_fr.json` ); let notif if (!fs.existsSync(`${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`)) { // by default we send back this but this highlght an issue notif = { iconnotif: 'bell', number: 1, notifheader: 'Your last Messages', notiffooter: 'Check all Messages', actionnotifmanager: '', href: '?action=notification.view', notifs: [{ urldetail: '#', classicon: 'text-danger', icon: 'alert', title: 'File does not exist', desc: ` ${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`, elapse: 'il y a 13mn' }] } } else { notif = jsonfile.readFileSync(`${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`) // clean up example notif notif.notifs = [] } // check notification for plugins of tribeid of the login glob.sync(`${config.tribes}/${tribeid}/plugins/*/Messages/global.json`) Object.keys(ACCESSRIGHTS.app) .forEach(a => { // check for each app if notification about app const appnot = `${config.tribes}/${a.split(':')[0]}/spacedev/${a.split(':')[1]}/Messages/global.json` if (fs.existsSync(appnot)) { notif.notifs = notif.notifs.concat(fs.readJsonSync(appnot)) } }) Object.keys(ACCESSRIGHTS.plugin) .forEach(p => { // each plugin if (ACCESSRIGHTS.plugin[p].profil === 'owner') { const pluginnot = `${config.tribes}/${p.split(':')[0]}/plugins/${p.split(':')[1]}/Messages/global.json` if (fs.existsSync(pluginnot)) { notif.notifs = notif.notifs.concat(fs.readJsonSync(pluginnot)) } } }) Object.keys(ACCESSRIGHTS.data) .forEach(c => { // each tribeid Object.keys(ACCESSRIGHTS.data[c]) .forEach(o => { const cliobjnot = `${config.tribes}/${c}/${o}/Messages/global.json` // check for each tribeid / Object per accessright user if (fs.existsSync(cliobjnot)) { logger.info(`droit sur client ${c} objet ${o} : ${ACCESSRIGHTS.data[c][o]}`) // check if intersection between user accessrigth for this object and the notification accessright is not empty @Todo replace true by intersec logger.info('WARN no actif filter all notif are shared with any authenticated user') const newnotif = fs.readJsonSync(cliobjnot) .filter(n => { return true }) notif.notifs = notif.notifs.concat(newnotif) } }) }) return { status: 200, data: { model: 'Messages', info: ['successgetnotification'], notif } } } module.exports = Messages