notif: remove code migrated to tcef-notifications microservice
The push/email notification processing now lives in the tcef-notifications service (fases 1a-1c). Removed from the web: - imports/startup/server/notificationsObserver.js (observe Notifications -> processNotif) - imports/modules/server/notificationsProcess.js (node-gcm push + notif emails) - the 'Process pending notif' SyncedCron job in cron.js - node-gcm dependency (dead Google API since jun-2024) subsUnion.js is KEPT: it feeds the subs-public-union SiteSettings that the REST API (status/subs-public-union) serves to the Flutter app; it is not part of the notifications pipeline. DEPLOY ORDERING (see UPGRADE.md): production must not run this build until the tcef-notifications service is emitting in prod — otherwise notifications stop. REST smoke test green (byte-identical).
This commit is contained in:
parent
296dab4f38
commit
dc8a05a0d4
5 changed files with 5 additions and 199 deletions
|
|
@ -1,153 +0,0 @@
|
||||||
/* eslint-disable import/no-absolute-path */
|
|
||||||
|
|
||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import Notifications from '/imports/api/Notifications/Notifications';
|
|
||||||
import i18n from 'i18next';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { dateLongFormat } from '/imports/api/Common/dates';
|
|
||||||
// import sendMail from '/imports/startup/server/email';
|
|
||||||
import sendEmail, { subjectTruncate } from '/imports/modules/server/send-email';
|
|
||||||
import { isMailServerMaster } from '/imports/startup/server/email';
|
|
||||||
// import { hr } from '/imports/startup/server/email';
|
|
||||||
import getEmailOf from '/imports/modules/get-email-of-user';
|
|
||||||
import image from 'google-maps-image-api-url';
|
|
||||||
import gcm from 'node-gcm';
|
|
||||||
import { trim } from '/imports/ui/components/NotificationsObserver/util.js';
|
|
||||||
import ravenLogger from '/imports/startup/server/ravenLogger';
|
|
||||||
|
|
||||||
let validFcmSender = true;
|
|
||||||
|
|
||||||
if (!(Meteor.settings.private.fcmApiToken && Meteor.settings.private.fcmApiToken.length > 0)) {
|
|
||||||
console.warn('Missing settings.private.fcmApiToken key, mobile notifications will not work');
|
|
||||||
validFcmSender = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.npmjs.com/package/google-maps-image-api-url
|
|
||||||
// https://stackoverflow.com/questions/24355007/is-there-no-way-to-embed-a-google-map-into-an-html-email
|
|
||||||
// https://developers.google.com/maps/documentation/static-maps/intro
|
|
||||||
function imgUrl(lat, lng) {
|
|
||||||
return image({
|
|
||||||
key: Meteor.settings.gmaps.key,
|
|
||||||
type: 'staticmap',
|
|
||||||
center: `${lat},${lng}`,
|
|
||||||
size: '640x480',
|
|
||||||
zoom: 16,
|
|
||||||
maptype: 'hybrid',
|
|
||||||
language: 'es',
|
|
||||||
markers: `icon: ${Meteor.settings.private.fireIconUrl}|${lat},${lng}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
function imgEl(lat, lng) {
|
|
||||||
return `<img src="${imgUrl(lat, lng)}" width="640" height="480"/>`;
|
|
||||||
} */
|
|
||||||
|
|
||||||
// Cutover flag (fase 1a): channels listed in
|
|
||||||
// Meteor.settings.private.notifDisabledChannels (e.g. ['mobile','web']) are
|
|
||||||
// handled by the tcef-notifications microservice, so the old observer/cron must
|
|
||||||
// NOT send them. Mutual exclusion per channel — reversible by editing settings,
|
|
||||||
// no code deploy needed. ES5-compatible on purpose (Meteor 1.6 / Node 8).
|
|
||||||
const notifDisabledChannels = (Meteor.settings.private && Meteor.settings.private.notifDisabledChannels) || [];
|
|
||||||
|
|
||||||
const processNotif = (notif) => {
|
|
||||||
if (notifDisabledChannels.indexOf(notif.type) !== -1) {
|
|
||||||
// This channel was migrated to tcef-notifications; do nothing here.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isMailServerMaster && validFcmSender && notif.type === 'mobile' && notif.notified !== true) {
|
|
||||||
const fcmSender = new gcm.Sender(Meteor.settings.private.fcmApiToken);
|
|
||||||
const user = Meteor.users.findOne({ _id: notif.userId });
|
|
||||||
moment.locale(user.lang);
|
|
||||||
// duplicate code below
|
|
||||||
const body = `${trim(notif.content)}`;
|
|
||||||
|
|
||||||
// https://firebase.google.com/docs/cloud-messaging/concept-options
|
|
||||||
const msg = new gcm.Message();
|
|
||||||
|
|
||||||
msg.addNotification({
|
|
||||||
title: i18n.t('Alerta de fuego'),
|
|
||||||
body,
|
|
||||||
click_action: 'FLUTTER_NOTIFICATION_CLICK',
|
|
||||||
tag: notif._id, // prevent duplication of fire notifications
|
|
||||||
sound: 'default', // Indicates sound to be played. Supports only default currently.
|
|
||||||
icon: 'launch_image' // 'ic_launcher'
|
|
||||||
});
|
|
||||||
|
|
||||||
msg.addData('id', notif._id._str);
|
|
||||||
msg.addData('description', body);
|
|
||||||
msg.addData('lat', notif.geo.coordinates[1]);
|
|
||||||
msg.addData('lon', notif.geo.coordinates[0]);
|
|
||||||
msg.addData('when', notif.when);
|
|
||||||
msg.addData('subsId', notif.subsId._str);
|
|
||||||
msg.addData('sealed', notif.sealed);
|
|
||||||
|
|
||||||
const registrationTokens = [];
|
|
||||||
if (!user.fireBaseToken) {
|
|
||||||
console.warn('This mobile user doesn\'t have a firebase registration token');
|
|
||||||
} else {
|
|
||||||
registrationTokens.push(user.fireBaseToken);
|
|
||||||
// FIXME: better join users
|
|
||||||
if (validFcmSender) {
|
|
||||||
fcmSender.send(msg, { registrationTokens }, Meteor.bindEnvironment(function processResult(err, response) {
|
|
||||||
if (err) {
|
|
||||||
console.error(`FCM error: ${err}`);
|
|
||||||
// FIXME send to sentry
|
|
||||||
ravenLogger.log(err);
|
|
||||||
} else {
|
|
||||||
// console.log(`FCM response: ${response}`);
|
|
||||||
Notifications.update(notif._id, { $set: { notified: true, notifiedAt: new Date() } });
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isMailServerMaster && notif.type === 'web' && notif.emailNotified !== true) {
|
|
||||||
const user = Meteor.users.findOne({ _id: notif.userId });
|
|
||||||
const { firstName, emailAddress } = getEmailOf(user);
|
|
||||||
|
|
||||||
if (emailAddress) {
|
|
||||||
const img = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]);
|
|
||||||
// const url = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]);
|
|
||||||
const fireUrl = `${Meteor.absoluteUrl('fire/')}${notif.sealed}`;
|
|
||||||
// const fireHtmlUrl = `<a href="${fireUrl}">${i18n.t('Más información sobre este fuego')}</a>`;
|
|
||||||
// TODO get _id of fire
|
|
||||||
// const fireTextUrl = `${i18n.t('Más información sobre este fuego')}:\n${fireUrl}`;
|
|
||||||
// FIXME use our map as url and static map as img
|
|
||||||
moment.locale(user.lang);
|
|
||||||
// moment user tz ?
|
|
||||||
const message = `${trim(notif.content)} (${i18n.t('fireDetectedAt', { when: dateLongFormat(notif.when) })}).`;
|
|
||||||
|
|
||||||
// TODO Comunes Address
|
|
||||||
|
|
||||||
const emailOpts = {
|
|
||||||
to: emailAddress,
|
|
||||||
// userName: firstName,
|
|
||||||
// sendAt: new Date(),
|
|
||||||
subject: subjectTruncate.apply(message),
|
|
||||||
// text: `${message}\n\n${fireTextUrl}\n\n`,
|
|
||||||
// template: '<body><h2>{{appName}}</h2>{{{html}}}</body>',
|
|
||||||
lang: user.lang,
|
|
||||||
template: 'new-fire',
|
|
||||||
templateVars: {
|
|
||||||
applicationName: i18n.t('AppName'),
|
|
||||||
firstName,
|
|
||||||
message,
|
|
||||||
fireUrl,
|
|
||||||
img,
|
|
||||||
subsUrl: Meteor.absoluteUrl('subscriptions')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sendEmail(emailOpts).catch((error) => {
|
|
||||||
throw new Meteor.Error('500', `${error}`);
|
|
||||||
});
|
|
||||||
// sendMail(emailOpts, true);
|
|
||||||
Notifications.update(notif._id, { $set: { emailNotified: true, emailNotifiedAt: new Date() } });
|
|
||||||
} else {
|
|
||||||
// Not email or not verified -> remove notif so we don't retry to send it
|
|
||||||
Notifications.remove({ _id: notif._id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default processNotif;
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { tweetIberiaFires, tweetEuropeFires } from '/imports/api/ActiveFires/server/tweetFiresInZone';
|
import { tweetIberiaFires, tweetEuropeFires } from '/imports/api/ActiveFires/server/tweetFiresInZone';
|
||||||
import { isMailServerMaster, sendEmailsFromQueue } from '/imports/startup/server/email';
|
import { isMailServerMaster, sendEmailsFromQueue } from '/imports/startup/server/email';
|
||||||
import Notifications from '/imports/api/Notifications/Notifications';
|
|
||||||
import processNotif from '/imports/modules/server/notificationsProcess.js';
|
|
||||||
|
|
||||||
// https://github.com/thesaucecode/meteor-synced-cron/
|
// https://github.com/thesaucecode/meteor-synced-cron/
|
||||||
|
|
||||||
|
|
@ -55,29 +53,11 @@ Meteor.startup(() => {
|
||||||
job: () => sendEmailsFromQueue()
|
job: () => sendEmailsFromQueue()
|
||||||
});
|
});
|
||||||
|
|
||||||
SyncedCron.add({
|
// NOTE: the "Process pending notif" job (push via node-gcm + notification
|
||||||
name: 'Process pending notif',
|
// emails) was removed here — that responsibility now lives in the
|
||||||
timezone: 'Europe/Madrid',
|
// tcef-notifications microservice (fases 1a-1c). See UPGRADE.md for the
|
||||||
schedule: (parser) => {
|
// deploy-ordering dependency (the old code must not be shut off in
|
||||||
// http://bunkat.github.io/later/
|
// production until the new service is emitting).
|
||||||
const sched = parser.text('every 15 min');
|
|
||||||
if (sched.error !== -1) {
|
|
||||||
console.error(`Mail cron 'when' field parsed with errors: ${sched.error}`);
|
|
||||||
}
|
|
||||||
return sched;
|
|
||||||
},
|
|
||||||
job: () => {
|
|
||||||
const mobileNotif = Notifications.find({ nofitied: null, type: 'mobile' });
|
|
||||||
mobileNotif.forEach((notif) => {
|
|
||||||
processNotif(notif);
|
|
||||||
});
|
|
||||||
const emailNotif = Notifications.find({ emailNofitied: null, type: 'web' });
|
|
||||||
emailNotif.forEach((notif) => {
|
|
||||||
processNotif(notif);
|
|
||||||
});
|
|
||||||
return { mobile: mobileNotif.count(), web: emailNotif.count() };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const esEn = Meteor.settings.private.twitter.es.enabled;
|
const esEn = Meteor.settings.private.twitter.es.enabled;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import './fixtures';
|
||||||
import './email';
|
import './email';
|
||||||
import './IPGeocoder';
|
import './IPGeocoder';
|
||||||
import './migrations';
|
import './migrations';
|
||||||
import './notificationsObserver';
|
|
||||||
import './facts';
|
import './facts';
|
||||||
import './comments';
|
import './comments';
|
||||||
import './sitemaps';
|
import './sitemaps';
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/* eslint-disable import/no-absolute-path */
|
|
||||||
|
|
||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import Notifications from '/imports/api/Notifications/Notifications';
|
|
||||||
import processNotif from '/imports/modules/server/notificationsProcess.js';
|
|
||||||
import { isMailServerMaster } from '/imports/startup/server/email';
|
|
||||||
|
|
||||||
Meteor.startup(() => {
|
|
||||||
if (isMailServerMaster) {
|
|
||||||
Notifications.find().observe({
|
|
||||||
added: function notifAdded(notif) {
|
|
||||||
processNotif(notif);
|
|
||||||
},
|
|
||||||
changed: function notifChanged(updatedNotif) { // , oldNotif) {
|
|
||||||
processNotif(updatedNotif);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -49,7 +49,6 @@
|
||||||
"modernizr": "^3.6.0",
|
"modernizr": "^3.6.0",
|
||||||
"moment": "^2.19.1",
|
"moment": "^2.19.1",
|
||||||
"moment-timezone": "^0.5.14",
|
"moment-timezone": "^0.5.14",
|
||||||
"node-gcm": "^1.0.2",
|
|
||||||
"node-geocoder": "^3.21.1",
|
"node-geocoder": "^3.21.1",
|
||||||
"node-gyp": "^3.7.0",
|
"node-gyp": "^3.7.0",
|
||||||
"nodemailer": "^4.4.2",
|
"nodemailer": "^4.4.2",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue