401 lines
15 KiB
JavaScript
Executable File
401 lines
15 KiB
JavaScript
Executable File
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`);
|
|
/*
|
|
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: 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 = `<p>${data.contactname} - ${contact.replace(/###/g,' ').replace(/##/g,":")}</p><p>Message: ${data.contactmessage}</p>`
|
|
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: notif
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
module.exports = Messages;
|