Disable email sending on a legacy Meteor server without redeployment of codes - meteor

We are facing a strange situation where we have an old legacy Meteor server that we are no longer able to deploy any new codes.
We are moving away from the old server to a new server.
We wanted to keep the old server running as a "reference", but there are cronjobs that still regularly send out emails to clients in the database.
We have to stop these emails from being sent out, without restarting the server or redeploying the codes if possible. Is there any way to do it?
p/s: This is how the server is sending out emails using the Email Meteor package:
Meteor.methods({
sendEmail: function(options) {
// compile the template
var template = _.template(Assets.getText(options.template));
if (Meteor.DEBUG) {
Email.send({
to: Meteor.EMAIL_ADMIN,
from: options.from || Meteor.REPLYTO,
subject: options.subject || '',
html: template(options.data)
});
} else {
Email.send({
to: options.to,
from: options.from || Meteor.REPLYTO,
subject: options.subject || '',
html: template(options.data)
});
}
},

Related

Service Worker - Push notification with VAPID prv/pub keys

A couple of years ago I implemented push notification with service worker on a project I was working on, by registering an app on Firebase, and using the registration number as part of the manifest.json file on the server side app. In that case I requested the user to allow notifications, got the browser registration once, saved on server side, and all works fine.
I'm now trying to implement a similar solution, but using the VAPID (https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications#using_vapid).
Browser registers correctly, sends the registration to the server side app, and the app is able to send push notifications.
The issue I got is that after at least 24 hours, when I try to send a push notification to an already registered subscription, I get InvalidSubscription response (410 NotRegistered).
Using VAPID, does the browser registration expire after a few hours? do I need to get new registration every certain amount of hours? If yes, how? For example, if user never revisits the site within a day or so, how am I able to keep sending them notifications? I can't find any clear reference for this issue I'm experiencing.
Here is the JS code I use within the SW to get the browser registration:
function postPushReg(sub){
var rawKey = sub.getKey ? sub.getKey('p256dh') : '';
var key = rawKey ?
btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) :
'';
var rawAuthSecret = sub.getKey ? sub.getKey('auth') : '';
var authSecret = rawAuthSecret ?
btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) :
'';
fetch('https://XXXXX', {
method: 'post',
headers: {'Content-type': 'application/json'},
body: JSON.stringify({endpoint: sub.endpoint, key: key, authSecret: authSecret}),
});
}
self.addEventListener('install', function(event){
self.registration.pushManager.getSubscription()
.then(function(sub){
if (sub) return postPushReg(sub);
return self.registration.pushManager.subscribe({userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array('XXX')})
.then(function(sub){
postPushReg(sub);
});
});
});
self.addEventListener('push', function(e){
...
});
This is the Rails/Ruby server side gem (webpush) I use to send the notification:
Webpush.payload_send(
message: "msg",
endpoint: j['endpoint'],
p256dh: j['key'],
auth: j['authSecret'],
vapid: {
subject: "mailto:XXXX",
public_key: "XXX",
private_key: "XXX",
}
)
Again, within the first few hours everything works, then I get 410 NotRegistered response.
Trying the same suggestion posted here: Web Push with VAPID: 400/401 Unauthorized Registration , it is now working fine. I get the browser registration only once, and after 2 days it is still working fine

Insert new collection after function runs on server

When I return the geocode from googles API I'm trying to save it into my database. I've been trying to use the code below, to just insert a Test document with no luck. I think it has something to do with meteor being asynchronous. If I run the insert function before the googleMapsClient.geocode function it works fine. Can someone show me what I'm doing wrong.
Meteor.methods({
'myTestFunction'() {
googleMapsClient.geocode({
address: 'test address'
}, function(err, response) {
if (!err) {
Test.insert({test: 'test name'});
}
});
}
});
I see now where you got the idea to run the NPM library on the client side, but this is not what you really want here. You should be getting some errors on the server side of your meteor instance when you run the initial piece of code you gave us here. The problem is that the google npm library runs in it's own thread, this prevents us from using Meteor's methods. The easiest thing you could do is wrap the function with Meteor.wrapAsync so it would look something like this.
try {
var wrappedGeocode = Meteor.wrapAsync(googleMapsClient.geocode);
var results = wrappedGeocode({ address : "testAddress" });
console.log("results ", results);
Test.insert({ test : results });
} catch (err) {
throw new Meteor.Error('error code', 'error message');
}
You can find more info by looking at this thread, there are others dealing with the same issue as well
You should run the googleMapsClient.geocode() function on the client side, and the Test.insert() function on the server side (via a method). Try this:
Server side
Meteor.methods({
'insertIntoTest'(json) {
Test.insert({results: json.results});
}
});
Client side
googleMapsClient.geocode({
address: 'test address'
}, function(err, response) {
if (!err) {
Meteor.call('insertIntoTest', response.json);
}
});
Meteor Methods should be available on the both the server and client sides. Therefore make sure that your method is accessible by server; via proper importing on /server/main.js or proper folder structuring.
(If a method contains a secret logic run on the server, it should be isolated from the method runs on both server & client, though)

SignalR: getting error: WebSocket closed

I am working on project of Ionic with angular and AspNet with SignalR that have chat module.
I use SignalR for Chat.It's working smoothly but some time i am getting error as per below screen shot and because of that it's get stop working at all.
I have hosted my service on IIS and creating proxy and communicating with client and server. Here is sample
(function () {
angular
.module('app')
.factory('SignalRFactory', SignalRFactory);
SignalRFactory.$inject = ['$rootScope', 'Hub', 'ionicToast'];
function SignalRFactory($rootScope, Hub, ionicToast) {
var signalRLocal = this;
var serverURL = 'https://serivcerURL.com/signalr';
//Hub setup
var hub = new Hub('CommunicationHub', {
rootPath: serverURL,
listeners: {
'send': function (data) {
console.log("send " + data);
}
},
errorHandler: function (error) {
//Here i am getting that websocket closed error
console.error(error);
}
});
signalRLocal.Connect = function (user) {
console.log("SignalR Connecting as :" + user.UserName);
hub.invoke('connect', user);
};
return signalRLocal;
}
})();
I have hosted service on IIS. I search for the solution and find something like this link
I also try with above link solution by using "long Polling" as per below
Hub.connection.start({ transport: 'longPolling' });
But i don't want to use "long Polling" at all.
So can someone help me to figure out this issue without use of 'long Polling'.
Can someone tell me what configuration i have to do at client side or at IIS level.
As we said in comments, SignalR client will try to reconnect after the connection is lost. Besides, many factors (such as physical network interruption, client browser failure, server offline etc) can cause the connection lost, this article explains some disconnection scenarios, you can refer to it and find the possible causes of the issue.
Besides, as I mentioned in comment, you can call the Start method from your Closed event handler (disconnected event handler on JavaScript clients) to start a new connection to make client automatically re-establish a connection after it has been lost.
edit:
The connection to ws://localhost:3156/signalr/signalr/connect?transport=webSoc‌​kets&clientProtocol=‌​1.5&connectionToken=‌​g8vpRv9ncVDjPIYB9UuE‌​pAAILEaOcTMTG9p46IA2‌​4 was interrupted while the page was loading.
Under "Client disconnection scenarios" section in the article, you can find:
In a browser client, the SignalR client code that maintains a SignalR connection runs in the JavaScript context of a web page. That's why the SignalR connection has to end when you navigate from one page to another, and that's why you have multiple connections with multiple connection IDs if you connect from multiple browser windows or tabs. When the user closes a browser window or tab, or navigates to a new page or refreshes the page, the SignalR connection immediately ends because SignalR client code handles that browser event for you and calls the Stop method.
I am trying same and my client is in vue.js. I have changed below in vue.config.js
module.exports = {
devServer: {
proxy: {
'/hub': {
target: 'https://localhost:5001',
changeOrigin: false,
secure: false,
headers: {
'x-forwarded-proto': 'http',
},
},
},
},
}
Previously i am trying 'x-forwarded-proto': 'https', when I changed to http its work.
https://localhost:5001 is my .net endpoint and http://localhost:8080 is my vue enpoint
Changed in Startup.csbelow,
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<CardsHub>("/hub/cardsHub");
});
Added below code in vue component
this.connection = new signalR.HubConnectionBuilder()
.withUrl('/hub/cardsHub')
.build()
In my case the culprit was the wrong version of Microsoft.AspNetCore.SignalR.Common
the default one installed was 5.0.3
but I was targeting 3.1.0
Downgrading to 3.1.12 fixed the issue with the connection.

DDP.connect and Meteor.users

I am in the process to try to separate out the mobile from the desktop part of my application and thought I try DDP.connect as a means for the mobile application to share data with the desktop application.
My first hurdle is concerning Meteor internal collections and publications.
How am I supposed to authenticate users? I know I can call the login method to authenticate a user, but that still doesn't give me all the other nice reactive features I am used to with Meteor.users
Is this supposed to work, and if so what is the pattern.
Thanks
This is what integrated completely with a remote server (except code refresh, which forgets user session)
if (Meteor.isClient) {
Meteor.connection = DDP.connect('http://remote.site.com');
Accounts.connection = Meteor.connection;
Meteor.users = new Meteor.Collection('users');
SomeCollection = new Meteor.Collection('remote_collection');
Meteor.connection.subscribe('users');
Meteor.connection.subscribe('remote_collection');
// rest if the code just as always
}
This way you can use login directly (via accounts-base, accounts-passed, etc) and don't need to call a login method. Just add accounts-ui and include {{>loginButtons}} and it works
I had a similar problem. I wanted to have two different front-ends (although both are for desktop) to the same back-end, so they could use same database, publications, and methods. After looking through Meteor's source code (version 1.1.0.3) I've managed to do this as follows.
1) Start back-end server project.
$ meteor --port 3100
2) In front-end project(s), put following in server/server.config.js.
var backendUrl = process.env.BACKEND_URL;
if (backendUrl) {
__meteor_runtime_config__.BACKEND_URL = backendUrl;
__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL = backendUrl;
console.log('config', __meteor_runtime_config__);
}
3) In front-end project(s), put following in client/lib/client.connection.js. APS is just a namespace for my application. Be sure to have this loaded before you use subscriptions or methods (that's why it's in lib folder).
if (typeof APS == 'undefined') APS = {};
var backendUrl = __meteor_runtime_config__.BACKEND_URL;
if (backendUrl) {
APS.backendConnection = DDP.connect(backendUrl);
Meteor.connection = APS.backendConnection;
_.each(['subscribe', 'methods', 'call', 'apply', 'status', 'reconnect', 'disconnect'], function(name) {
Meteor[name] = _.bind(Meteor.connection[name], Meteor.connection);
});
console.log('connected to backend', APS.backendConnection);
}
4) Start front-end server with BACKEND_URL environment variable pointing to your back-end server.
$ BACKEND_URL=http://192.168.33.10:3100 meteor
That's all. Refresh on client works OK. And we don't have to fiddle with Accounts.*.
UPDATE: Just found a problem with my solution. When calling server methods, this.userId is always null. This is because Meteor.connection and Accounts.connection were two separate connections, despite to the same BACKEND_URL. Upon authentication, user ID gets associated only with the latter. Fixed client.connection.js is as follows.
if (typeof APS == 'undefined') APS = {};
var backendUrl = __meteor_runtime_config__.BACKEND_URL;
if (backendUrl) {
APS.originalConnection = Meteor.connection;
// Accounts is already connected to our BACKEND_URL
APS.backendConnection = Accounts.connection;
// Reusing same (authenticated) connection for method calls and subscriptions
Meteor.connection = APS.backendConnection;
_.each(['subscribe', 'methods', 'call', 'apply', 'status', 'reconnect', 'disconnect'], function(name) {
Meteor[name] = _.bind(Meteor.connection[name], Meteor.connection);
});
console.log('Connected to backend', APS.backendConnection);
}
You can authenticate using code like this:
var connection = DDP.connect("<url>")
To authenticate
connection.call("login", {"password":"qwerty","user":{"username":"user_1"}});
to get the user, add this on the other server)
Meteor.methods({
whoami: function() { return Meteor.user() }
});
Then you can run further commands as if you were authenticated, like this to get who's logged in
console.log(connection.call("whoami");
User account creation/Authentication:
In client.js, create a DDP connection and set it to Accounts.connection
Accounts.connection = Meteor.remoteConnection;
Create an Accounts.users collection in the client and subscribe its contents from the external server as below.
Accounts.users = new Meteor.Collection('users', {connection: Meteor.remoteConnection});
Meteor.remoteConnection.subscribe('users');
Now call the login method required as below and set the token returned in the localStorage. This works for all the internal clicks and routing.
Meteor.loginWithPassword(login_email, login_password, function(err) {
submit_button.button("reset");
if (err)
{
console.log(err);
pageSession.set("errorMessage", err.message);
return false;
}else{
console.log("logged in as "+Meteor.userId());
var token = Accounts._storedLoginToken();
localStorage.setItem('_storedLoginToken', token);
}
});
The problem with the above code is that, the token is reset after every manual client refresh. The result object contains the below signed in information. We have to take the token and login with token for every external client refresh.
id:"5RigABaSzbARHv9ZD"
token:"MItg8P59gsl_T5OXtaWRSjUnETqzns0hGEV26xWYxj7"
tokenExpires:Thu Jul 20 2017 12:46:31 GMT+0530 (India Standard Time)
In client.js, start-up call the loginwithtoken function with the returned token as below, whenever there is no user available.
var user = Meteor.user();
var token = localStorage.getItem('_storedLoginToken');
if(user==null){
console.log("Token"+token +user);
if(token)
Meteor.loginWithToken(token, function(err){
// this is going to throw error if we logged out
if(!err) {
console.log('logged in !!!! ',token);
}
});
}
Meteor throws an error while logging in with the token,
Error logging in with token: Error: You've been logged out by the server. Please log in again. [403]
To overcome this issue, we have to write a tracker function to track the logged in session and login again if required. This is basically a hack suggested in meteor forums.
Tracker.autorun(function () { var user = Meteor.user(); var token
= localStorage.getItem('_storedLoginToken'); if(user==null){ console.log("Token"+token +user); if(token)
Meteor.loginWithToken(token, function(err){
// this is going to throw error if we logged out
if(!err) {
console.log('logged in !!!! ',token); }
}); } });
Reset the localStorage if user navigates to the login path. In Layout.js,
if(path=='/login')
localStorage.setItem('_storedLoginToken',null);

Linking Robomongo to an automatic email sending service?

I have an application running on meteor.js and mongo.db. I am using robomongo as a tool for mongo.db. Now I'd like to do the following:
1. Somebody registers with my service (adding email to db)
2. I want to send an automatic welcome email to that person.
Is there any possibility how to do it?
You need an email server (SMTP), and then use the meteor email library. If you don't have an email server and don't want to create one, use a commercial solution. (Example)
Full working example you can find here: http://meteorpad.com/pad/iNMBHtNsv7XKHeq44
Notice it creates new users from within Meteor app, but the same effect will be when you use Robomongo or any other way of updating MongoDB.
First install package Email to be able to use Email.send.
In below example I assume that adding new user to collection Meteor.users should fire sending "invitation" email.
In very similar way you can detect if email was added to user object
(user.emails.length was changed) and then send email.
Then take a look at code:
// SERVER SIDE CODE:
Meteor.startup(function () {
// clean users on app resetart
// Meteor.users.remove({});
if(Meteor.users.find().count() === 0){
console.log("Create users");
Accounts.createUser({
username:"userA",
email:"userA#example.com",
profile:{
invitationEmailSend:false
}
}) ;
Accounts.createUser({
username:"userB",
email:"userB#example.com",
profile:{
invitationEmailSend:false
}
})
}
Meteor.users.find().observe({
added:function(user){
console.log(user.username, user.profile.invitationEmailSend)
if(!user.profile.invitationEmailSend){
Email.send({
from: "from#mailinator.com",
to: user.emails[0].address,
subject: "Welcome",
text: "Welcome !"
});
// set flag 'invitationEmailSend' to true, so email won't be send twice in the future ( ex. during restart of app)
Meteor.users.update({_id:user._id},{$set:{"profile.invitationEmailSend":true}});
}
}
})
});
Above code will send email to users who don't have flag equal to true in profile.invitationEmailSend. After e-mail is sent server updates user document in db and set user.profile.invitationEmailSend to true.
Whenever you add users to mongoDB (using Robomongo or any other way), then added function is executed and e-mail is send only to new users.

Resources