Meteor signaling without db write - meteor

I've been looking for a good way to do, but haven't found anything that doesn't seem hacky. I want to signal the client without going through the database and a subscription. For example, in a game I want to send a message to the client to display "Player 1 almost scores!". I don't care about this information in the long run, so I don't want to push it to the DB. I guess I could just set up another socket.io, but I'd rather not have to manage a second connection if there is a good way to go it within meteor. Thanks! (BTW, have looked at Meteor Streams, but it appears to have gone inactive)

You know that Meteor provides real-time communication from the server to clients through Publish and Subscribe mechanism, which is typically used to send your MongoDB data and later modifications.
You would like a similar push system but without having to record some data into your MongoDB.
It is totally possible re-using the Meteor Pub/Sub system but without the database part: while with Meteor.publish you typically return a Collection Cursor, hence data from your DB, you can also use its low-level API to send arbitrary real-time information:
Alternatively, a publish function can directly control its published record set by calling the functions added (to add a new document to the published record set), changed (to change or clear some fields on a document already in the published record set), and removed (to remove documents from the published record set). […]
Simply do not return anything, use the above mentioned methods and do not forget calling this.ready() by the end of your publish function.
See also the Guide about Custom publications
// SERVER
const customCollectionName = 'collection-name';
let sender; // <== we will keep a reference to the publisher
Meteor.publish('custom-publication', function() {
sender = this;
this.ready();
this.onStop(() => {
// Called when a Client stops its Subscription
});
});
// Later on…
// ==> Send a "new document" as a new signal message
sender.added(customCollectionName, 'someId', {
// "new document"
field: 'values2'
});
// CLIENT
const signalsCollectionName = 'collection-name'; // Must match what is used in Server
const Signals = new Mongo.Collection(signalsCollectionName);
Meteor.subscribe('custom-publication'); // As usual, must match what is used in Server
// Then use the Collection low-level API
// to listen to changes and act accordingly
// https://docs.meteor.com/api/collections.html#Mongo-Cursor-observe
const allSignalsCursor = Signals.find();
allSignalsCursor.observe({
added: (newDocument) => {
// Do your stuff with the received document.
}
});
Then how and when you use sender.added() is totally up to you.
Note: keep in mind that it will send data individually to a Client (each Client has their own Server session)
If you want to broadcast messages to several Clients simultaneously, then the easiest way is to use your MongoDB as the glue between your Server sessions. If you do not care about actual persistence, then simply re-use the same document over and over and listen to changes instead of additions in your Client Collection Cursor observer.

It's completly fine to use the database for such a task.
Maybe create a collection of "Streams" where you store the intended receiver and the message, the client subscribe to his stream and watches any changes on it.
You can then delete the stream from the database after the client is done with it.
This is a lot easier than reinventing the wheel and writing everything from scratch.

Related

Using the nativescript-plugin-firebase add an Event Listener with a Query

I am building a chat inside my app using the nativescript-plugin-firebase for nativescript with firebase realtime database. I need to listen only messages sent to a user's account.
Is it possible to use the listener addChildEventListener with a query so to listen only specific messages (in this case the sent to a specific user_id)?
I could filter in the app the messages but that would not work since the amount of messages will be huge.
I know this is possible with Firebase, but, I'm using nativescript-plugin-firebase
firebase.addChildEventListener(this.onMessagesChanged, "/chats").then(
function(listenerWrapper) {
var path = listenerWrapper.path;
var listeners = listenerWrapper.listeners; // an Array of listeners added
// you can store the wrapper somewhere to later call 'removeEventListeners'
}
)
The idea is listen only to the /chats that are relevant for a user instead of all.
Thanks!
Adding sub collections was the answer as stated by #Manoj

Check if a user is currently online to meteor server

I'd like to determine if a user is currently "online" or connected to the Meteor server.
I need this information before I send the user message, If the user is not connected I'd like to send the message via email.
I know that for traditional web applications that are totally state-less the definition of "online" use is a bit not clear but since modern web frameworks rely on websocket, a user is supposed to be online if a websocket is open.
The question is does Meteor include a method to determine if a user is connected or not?
Summarized: yes, there is such a mechanism.
There are for example package, that store the active login connections of the users with the meteor server and make them available either via an own collection or as part of the user profile.
See: https://github.com/dburles/meteor-presence
(Creates a new collection, called Presences)
or https://github.com/dan335/meteor-user-presence/
(Creates a user's profile entry, called presence. However, has also a collection to store and update the information in the background)
or https://github.com/mizzao/meteor-user-status
(Thanks to blueren in the comments)
Code example (from the first listed package)
Meteor.onConnection(function(connection) {
// console.log('connectionId: ' + connection.id);
Presences.insert({ _id: connection.id });
connections[connection.id] = {};
tick(connection.id);
connection.onClose(function() {
// console.log('connection closed: ' + connection.id);
expire(connection.id);
});
});
If you don't want to rely on the packages you may make use of that mechanism yourself.
See: https://docs.meteor.com/api/connections.html#Meteor-onConnection

How to reactively run something on the server in Meteor?

It seems that most of the reactivity is on the client side.
I've got a server function that I need to run based on the number of items in a collection. The function creates a schema for an OrderSubmissions collection based on the number of documents in Services, which changes.
On the client this is easy - I simply do
/lib/schemas
if(Meteor.isClient){
Tracker.autorun(function(){
Meteor.subscribe('services', function(){
// re-creates OrderSubmissions schema any time the Services subscription returns different data
});
});
};
I could also rig up something with Session or ReactiveVar. Unfortunately, all of these techniques are only available to the client. I need to do:
/lib/schemas
if(Meteor.isServer){
// re-creates OrderSubmissions schema any time Services collection changes
};
Is the only way to do this to use .observe? It seems like kind of an expensive thing to do. My Services collection will change very very infrequently (in fact, at this point all I want to do is create the OrderSubmissions schema in /lib/schemas when my /server/fixtures.js is done loading stuff into the Services collection.)
Unfortunately for my case, Meteor loads stuff in /lib before /server, so my schema in /lib is being created erroneously:
/lib/schemas.js runs and OrderSubmissions schema, which depends on Services to be populated, gets created erroneously on both the server and client because there is nothing in the Services collection.
/server/fixtures.js runs and populates the Services collection.
I need #2 to happen before #1, but #2 needs to stay in server code. I don't want to wrap it in if(Meteor.isServer) in /lib since it's not secure.
If you add peerlibrary:server-autorun
You could do the following on the server
Tracker.autorun(function(){
// this code now reruns on the server if there is a reactive dependency enclosed here
})

Meteor: Difference between using PubSub and Collection.find(options) on the client

I have seen developers subscribe to data published by the server and passing in options to modify the returned cursor. I have also seen developers who just directly call Collection.find(options) directly from the client files.
Is there a difference between the two approaches? If so, which is the recommended method?
Both are complementary.
In order to fully understand how Meteor works with data you have to forget how a traditional app works (i.e. request data and get them in a static form (like json)).
TL;DR Do not forget that Meteor is DB everywhere
Meteor data reactivity works like this:
1) Publication: You have some data in your MongoDB (server) that you want to be available on the client, said last 10 articles over a total of 500. So, you will set a publication on the server like this:
//SERVER
Meteor.publish('lastTen', function() {
return Articles.find({}, {limit: 10, sort: {date: -1}});
});
2) Subscription: To actually make the data available on the client you will subscribe to the publication like this on the client:
//CLIENT
Meteor.subscribe('lastTen');
3) Fetch data on the client: data you have subscribed to in 2) are not send statically to the client, they are instead replicated in a client side DB (currently MiniMongo). That is why to fetch the data and display them to the user you will have to query against this DB on the client side:
//CLIENT
Articles.find({}, {sort: {date: 1}});
In this case there is no need to limit your query as you only have 10 records on the client. Also, you can sort in another order as it is a DB and not static data.
Be careful, by default the autopublish package is installed to allow fast prototyping. It will publish all the data on the client, so you don't need any publish/subscribe mechanism and it may be confusing.

How do you secure the client side MongoDB API?

I don't want just all of my users being able to insert/destroy data.
While there is no documented way to do this yet, here's some code that should do what you want:
Foo = new Meteor.Collection("foo");
...
if (Meteor.is_server) {
Meteor.startup(function () {
Meteor.default_server.method_handlers['/foo/insert'] = function () {};
Meteor.default_server.method_handlers['/foo/update'] = function () {};
Meteor.default_server.method_handlers['/foo/remove'] = function () {};
});
}
This will disable the default insert/update/remove methods. Clients can try to insert into the database, but the server will do nothing, and the client will notice and remove the locally created item when the server responds.
insert/update/remove will still work on the server. You'll need to make methods with Meteor.methods that run on the server to accomplish any database writes.
All of this will change when the authentication branch lands. Once that happens, you'll be able to provide validators to inspect and authorize database writes on the server. Here's a little more detail: http://news.ycombinator.com/item?id=3825063
[UPDATE] There is now an official and documented Auth Package which provides different solutions to secure a collection.
On a CRUD level :
[Server] collection.allow(options) and collection.deny(options). Restricts default write methods on this collection. Once either of these are called on a collection, all write methods on that collection are restricted regardless of the insecure package.
And there is also insecureto remove full write access from the client.
source : Getting Started with Auth (thanks to #dan-dascalescu)
[OLD ANSWER]
Apparently there are working on Auth Package(?) that should avoid any users taking full control on the db as it is now. There is also someone suggesting that there is an existing solution (workaround) by defining your own mutations (methods) and make them failed if they attempts to perform an unauthorized action. I didn't get it much better but I think this will often be necessary since I doubt the Auth Package will let you implement the usual auth logic on a row level but probably only on the CRUD methods. Will have to see what the devs have to say.
[EDIT]
Found something that seems to confirm my thoughts :
Currently the client is given full write access to the collection. They can execute arbitrary Mongo update commands. Once we build authentication, you will be able to limit the client's direct access to insert, update, and remove. We are also considering validators and other ORM-like functionality.
Sources of this answer :
Accessing to DB at client side as in server side with meteor
https://stackoverflow.com/questions/10100813/data-validation-and-security-in-meteor/10101516#10101516
A more succinct way:
_.each(['collection1', 'collection2'], function(collection){
_.each(['insert','update', 'remove'], function(method){
Meteor.default_server.method_handlers['/' + collection + '/' + method] = function(){}
});
});
or to make it more idiomatic:
extend meteor:
_.extend(Meteor.Collection.prototype, {
remove_client_access: function(methods){
var self = this;
if(!methods) methods = ['insert','update','remove'];
if(typeof methods === 'String') methods = [methods];
_.each(methods, function(method){
Meteor.default_server.method_handlers[self._prefix + method] = function(){}
});
}
});
Calls are simpler:
List.remove_client_access() // restrict all
List.remove_client_access('remove') //restrict one
List.remove_client_access(['remove','update']) //restrict more than one
I am new to Meteor, but what I have come across so far are these two points
You can limit what a client can access in the database by adding parameters to the find command in the server-side publish command. Then when the client calls Collection.find({}), the results that are returned correspond to what on the server side would be, for example, Collection.find({user: this.userId}) (see also Publish certain information for Meteor.users and more information for Meteor.user and http://docs.meteor.com/#meteor_publish)
One thing that is built in (I have meteor 0.5.9) is that the client can only update items by id, not using selectors. An error is logged to console on the client if there is an attempt that doesn't comply. 403: "Not permitted. Untrusted code may only update documents by ID." (see Understanding "Not permitted. Untrusted code may only update documents by ID." Meteor error).
In view of number 2, you need to use Meteor.methods on the server side to make remote procedure calls available to the client with Meteor.call.

Resources