Meteor server-side-only session - meteor

Is there a way to store information specific to the current connection on the server like a session?
something like would be usefull
if(Meteor.isServer){
Session.set('something-only-server-side-for-current-connection', 'hello');
}
Thank you.

this.connection is available in both publish functions, and meteor methods.
Create another (unpublished) collection on the server (or you can use another key-value store) to store associated parameters with the connection. Create a onStop callback to cleanup the server side collection (or do this periodically).
erasaur:server-session is a package that looks to be doing something very similar to the above.

I would create a collection called UserSessions (or similar) and put whatever you need into it indexed by Meteor.default_connection._lastSessionId which turns out to be the current sessionId [reference].
UserSessions.insert({ sessionId: Meteor.default_connection._lastSessionId,
createdAt: new Date(), key1: value, key2: value, ...});
Make sure you index-unique sessionId for performance. You can also purge old sessions from time-to-time based on the createdAt timestamp.

Related

How can I avoid an infinite loop in my meteor router?

I'm building an online store in meteor where customers can customize products in the store. I have setup a client-only collection called Inventory which stores all the product data and is updated accordingly in response to user input. Once the user is ready to checkout, I dump the product data into a client & server side collection called ShoppingCart. I want to allow users to go back and revise their edits on the product in Inventory so I setup my router to $set data from the ShoppingCart into Inventory if it finds a match:
Router.route '/:_type/:_id', ->
Session.set "inCart", false
#render #params._type,
data: =>
storedItem = ShoppingCart.findOne {
userId: Meteor.userId(),
image: #params._id
}
if storedItem?
delete storedItem._id
Inventory.update {image: #params._id}, {
$set: storedItem
}
Inventory.findOne image: #params._id
EDIT: This seems to cause my router method to get stuck in an infinite loop whenever data in Inventory changes. Is there any way to avoid this issue? Is there a better way of handling this kind of data altogether that I should consider?
MAJOR CAVEAT - I don't do CoffeeScript, so this is what I can gather having put your code through a compiler.
I think the problem is that the data function is reactive, and you're updating and returning an item from the Inventory collection within it. Every time the route runs, unless there is no storedItem, it's going to invalidate a computation on which it itself depends and thus rerun again immediately (and subsequently do the same again, etc...).
As a general rule, I think it's a very bad idea indeed to be updating a collection from within a data function - if you have to do this within the route function, consider the onRun, or onBeforeAction hooks for the update.
Final thing, just because I don't understand: why do you need to dump the item from the ShoppingCart back into Inventory? Shouldn't it already be there, unless the user has started a new session?

Meteor - why can't I store my Meteor.subscribe handle in the Session? I need to get it later to stop the subscription

I have the following code:
sub_geo = Meteor.subscribe('geo');
console.log('sub_geo returned from Meteor.subscribe: ');
console.log(sub_geo);
Session.set('sub_geo', sub_geo);
console.log('sub_geo retrieved from Session: ');
console.log(Session.get('sub_geo'));
The output is as follows:
sub_geo returned from Meteor.subscribe:
> Object {stop: function, ready: function}
sub_geo retrieved from Session:
> Object {}
Obviously I need to store the returned subscription handle because I need to call the ready() and stop() functions on it later. Not sure how else to store it other than in the Session. Can I just use a global variable? Also - even if there is some other way of doing it, why doesn't this approach work?
It doesn't work because you can only store EJSON-serializable objects in session variables. If you need the subscription handle outside of the current file, you'll need to store it in a global variable (perhaps under a namespace like Subscriptions - e.g. see richsilv's answer to this question.
You can actually do this, but not the way you would expect.
Just set the Session as you do:
Session.set('sub_geo', sub_geo);
Then when you want to do something with your subscription, retrieve the handle from Meteor's own registry:
const handle = Meteor.default_connection._subscriptions[Session.get('sub_geo').subscriptionId]
And if you ARE going to use the Session (which is not encouraged, except if you really have to), clean it up:
delete Session.keys['sub_geo'];
This is working fine in Meteor 1.3.3.1 for me at the moment, but probably will work for most previous versions as well.
PS: Alternatives for Session are Reactive Var and Reactive Dict.

Accounts.createUser create users, but only from the server and not allow the client to create user

first excuse my writing, I'm using google translator.
What I want is to create users meteor, but only from the server and not allow the client to create users.
try putting
Accounts.validateNewUser (function () {
return false;
});
but it denies me create users to the server (which I do not want)
thank you very much in advance.
You can use Accounts.config for that purpose. Just anywhere in your server code do
Accounts.config({
forbidClientAccountCreation: true,
});
One way to do this would be to add some kind of un-guessable string to the profile key when you create a user on the server, check for that key in the validateNewUser function and then remove it immediately using observe on an appropriate cursor.
On the server you would have:
Accounts.validateNewUser(function(user) {
return (user.profile && user.profile.createUserKey === [KEY]);
});
Meteor.users.find().observe({
added: function(user) {
Meteor.users.update(user, {$unset: {'profile.createUserKey': true}});
}
});
And then your server-side createUser line looks like this (along with any other data you want to pass in the profile):
Accounts.createUser({username: [USERNAME], password: [PASSWORD], profile: {createUserKey: [KEY]}});
Assuming you don't publish the validation function anywhere (like Github), a client will have no way of knowing what the key is and would thus be unable to create a new user. If you don't trust yourself not to push it to Github or similar, store the key in an unpublished collection in your MongoDB and then pull it out on server start-up - that's what I do.
NOTE I think it's slightly unsatisfactory that I'm using a cursor here as it would be far better to remove the key in an onCreateUser callback, but this seems to be called before validateNewUser, so it's no use here. The alternatives are using your own modified version of the createUser function in the Accounts package, which probably isn't too tough but is a little involved to go into here, or just leaving the key in the user document and making sure you don't publish it.

Update document in Meteor mini-mongo without updating server collections

In Meteor, I got a collection that the client subscribes to. In some cases, instead of publishing the documents that exists in the collection on the server, I want to send down some bogus data. Now that's fine using the this.added function in the publish.
My problem is that I want to treat the bogus doc as if it were a real document, specifically this gets troublesome when I want to update it. For the real docs I run a RealDocs.update but when doing that on the bogus doc it fails since there is no representation of it on the server (and I'd like to keep it that way).
A collection API that allowed me to pass something like local = true this would be fantastic but I have no idea how difficult that would be to implement and I'm not to fond of modifying the core code.
Right now I'm stuck at either creating a BogusDocs = new Meteor.Collection(null) but that makes populating the Collection more difficult since I have to either hard code fixtures in the client code or use a method to get the data from the server and I have to make sure I call BogusDocs.update instead of RealDocs.update as soon as I'm dealing with bogus data.
Maybe I could actually insert the data on the server and make sure it's removed later, but the data really has nothing to do with the server side collection so I'd rather avoid that.
Any thoughts on how to approach this problem?
After some further investigation (the evented mind site) it turns out that one can modify the local collection without making calls to the server. This is done by running the same methods as you usually would, but on MyCollection._collection instead of just on Collection. MyCollection.update() would thus become MyCollection._collection.update(). So, using a simple wrapper one can pass in the usual arguments to a update call to update the collection as usual (which will try to call the server which in turn will trigger your allow/deny rules) or we can add 'local' as the last argument to only perform the update in the client collection. Something like this should do it.
DocsUpdateWrapper = function() {
var lastIndex = arguments.length -1;
if (arguments[lastIndex] === 'local') {
Docs._collection.update(arguments.slice(0, lastIndex);
} else {
Docs.update(arguments)
}
}
(This could of course be extended to a DocsWrapper that allows for insertion and removals too.)(Didnt try this function yet but it should serve well as an example.)
The biggest benefit of this is imo that we can use the exact same calls to retrieve documents from the local collection, regardless of if they are local or living on the server too. By adding a simple boolean to the doc we can keep track of which documents are only local and which are not (An improved DocsWrapper could check for that bool so we could even omit passing the 'local' argument.) so we know how to update them.
There are some people working on local storage in the browser
https://github.com/awwx/meteor-browser-store
You might be able to adapt some of their ideas to provide "fake" documents.
I would use the transform feature on the collection to make an object that knows what to do with itself (on client). Give it the corruct update method (real/bogus), then call .update rather than a general one.
You can put the code from this.added into the transform process.
You can also set up a local minimongo collection. Insert on callback
#FoundAgents = new Meteor.Collection(null, Agent.transformData )
FoundAgents.remove({})
Meteor.call 'Get_agentsCloseToOffer', me, ping, (err, data) ->
if err
console.log JSON.stringify err,null,2
else
_.each data, (item) ->
FoundAgents.insert item
Maybe this interesting for you as well, I created two examples with native Meteor Local Collections at meteorpad. The first pad shows an example with plain reactive recordset: Sample_Publish_to_Local-Collection. The second will use the collection .observe method to listen to data: Collection.observe().

How do I add a field to a document from the server in a publish without saving it?

I want to add and return a calculated field from a server Meteor.publish that doesn't actually persist in MongoDB. Is this possible? Something like where I format some markdown:
Meteor.publish('recentEdits', function(pageId) {
var edits, formattedContent;
edits = WikiEdits.find({pageId: pageId}, {sort: {ts: -1},
limit: RECENT_EDIT_LIMIT});
edits.forEach(function(edit) {
formattedContent = marked(edit.content);
edit.formattedContent = formattedContent;
});
return edits;
});
To the client it should seem like that formattedContent field came like any other, but it's not actually in MongoDB. Is this possible and if so what is the best way? Even if I should store the formattedContent, I would still like to know how to do this.
I tried using the transform option in Meteor.Collection and that only ran on the client, but I want this to happen from the server.
you can use the transform option of the find
the only problem is that it is also executed on the client. If you can live with that, it's a solution.
Another option could be to add an unmanaged collection on the client and get the contents from a Meteor method
The local unmanaged collection will not be stored in the MongoDB you can call the Meteor.method from an autorun and observe will work on the local collection.

Resources