558 lines
18 KiB
JavaScript
Executable File
558 lines
18 KiB
JavaScript
Executable File
const fs = require( 'fs-extra' );
|
|
const path = require( 'path' );
|
|
const formidable = require( 'formidable' );
|
|
const jsonfile = require( 'jsonfile' );
|
|
const mustache = require( 'mustache' );
|
|
const moment = require( 'moment' );
|
|
const UUID = require( 'uuid' );
|
|
const pdf = require( "pdf-creator-node" );
|
|
const nodemailer = require( 'nodemailer' );
|
|
const smtpTransport = require( 'nodemailer-smtp-transport' );
|
|
const axios = require( 'axios' );
|
|
const { GoogleSpreadsheet } = require( 'google-spreadsheet' );
|
|
const config = require( '../tribes/townconf.js' );
|
|
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js` );
|
|
|
|
const Outputs = {};
|
|
|
|
Outputs.ggsheet2json = async ( req, header ) => {
|
|
/*
|
|
req.ggsource = key name of the ggsheet into clientconf
|
|
req.sheets = [list of sheet names to import]
|
|
into /ggsheets/ ggsource.json = {resultfolder,productIdSpreadsheet,
|
|
googleDriveCredentials}
|
|
*/
|
|
if( !fs.existsSync( `${config.tribes}/${header.xworkon}/${req.ggsource}` ) ) {
|
|
// Misconfiguration
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
data: {},
|
|
info: [ 'Misconfiguration' ],
|
|
moreinfo: `${header.xworkon}/${req.ggsource} missing file check gitlabdocumentation`,
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
}
|
|
const confgg = fs.readJsonSync( `${config.tribes}/${header.xworkon}/${req.ggsource}`, 'utf-8' )
|
|
//const ggconnect = clientconf.ggsheet[ req.ggsource ]
|
|
//googleDriveCredentials;
|
|
//logger.info( ggconnect )
|
|
doc = new GoogleSpreadsheet( confgg.productIdSpreadsheet );
|
|
await doc.useServiceAccountAuth( confgg.googleDriveCredentials );
|
|
await doc.loadInfo();
|
|
let result = [];
|
|
let invalidfor = "";
|
|
//logger.info( "sheets", req.sheets );
|
|
for( const sheetName of req.sheets ) {
|
|
logger.info( 'loading: ', sheetName );
|
|
if( !doc.sheetsByTitle[ sheetName ] ) {
|
|
invalidfor += " " + sheetName;
|
|
} else {
|
|
sheet = doc.sheetsByTitle[ sheetName ]
|
|
await sheet.loadHeaderRow();
|
|
const records = await sheet.getRows( { offset: 1 } )
|
|
records.forEach( record => {
|
|
let offer = {}
|
|
for( let index = 0; index < record._sheet.headerValues.length; index++ ) {
|
|
offer[ record._sheet.headerValues[ index ] ] = record[ record._sheet.headerValues[ index ] ];
|
|
}
|
|
result.push( offer );
|
|
} );
|
|
}
|
|
}
|
|
const idfile = UUID.v4();
|
|
fs.outputJsonSync( `${config.tribes}/${header.xworkon}/${confgg.resultfolder}/${idfile}.json`, result, 'utf8' );
|
|
if( invalidfor == "" ) {
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
data: `${confgg.resultfolder}/${idfile}.json`,
|
|
info: [ 'Successfull' ],
|
|
model: 'Outputs'
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
status: 207,
|
|
payload: {
|
|
data: `${confgg.resultfolder}/${idfile}.json`,
|
|
info: [ 'PartialySuccessfull' ],
|
|
moreinfo: `Following sheetNames does not exist :${invalidfor}`,
|
|
model: 'Outputs'
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
// REVOIR TOUT CE QU'IL Y A EN DESSOUS et ré-écrire avec la logique de ggsheet2json phil 25/10/2021
|
|
///////////////////////////////////////////////////
|
|
const sleep = ( milliseconds = 500 ) => new Promise( resolve => setTimeout( resolve, milliseconds ) );
|
|
/*
|
|
Process any data to a specific output:
|
|
emailer => generate a finale text file to send
|
|
csv => export json file to csv data
|
|
pdf => generate a customized document
|
|
*/
|
|
Outputs.envoiemail = async ( msg, nowait, nbfois ) => {
|
|
// logger.info('{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}');
|
|
// logger.info('msg to send', msg);
|
|
logger.info( 'nbfois', nbfois );
|
|
let transporter = nodemailer.createTransport( msg.smtp );
|
|
if( !nowait ) {
|
|
logger.info( 'attente 1er msg avant d envoyer les autres' );
|
|
const transport = await transporter.verify();
|
|
logger.info( 'transport', transport );
|
|
if( transport.error ) {
|
|
logger.info( 'Probleme de smtp', error );
|
|
return {
|
|
status: 500,
|
|
payload: {
|
|
info: ''
|
|
}
|
|
};
|
|
} else {
|
|
let rep = await transporter.sendMail( msg );
|
|
logger.info( 'rep sendMail', rep );
|
|
if( rep.accepted && rep.accepted.length > 0 && rep.rejected.length == 0 ) {
|
|
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after waiting 1st email to send ' + msg.to + '\n', 'utf-8' );
|
|
return {
|
|
status: '200',
|
|
payload: {
|
|
info: [ 'send1stemailok' ],
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
}
|
|
// status à tester si necessaire de detecter une erreur
|
|
// if (rep.rejectedErrors) {
|
|
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after waiting result\n ' + msg.to, 'utf-8' );
|
|
return {
|
|
status: '500',
|
|
payload: {
|
|
info: [ 'rejectedError' ],
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
}
|
|
} else {
|
|
transporter.sendMail( msg, async ( err, info ) => {
|
|
if( err ) {
|
|
if( nbfois < 4 ) {
|
|
logger.info( 'nouvelle tentative ', nbfois );
|
|
await sleep( 600000 ); // attends 60sec pour faire une niéme tentative
|
|
Outputs.envoiemail( msg, true, nbfois + 1 );
|
|
} else {
|
|
// logerror in file
|
|
logger.info( 'err', err )
|
|
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after 4 tries to ' + info.rejected.join( ',' ) + '\n', 'utf-8' );
|
|
// logger.info('msg.to not well sent', msg.to);
|
|
}
|
|
} else {
|
|
logger.info( 'info', info )
|
|
// logger.info('msg.to well sent', msg.to);
|
|
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after ' + nbfois + ' tries to ' + info.accepted.join( ',' ) + '\n', 'utf-8' );
|
|
}
|
|
} );
|
|
}
|
|
// return something to not wait the rest of email
|
|
return {
|
|
status: '200',
|
|
payload: {
|
|
info: [ 'send1stemailok' ],
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
};
|
|
Outputs.generemsg = async ( msg, header ) => {
|
|
/*
|
|
wait msg sent and return result sent
|
|
*/
|
|
// Recupere les parametre smtp du domainName à utiliser
|
|
logger.info( 'pass Outputs.generemsg' )
|
|
try {
|
|
const confclientexpediteur = jsonfile.readFileSync( `${config.tribes}/${msg.tribeidperso.tribeidexpediteur}/clientconf.json` );
|
|
//logger.info('expediteur', confclientexpediteur);
|
|
msg.smtp = confclientexpediteur.smtp;
|
|
/* const confclient = jsonfile.readFileSync(
|
|
`${config.tribes}/${msg.tribeidperso.tribeid}/clientconf.json`
|
|
);*/
|
|
} catch ( err ) {
|
|
logger.info( 'la conf smtp du client n\'est pas definie' );
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'smtpnotdefined' ],
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
}
|
|
logger.info( msg );
|
|
if( !msg.template.sendascontent && msg.template.htmlfile ) {
|
|
try {
|
|
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
} catch ( err ) {
|
|
logger.info( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
logger.info( err );
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'fileUnknown' ],
|
|
model: 'UploadFiles',
|
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
}
|
|
};
|
|
}
|
|
}
|
|
if( msg.template.html.length == 0 ) {
|
|
logger.info( 'template.html est vide' )
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'ERRnotemplate' ],
|
|
model: 'Outputs',
|
|
moreinfo: 'No template email check '
|
|
}
|
|
};
|
|
}
|
|
// mustache any data into
|
|
// logger.info(msg);
|
|
const msg2send = {};
|
|
msg2send.smtp = msg.smtp;
|
|
msg2send.from = msg.tribeidperso.from;
|
|
if( msg.tribeidperso.cc ) msg2send.cc = msg.tribeidperso.cc;
|
|
if( msg.tribeidperso.bcc ) msg2send.bcc = msg.tribeidperso.bcc;
|
|
if( msg.tribeidperso.replyTo ) msg2send.replyTo = msg.tribeidperso.replyTo;
|
|
msg2send.headers = {
|
|
'x-campaign-id': msg.tribeidperso.messageId,
|
|
'x-client-nd-id': msg.tribeidperso.tribeid,
|
|
'x-template-nd-id': msg.tribeidperso.templateId
|
|
};
|
|
// we get in datacust= {tribeidperso: with clientconf,destperso: personnalise data to send for email}
|
|
// logger.info(msg);
|
|
logger.info( 'nb de message to send:', msg.destperso.length );
|
|
//logger.info(msg.destperso);
|
|
//msg.destperso.forEach(async (perso, pos) => {
|
|
let pos;
|
|
let pass1ermsg = false;
|
|
for( pos = 0; pos < msg.destperso.length; pos++ ) {
|
|
const datacust = {
|
|
tribeidperso: msg.tribeidperso,
|
|
destperso: msg.destperso[ pos ]
|
|
};
|
|
// Evaluation of each field if mustache exist
|
|
const datacusteval = {};
|
|
Object.keys( datacust.tribeidperso )
|
|
.forEach( k => {
|
|
if( typeof datacust.tribeidperso[ k ] === 'string' || datacust.tribeidperso[ k ] instanceof String ) {
|
|
datacusteval[ k ] = mustache.render( datacust.tribeidperso[ k ], datacust )
|
|
} else {
|
|
datacusteval[ k ] = datacust.tribeidperso[ k ];
|
|
}
|
|
} )
|
|
Object.keys( datacust.destperso )
|
|
.forEach( k => {
|
|
if( typeof datacust.destperso[ k ] === 'string' || datacust.destperso[ k ] instanceof String ) {
|
|
datacusteval[ k ] = mustache.render( datacust.destperso[ k ], datacust )
|
|
} else {
|
|
datacusteval[ k ] = datacust.destperso[ k ];
|
|
}
|
|
} )
|
|
msg2send.to = msg.destperso[ pos ].email;
|
|
logger.info( 'msg2send.to ' + msg2send.to + ' pos:' + pos );
|
|
// logger.info('avec datacusteval ', datacusteval)
|
|
msg2send.subject = mustache.render( msg.template.subject, datacusteval );
|
|
msg2send.text = mustache.render( msg.template.text, datacusteval );
|
|
msg2send.html = mustache.render( msg.template.html, datacusteval );
|
|
let nowait = true;
|
|
if( config.emailerurl == 'http://devapia.maildigit.fr:3015' ) {
|
|
fs.writeFileSync( 'devdata/tmp/test.html', msg2send.html, 'utf-8' );
|
|
logger.info( 'lancement email sur dev, pour controler le mail générer voir ds ./config.js il faut changer config.emailerurl avec https://mail.maildigit.fr pour envoyer le mail ' )
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'msgsentok' ],
|
|
model: 'Outputs',
|
|
moreinfo: "parametrer sur emailer de dev et pas de production"
|
|
}
|
|
}
|
|
}
|
|
if( pos == 0 ) {
|
|
nowait = false;
|
|
/* we are waiting the first email was sent ok then we send all other
|
|
check NEEDDATA/OVH/workspace/emailerovh to send emailer with nodemailer and nodemailer-smtp-transport
|
|
*/
|
|
// logger.info('envoie msg', msg);
|
|
//logger.info(msg2send);
|
|
const ret = await Outputs.envoiemail( msg2send, nowait, 0 );
|
|
logger.info( 'ret 1er msg', ret );
|
|
if( ret.status == 200 ) {
|
|
pass1ermsg = true;
|
|
};
|
|
} else if( pass1ermsg ) {
|
|
logger.info( '###############################################' )
|
|
logger.info( "envoie msg numero: " + pos + " email: " + msg2send.to )
|
|
//logger.info(msg2send)
|
|
Outputs.envoiemail( msg2send, nowait, 0 );
|
|
/*Outputs.envoiemail(msg2send, nowait, 0).then(rep => {
|
|
logger.info("envoie email" + pos)
|
|
}).catch(err => {
|
|
logger.info(err);
|
|
});*/
|
|
};
|
|
};
|
|
if( pass1ermsg ) {
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'msgsentok' ],
|
|
model: 'Outputs'
|
|
}
|
|
};
|
|
} else {
|
|
return {
|
|
status: 500,
|
|
payload: {
|
|
info: [ 'msgsentko' ],
|
|
model: 'Ouputs',
|
|
moreinfo: "1er msg non envoyé car erreur"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
Outputs.sendMailcampain = async ( msg, headersmsg ) => {
|
|
/*
|
|
Permet de lancer une campagne de mail personnalisé en mode web service
|
|
avec axios config.emailerurl https://mail qui peut être sur un autre serveur que celui en cours
|
|
Attention headermsg doit être retraduit avec les champs envoyé par un navigateur
|
|
Si le serveur en cours appelle cette fonction les champs du header doivent changer
|
|
voir config.js node_env .exposedHeaders
|
|
|
|
Pour un exemple de msg voir u exemple type de message envoyé dans un tribeid/domain/clientconf.json
|
|
avec l'envoi d'email
|
|
*/
|
|
//logger.info(msg)
|
|
// On ajoute le contenu du template directement dans la demande
|
|
if( msg.template.sendascontent && msg.template.htmlfile ) {
|
|
try {
|
|
logger.info( 'test', msg.template.sendascontent )
|
|
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
} catch ( err ) {
|
|
logger.info( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
//logger.info(err);
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'fileUnknown' ],
|
|
model: 'UploadFiles',
|
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
}
|
|
};
|
|
}
|
|
delete msg.template.htmlfile;
|
|
if( msg.template.html.length == 0 ) {
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'ERRnotemplate' ],
|
|
model: 'Outputs',
|
|
moreinfo: 'No template email check '
|
|
}
|
|
};
|
|
}
|
|
}
|
|
logger.info( 'envoie sur', `${config.emailerurl}/outputs/msg` )
|
|
//logger.info(msg)
|
|
// on check si les key de headermsg sont des key traduite via exposedHeaders
|
|
// (cas ou c'est l'application qui envoie un email)
|
|
if( headersmsg.xtribeid ) {
|
|
Object.keys( config.exposedHeaders )
|
|
.forEach( h => {
|
|
headersmsg[ h ] = headersmsg[ config.exposedHeaders[ h ] ];
|
|
delete headersmsg[ config.exposedHeaders[ h ] ];
|
|
} );
|
|
}
|
|
// on ajoute le code pour la signature
|
|
headersmsg.hashbody = msg.code;
|
|
logger.info( 'header after traduction: ', headersmsg )
|
|
try {
|
|
const resCamp = await axios.post( `${config.emailerurl}/outputs/msg`, msg, {
|
|
headers: headersmsg
|
|
} );
|
|
//logger.info('Etat:', resCamp);
|
|
logger.info( 'Tried to send 1st email of the campain ' + msg.destperso[ 0 ].email );
|
|
// it just check the 1st email in destperso to return an answer if 1st is ok then all other are send in queue
|
|
if( resCamp ) {
|
|
return resCamp;
|
|
} else {
|
|
return { status: 200, payload: { info: [ 'CampainSent' ], model: 'Outputs' } };
|
|
}
|
|
} catch ( err ) {
|
|
// aios error handler
|
|
return { status: 401, payload: { info: [ 'erreuraxios' ], model: 'Outputs', moreinfo: err.message } }
|
|
}
|
|
};
|
|
Outputs.get = function ( filename, header ) {
|
|
// check file exist
|
|
const file = `${config.tribes}/${header.xworkon}/${filename}`;
|
|
// logger.info('fichier demande ', file);
|
|
if( !fs.existsSync( file ) ) {
|
|
// logger.info('le fichier demande n existe pas ', file);
|
|
return {
|
|
status: 404,
|
|
payload: {
|
|
info: [ 'fileUnknown' ],
|
|
model: 'UploadFiles'
|
|
}
|
|
};
|
|
} else {
|
|
logger.info( 'envoie le fichier ', file );
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'fileknown' ],
|
|
model: 'UploadFiles',
|
|
file: file
|
|
}
|
|
};
|
|
}
|
|
};
|
|
Outputs.addjson = function ( data, header ) {
|
|
/*
|
|
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
|
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
|
*/
|
|
// logger.info(req.body.jsonp);
|
|
try {
|
|
jsonfile.writeFileSync( header.destinationfile + '/' + header.filename, data.jsonp );
|
|
if( data.callback ) {
|
|
const execCB = require( `${config.mainDir}/models/tribeid/${header.xworkon
|
|
}` );
|
|
execCB[ data.callback ]();
|
|
}
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'wellUpload' ],
|
|
model: 'UploadFiles',
|
|
render: {
|
|
destination: header.destinationfile,
|
|
filename: header.filename
|
|
}
|
|
}
|
|
};
|
|
} catch ( err ) {
|
|
logger.info( 'Impossible de sauvegarder le fichier, A COMPRENDRE', err );
|
|
return {
|
|
status: 503,
|
|
payload: {
|
|
info: [ 'savingError' ],
|
|
model: 'UploadFiles'
|
|
}
|
|
};
|
|
}
|
|
};
|
|
Outputs.add = function ( req, header ) {
|
|
const form = new formidable.IncomingForm();
|
|
logger.info( 'req.headers', req.headers );
|
|
logger.info( 'req.params', req.params );
|
|
logger.info( 'req.query', req.query );
|
|
logger.info( 'req.body', req.body );
|
|
let destinationfile = `${config.tribes}/${header.xworkon}/${header.destinationfile
|
|
}`;
|
|
form.parse( req, function ( err, fields, files ) {
|
|
logger.info( 'files', files.file.path );
|
|
logger.info( 'fields', fields );
|
|
const oldpath = files.file.path;
|
|
destinationfile += '/' + files.file.name;
|
|
logger.info( 'oldpath', oldpath );
|
|
logger.info( 'destinationfile', destinationfile );
|
|
fs.copyFile( oldpath, destinationfile, function ( err ) {
|
|
if( err ) {
|
|
logger.info( err );
|
|
return {
|
|
status: 500,
|
|
payload: {
|
|
info: [ 'savingError' ],
|
|
model: 'UploadFiles'
|
|
}
|
|
};
|
|
} else {
|
|
logger.info( 'passe' );
|
|
fs.unlink( oldpath );
|
|
return {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'wellUpload' ],
|
|
model: 'UploadFiles',
|
|
render: {
|
|
destination: destinationfile
|
|
}
|
|
}
|
|
};
|
|
}
|
|
} );
|
|
} );
|
|
};
|
|
Outputs.generepdf = ( req, header ) => {
|
|
return new Promise( ( resolve, reject ) => {
|
|
let options = {
|
|
format: "A4",
|
|
orientation: "portrait",
|
|
border: "10mm",
|
|
footer: {
|
|
height: "20mm",
|
|
contents: {
|
|
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>', // html pagination if edit needed
|
|
}
|
|
}
|
|
};
|
|
let document = {
|
|
html: req.html,
|
|
data: {
|
|
data: req.data,
|
|
},
|
|
path: `${config.tribes}/${header.xtribe}/outputs/${UUID.v4()}.pdf`,
|
|
type: "",
|
|
};
|
|
pdf // generate pdf
|
|
.create( document, options )
|
|
.then( ( res ) => {
|
|
resolve( {
|
|
status: 200,
|
|
payload: {
|
|
info: [ 'wellPdfGenerated' ],
|
|
model: 'Outputs',
|
|
data: {
|
|
path: document.path,
|
|
filename: req.data.filename
|
|
},
|
|
render: {
|
|
filename: req.data.filename
|
|
}
|
|
}
|
|
} );
|
|
} )
|
|
.catch( ( err ) => {
|
|
reject( {
|
|
status: 500,
|
|
payload: {
|
|
info: [ 'pdfGenerationError' ],
|
|
model: 'Outputs',
|
|
error: err
|
|
}
|
|
} );
|
|
} );
|
|
} );
|
|
};
|
|
module.exports = Outputs;
|