2023-12-07 12:04:19 +01:00
|
|
|
const glob = require("glob");
|
|
|
|
const path = require("path");
|
|
|
|
const fs = require("fs-extra");
|
|
|
|
const axios = require("axios");
|
2024-02-15 12:32:51 +01:00
|
|
|
const dayjs = require("dayjs");
|
2024-07-18 09:34:26 +02:00
|
|
|
const Mustache = require('mustache');
|
2024-02-15 12:32:51 +01:00
|
|
|
const Checkjson = require(`./Checkjson.js`);
|
2023-12-07 12:04:19 +01:00
|
|
|
//const smtp = require("smtp-client");
|
|
|
|
const nodemailer = require("nodemailer");
|
2024-03-20 11:24:03 +01:00
|
|
|
const conf = require(`../../../conf.json`);
|
2024-02-15 12:32:51 +01:00
|
|
|
const currentmod = "Notifications";
|
|
|
|
const log = conf.api.activelog.includes(currentmod);
|
2023-12-07 12:04:19 +01:00
|
|
|
/**
|
|
|
|
* To manage any communication between Pagan
|
|
|
|
* mayor druid emailing/sms/paper from tribe register smtp, simcard, mail api to Person(s) / Pagan(s)
|
|
|
|
* volatile notification message from tribe activities to Pagans / person ()
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
const Notifications = {};
|
|
|
|
|
|
|
|
Notifications.get = (alias, tribeId) => {
|
2024-03-19 09:10:07 +01:00
|
|
|
const notiffile = `../../${req.params.tribeId}/notifications/${req.params.alias}.json`;
|
2023-12-07 12:04:19 +01:00
|
|
|
const msg = fs.existsSync(notiffile) ? fs.readJSONSync(notiffile) : {};
|
|
|
|
return {
|
|
|
|
status: 200,
|
|
|
|
ref: "Notification",
|
|
|
|
msg: "Messagelist",
|
|
|
|
data: { notif: [{ tribeId, msg }] },
|
|
|
|
};
|
|
|
|
};
|
2024-04-03 17:32:37 +02:00
|
|
|
/**
|
|
|
|
* Get statistic of registering email phone
|
|
|
|
*/
|
2024-07-11 13:29:54 +02:00
|
|
|
Notifications.statmaillist = (tribe) => {
|
|
|
|
const statinfo = {};
|
|
|
|
let csv = "email/phone;name;srckey\n";
|
|
|
|
const src = `../../${tribe}/objects/maillinglists/*.json`;
|
|
|
|
console.log(path.resolve(src));
|
|
|
|
glob.sync(src).forEach((f) => {
|
|
|
|
const name = path.basename(f, ".json");
|
|
|
|
const mlst = fs.readJSONSync(f);
|
|
|
|
Object.keys(mlst).forEach((c) => {
|
|
|
|
csv += `"${c}";"${name}";"${mlst[c].srckeys.join("-")}"\n`;
|
|
|
|
mlst[c].srckeys.forEach((s) => {
|
|
|
|
if (!statinfo[s]) statinfo[s] = {};
|
|
|
|
if (!statinfo[s][name]) statinfo[s][name] = 0;
|
|
|
|
statinfo[s][name]++;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2024-04-12 12:49:48 +02:00
|
|
|
// fichier csv stocker en local en attendant d'avoir un back pour stocker la reponse dans data.csv
|
2024-07-11 13:29:54 +02:00
|
|
|
fs.outputFileSync(`../../${tribe}/mailinglst.csv`, csv, "utf-8");
|
|
|
|
return {
|
|
|
|
status: 200,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "statistics",
|
|
|
|
data: statinfo,
|
|
|
|
};
|
|
|
|
};
|
2023-12-29 13:38:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Register an typekey(email) or (phone) key into mailinglist for a tribe
|
2024-07-11 13:29:54 +02:00
|
|
|
*
|
2023-12-29 13:38:47 +01:00
|
|
|
*/
|
|
|
|
Notifications.registertolist = (key, typekey, tribe, mlist, srckey, uuid) => {
|
|
|
|
key = key.toLowerCase();
|
2024-07-11 13:29:54 +02:00
|
|
|
typekey = typekey == "telephone" ? "telephonefr" : typekey;
|
2023-12-29 13:38:47 +01:00
|
|
|
if (!Checkjson.testformat(key, typekey))
|
|
|
|
return {
|
|
|
|
status: 400,
|
|
|
|
ref: "Notifications",
|
2024-02-15 12:32:51 +01:00
|
|
|
msg: "formaterror",
|
2024-07-11 13:29:54 +02:00
|
|
|
data: { fielderr: typekey, format: typekey },
|
2023-12-29 13:38:47 +01:00
|
|
|
};
|
2024-02-19 17:55:06 +01:00
|
|
|
|
2024-03-19 09:10:07 +01:00
|
|
|
const destin = `../../${tribe}/objects/maillinglists/${typekey}_${mlist}.json`;
|
2024-07-11 13:29:54 +02:00
|
|
|
if (!fs.existsSync(destin)) {
|
|
|
|
console.log(
|
|
|
|
`######## Attention tentative d'ecriture non autorisé,le fichier n'existe pas ${destin} créer le à la main vide {}`
|
|
|
|
);
|
|
|
|
return {
|
|
|
|
status: 406,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "destinnotallow",
|
|
|
|
data: { destin },
|
|
|
|
};
|
2024-02-19 17:55:06 +01:00
|
|
|
}
|
|
|
|
|
2023-12-29 13:38:47 +01:00
|
|
|
const filestorage = fs.existsSync(destin) ? fs.readJsonSync(destin) : {};
|
2024-02-15 12:32:51 +01:00
|
|
|
//if (log) console.log(currentmod,`filestorage`,filestorage, key, (filestorage[key]));
|
2023-12-29 13:38:47 +01:00
|
|
|
if (filestorage[key]) {
|
|
|
|
filestorage[key].dt_update = dayjs().toISOString();
|
|
|
|
if (!filestorage[key].srckeys.includes(srckey))
|
|
|
|
filestorage[key].srckeys.push(srckey);
|
|
|
|
if (!filestorage[key].uuids.includes(uuid))
|
|
|
|
filestorage[key].uuids.push(uuid);
|
2024-07-11 13:29:54 +02:00
|
|
|
} else {
|
|
|
|
filestorage[key] = {};
|
|
|
|
filestorage[key].dt_create = dayjs().toISOString();
|
|
|
|
filestorage[key].srckeys = [srckey];
|
|
|
|
filestorage[key].uuids = [uuid];
|
2023-12-29 13:38:47 +01:00
|
|
|
}
|
|
|
|
fs.outputJSONSync(destin, filestorage);
|
2024-07-11 13:29:54 +02:00
|
|
|
return {
|
|
|
|
status: 200,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "registersuccess",
|
|
|
|
data: { key, typekey, tribe, mlist, srckey, uuid },
|
|
|
|
};
|
|
|
|
};
|
2023-12-29 13:38:47 +01:00
|
|
|
/**
|
|
|
|
* Unsubscribe an eamil or phone from a mailinglist for a tribe
|
|
|
|
*/
|
2024-07-11 13:29:54 +02:00
|
|
|
Notifications.unregisterfromlist = (key, typekey, tribe, mlist) => {
|
2024-03-15 08:49:23 +01:00
|
|
|
key = key.toLowerCase();
|
2024-07-11 13:29:54 +02:00
|
|
|
};
|
2023-12-29 13:38:47 +01:00
|
|
|
/**
|
|
|
|
* Message to send to an alias from an anonymous or not
|
|
|
|
*/
|
2024-07-11 13:29:54 +02:00
|
|
|
Notifications.sendcontact = (body, header) => {};
|
2023-12-07 12:04:19 +01:00
|
|
|
Notifications.sendsms = async (data, tribeId) => {
|
|
|
|
/**
|
|
|
|
* Never use need wallet in mailjet to test
|
|
|
|
* To set up with mailjet see https://dev.mailjet.com/sms/guides/send-sms-api/#authentication
|
|
|
|
*
|
|
|
|
* @param {string} data.To a phone number with international +3360101010101
|
|
|
|
* @param {string} data.Text text to send
|
|
|
|
*
|
|
|
|
* a conf.sms with {url:"smsurl", Token:"", From:""}
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!conf.sms) {
|
|
|
|
return {
|
|
|
|
status: 412,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingconf",
|
|
|
|
data: { tribe: tribeId },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let missingk = [][("To", "Text")].forEach((k) => {
|
|
|
|
if (!data[k]) {
|
|
|
|
missingk.push(k);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (missingk.lenght > 0) {
|
|
|
|
return {
|
|
|
|
status: 428,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingdata",
|
|
|
|
data: { missingk: missingk },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let confsms = conf.sms;
|
2024-07-11 13:29:54 +02:00
|
|
|
if (fs.existsSync(`../../itm/${req.session.header.xtribe}.json`)) {
|
2023-12-07 12:04:19 +01:00
|
|
|
const conftrib = fs.readJSONSync(
|
2024-03-19 09:10:07 +01:00
|
|
|
`../../itm/${req.session.header.xtribe}.json`
|
2023-12-07 12:04:19 +01:00
|
|
|
);
|
|
|
|
if (conftrib.sms) confsms = conftrib.sms;
|
|
|
|
}
|
|
|
|
data.From = confsms.From;
|
|
|
|
const sendsms = await axios.post(confsms.url, {
|
|
|
|
headers: {
|
|
|
|
Authorization: `Bearer ${confsms.MJ_TOKEN}`,
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
});
|
|
|
|
if (sendsms.status == 200) {
|
|
|
|
return {
|
|
|
|
status: 200,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "successfullsentsms",
|
|
|
|
data: {},
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
status: sendsms.status,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "errsendsms",
|
|
|
|
data: { err: sendsms.data },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/* si tout se passe bien:
|
|
|
|
{
|
|
|
|
"From": "MJPilot",
|
|
|
|
"To": "+33600000000",
|
|
|
|
"Text": "Have a nice SMS flight with Mailjet !",
|
|
|
|
"MessageId": "2034075536371630429",
|
|
|
|
"SmsCount": 1,
|
|
|
|
"CreationTS": 1521626400,
|
|
|
|
"SentTS": 1521626402,
|
|
|
|
"Cost": {
|
|
|
|
"Value": 0.0012,
|
|
|
|
"Currency": "EUR"
|
|
|
|
},
|
|
|
|
"Status": {
|
|
|
|
"Code": 2,
|
|
|
|
"Name": "sent",
|
|
|
|
"Description": "Message sent"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
2024-07-11 13:29:54 +02:00
|
|
|
Notifications.manageemail = (data, template, tribe) => {
|
|
|
|
/**
|
|
|
|
* Manage email publipostage
|
|
|
|
* data must contain emailsto
|
|
|
|
* data optionaly can contain Cc,Bcc as array of emails and attachments as array of filename (into the server)
|
|
|
|
* Await the 1st email
|
|
|
|
*/
|
2024-07-18 09:34:26 +02:00
|
|
|
//console.log(data)
|
2024-07-11 13:29:54 +02:00
|
|
|
if (!data.emailsto || data.emailsto.length == 0) {
|
|
|
|
return {
|
|
|
|
status: 406,
|
|
|
|
ref: "Notifications",
|
2024-07-18 09:34:26 +02:00
|
|
|
msg: "emailstomissing",
|
2024-07-11 13:29:54 +02:00
|
|
|
data: data,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (typeof data.emailsto === "string") data.emailsto = [data.emailsto];
|
2024-07-18 09:34:26 +02:00
|
|
|
|
|
|
|
if (!fs.existsSync(path.resolve(template))){
|
|
|
|
return {
|
|
|
|
status: 404,
|
|
|
|
ref: "Notification",
|
|
|
|
msg: "templatenotfound",
|
|
|
|
data: { template:path.resolve(template) },
|
|
|
|
}
|
|
|
|
}
|
2024-07-11 13:29:54 +02:00
|
|
|
const tplemail = require(path.resolve(template));
|
2024-07-18 09:34:26 +02:00
|
|
|
let sendit={status:200,ref:"Notifications",msg:"successfullsend"};
|
2024-07-11 13:29:54 +02:00
|
|
|
data.emailsto.forEach(async (e, i) => {
|
|
|
|
if (Checkjson.testformat(e, "email")) {
|
|
|
|
const dat = {};
|
|
|
|
dat.to = e;
|
|
|
|
dat.subject = Mustache.render(tplemail.subject, data);
|
2024-07-19 16:53:25 +02:00
|
|
|
dat.html = Mustache.render(tplemail.html, data);
|
2024-07-11 13:29:54 +02:00
|
|
|
dat.text = Mustache.render(tplemail.text, data);
|
2024-07-19 16:53:25 +02:00
|
|
|
//dat.Cc = data.Cc ? tplemail.Cc.push(data.Cc) : tplemail.Cc;
|
|
|
|
//dat.Bcc = data.Bcc ? tplemail.Bcc.push(data.Bcc) : tplemail.Bcc;
|
2024-07-18 09:34:26 +02:00
|
|
|
if (data.attachments){
|
|
|
|
data.attachments.forEach(a=>tplemail.attachments.push(a))
|
|
|
|
}
|
2024-07-11 13:29:54 +02:00
|
|
|
if (i == 0) {
|
2024-07-18 09:34:26 +02:00
|
|
|
sendit = await Notifications.sendmail(dat, tribe);
|
|
|
|
if (sendit.status != 200) return {status:406,ref:"Notifications",msg:"emailnotsent"};
|
2024-07-11 13:29:54 +02:00
|
|
|
} else {
|
2024-07-18 09:34:26 +02:00
|
|
|
Notifications.sendmail(dat, tribe);
|
2024-07-11 13:29:54 +02:00
|
|
|
}
|
|
|
|
} else {
|
2024-07-18 09:34:26 +02:00
|
|
|
// not a well formated email @todo add a log file to follow it
|
2024-07-11 13:29:54 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return sendit;
|
|
|
|
};
|
|
|
|
|
2023-12-07 12:04:19 +01:00
|
|
|
Notifications.sendmail = async (data, tribe) => {
|
|
|
|
/**
|
2024-07-18 09:34:26 +02:00
|
|
|
* * 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"
|
|
|
|
* }
|
|
|
|
* }
|
2023-12-07 12:04:19 +01:00
|
|
|
* 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 ,
|
|
|
|
* @param {string} data.subject
|
|
|
|
* @param {string} data.html
|
|
|
|
* @param {string} data.text
|
|
|
|
* @param {string} [data.Cc] list of email in copy
|
|
|
|
* @param {string} [data.Bcc] list of email in hidden copy
|
2024-07-11 13:29:54 +02:00
|
|
|
* @param {string} [data.attachments] array of
|
2023-12-07 12:04:19 +01:00
|
|
|
* {filename:'filename.txt',content:'txt'},
|
|
|
|
* {filename:'img.svg',path:"https://....svg", contentType:'image/svg'}
|
|
|
|
* {filename:'img.svg',path:"https://....svg", contentType :'text/plain'}
|
|
|
|
* {filename:'img.png',path:"data:text/svg;base64.aGVsbG8gd29ybGQ="}
|
|
|
|
*
|
|
|
|
* @example data
|
2024-07-18 09:34:26 +02:00
|
|
|
* {"to":"wall-ants@ndda.fr",
|
2023-12-07 12:04:19 +01:00
|
|
|
* "subject":"Test",
|
|
|
|
* "html":"<h1>test welcome</h1>",
|
|
|
|
* "text":"test welcome",
|
|
|
|
* "attachments":[{filename:"text.txt",pathfile:"/media/phil/textA.txt","contenttype":"text/plain"}]
|
|
|
|
* }
|
|
|
|
* @return {object}
|
|
|
|
* { status: 200, ref:"pagans",msg:"",data: { } }
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
if (!conf.smtp || !conf.emailcontact) {
|
|
|
|
return {
|
|
|
|
status: 412,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingconf",
|
|
|
|
data: { tribe: tribe },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (!data.from) {
|
|
|
|
data.from = conf.emailcontact;
|
|
|
|
}
|
|
|
|
let missingk = [];
|
|
|
|
["from", "to", "subject", "html", "text"].forEach((k) => {
|
|
|
|
if (!data[k]) {
|
|
|
|
missingk.push(k);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (missingk.lenght > 0) {
|
|
|
|
return {
|
|
|
|
status: 428,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingdata",
|
|
|
|
data: { missingk: missingk },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let confsmtp = conf.smtp;
|
2024-03-19 09:10:07 +01:00
|
|
|
const conftribfile = `../../itm/${tribe}.json`;
|
2023-12-07 12:04:19 +01:00
|
|
|
if (fs.existsSync(conftribfile)) {
|
2023-12-29 13:38:47 +01:00
|
|
|
const conftrib = fs.readJSONSync(conftribfile);
|
2024-07-18 09:34:26 +02:00
|
|
|
if (!conftrib.emailcontact){
|
|
|
|
return {
|
|
|
|
status: 428,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingemailcontactinconf",
|
|
|
|
data: { tribe },
|
|
|
|
};
|
|
|
|
}
|
2023-12-07 12:04:19 +01:00
|
|
|
confsmtp = conftrib.smtp;
|
2024-07-18 09:34:26 +02:00
|
|
|
if (!data.from || data.from == conf.emailcontact) data.from = conftrib.emailcontact;
|
2023-12-07 12:04:19 +01:00
|
|
|
}
|
|
|
|
const transporter = await nodemailer.createTransport(confsmtp);
|
|
|
|
if (data.filelist) {
|
2023-12-29 13:38:47 +01:00
|
|
|
data.attachments = [];
|
2023-12-07 12:04:19 +01:00
|
|
|
let missingfile = [];
|
|
|
|
data.filelist.forEach((fo) => {
|
2023-12-29 13:38:47 +01:00
|
|
|
if (fs.existsSync(fo.pathfile)) {
|
2024-07-11 13:29:54 +02:00
|
|
|
} else {
|
|
|
|
missingfile.push(fo.pathfile);
|
|
|
|
}
|
2023-12-07 12:04:19 +01:00
|
|
|
});
|
|
|
|
if (missingfile.lenght > 0)
|
|
|
|
return {
|
|
|
|
status: 428,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "missingfile",
|
|
|
|
data: { missingfile: missingfile },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
//console.log("data:", data);
|
|
|
|
const res = await transporter.sendMail(data);
|
2024-07-18 09:34:26 +02:00
|
|
|
//console.log(res)
|
2023-12-07 12:04:19 +01:00
|
|
|
if (
|
|
|
|
res.accepted &&
|
|
|
|
data.to.split(",").reduce((acc, m) => acc && res.accepted.includes(m), true)
|
|
|
|
) {
|
|
|
|
data.accepted = res.accepted;
|
|
|
|
data.rejected = res.rejected;
|
|
|
|
return {
|
|
|
|
status: 200,
|
|
|
|
ref: "Notifications",
|
|
|
|
msg: "successfullsentemail",
|
|
|
|
data,
|
|
|
|
};
|
|
|
|
} else if (res.accepted && res.rejected) {
|
|
|
|
data.accepted = res.accepted;
|
|
|
|
data.rejected = res.rejected;
|
|
|
|
return { status: 410, ref: "Notifications", msg: "errsendmail", data };
|
|
|
|
} else {
|
|
|
|
data.errmailer = res.err;
|
|
|
|
return { status: 417, ref: "Notifications", msg: "errsendmail", data };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Notifications;
|