Getting currentUser id from server - meteor

I'm trying to update a the user "photoPath" field after uploading a file using meteor-uploads.
I'm getting this error :
Error: Meteor code must always run within a Fiber. Try wrapping
callbacks that you pass to non-Meteor libraries with
Meteor.bindEnvironment.
server/init.js
validateFile: function(file, req) {
Meteor.users.update( { _id: Meteor.userId() }, {$set: {"profile.photoPath": req.path}});
return null;
}

you should use
this.userId
to access user from server side

Related

Meteor accounts password: TypeError: Accounts.setPassword is not a function

I am using:
Meteor Version 1.8,
accounts-password#1.5.1
When invoking:
Meteor.methods({
setPassword(newPassword, userId) {
check(userId, String);
check(newPassword, String);
if(Meteor.user().isAdmin){
Accounts.setPassword(userId, newPassword);
}
},
});
by
Meteor.call('setPassword', password, this.userId);
i get this error:
Exception while simulating the effect of invoking 'setPassword' TypeError: Accounts.setPassword is not a function
but the password is still set...
Meteor methods can run on both server and client side (see here). Here the error is coming from the client side : simulating the effect means the client is trying to compute an optimistic answer to your query to the server.
The Accounts object is available both client and server side, but I bet that the Accounts.setPassword function is only available in the server for security reasons.
To avoid the error, you can either : Place the meteor method definition in a server-only folder see here (like in this file app_code/imports/api/accounts/server/methods.js), or wrap it with if(Meteor.isServer) see here as such:
if(Meteor.isServer){
Meteor.methods({
setPassword(newPassword, userId) {
check(userId, String);
check(newPassword, String);
if(Meteor.user().isAdmin){
Accounts.setPassword(userId, newPassword);
}
},
});
}

Error invoking Method 'pushFile': Internal server error [500] on uploading with meteor

I am trying to upload a file to the server but I keep getting Error invoking Method 'pushFile': Internal server error [500] in the console. I am not exactly sure what is going on here. I am pretty much brand new to meteor and any help would be greatly appreciated.
Template.hello.events({
'change .fileInput': function(event, template){
event.preventDefault();
// var theName = event.target.theName.value;
console.log(theName);
FS.Utility.eachFile(event, function(file){
var fileObj = new FS.File(file);
fileObj.itemtext = theName;
Meteor.call("pushFile", fileObj);
});
}
});
}
if(Meteor.isServer){
Meteor.methods({
'pushFile': function(fileObj){
fileObj.userId = this.userId;
Uploads.insert(fileObj, function(err){
console.log(err);
});
}
});
}
the rest of the error is below:
I20151112-17:29:03.764(-5)? Exception while invoking method 'pushFile' Error: DataMan constructor received data that it doesn't support
I20151112-17:29:03.770(-5)? at EventEmitter.FS.Collection.insert (packages/cfs_collection/packages/cfs_collection.js:269:1)
I20151112-17:29:03.770(-5)? at [object Object].Meteor.methods.pushFile (uploadexample.js:39:21)
I20151112-17:29:03.769(-5)? at new DataMan (packages/cfs_data-man/packages/cfs_data-man.js:75:1)
I20151112-17:29:03.770(-5)? at setData (packages/cfs_file/packages/cfs_file.js:107:1)
I20151112-17:29:03.770(-5)? at EventEmitter.fsFileAttachData [as attachData] (packages/cfs_file/packages/cfs_file.js:102:1)
I20151112-17:29:03.771(-5)? at maybeAuditArgumentChecks (livedata_server.js:1698:12)
I20151112-17:29:03.771(-5)? at livedata_server.js:708:19
I20151112-17:29:03.771(-5)? at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20151112-17:29:03.772(-5)? at livedata_server.js:706:40
I20151112-17:29:03.772(-5)? at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
Check the answer here... Meteor File Upload Not Working
When you need to insert a file that's located on a client, always call myFSCollection.insert on the client. While you could define your own method, pass it the fsFile, and call myFSCollection.insert on the server, the difficulty is with getting the data from the client to the server. When you pass the fsFile to your method, only the file info is sent and not the data. By contrast, when you do the insert directly on the client, it automatically chunks the file's data after insert, and then queues it to be sent chunk by chunk to the server. And then there is the matter of recombining all those chunks on the server and stuffing the data back into the fsFile. So doing client-side inserts actually saves you all of this complex work, and that's why we recommend it.
Then to secure the insert, since it is coming from the client side, setup your allow / deny rules to decide who can insert what where. In your server folder, add a file (usually /server/allow/Uploads.js) Something like this...
Uploads.allow({
insert: function (userId, doc) {
// the user must be logged in, and whatever other constraints you want
return (userId && otherCoolSecurityCheckFunction());
},
update: function (userId, doc, fields, modifier) {
// can only change your own documents
return doc.owner === userId;
},
remove: function (userId, doc) {
// can only remove your own documents
return doc.owner === userId;
},
fetch: ['owner']
});
See the allow docs for more information...
http://docs.meteor.com/#/full/allow

Meteor.call and latency compensation

I'm trying to understand why am I getting the error when calling a meteor server method. It works on the server side but it's throwing errors in the browser.
This is my server code in /server/methods.js file:
Meteor.methods({
getTicketSettings: function(){
var getTicketConfig = function(callback){
Assets.getText('ticketCustomizing.json', function(error, res){
if (error)
throw new Meteor.Error({error:'ticket-getCustomizing', reason:'No se pudo recuperar la configuración.'});
else callback && callback(null, JSON.parse(res));
});
}
var syncAssetRetrieve = Meteor.wrapAsync(getTicketConfig);
var result = syncAssetRetrieve();
return result;
},
});
And this is in my client/server code in /lib/initialization.js file:
App.config.tickets.tipos = new Mongo.Collection('tipos');
Meteor.startup(function(){
moment.locale('es');
var ticketSettingsObj = Meteor.call('getTicketSettings');
console.log(ticketSettingsObj);
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
When I run my application I have the JSON object logged in the console but the browser is showing this error: Uncaught TypeError: Cannot read property 'tipos' of undefined in my /lib/initialization.js here:
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
Obviously I misunderstood something but still wondering...
You need to pass a callback to the Meteor.call. The server can run it synchronously, blocking until it gets a return, but the client cannot so ticketSettingsObj will always be undefined.
See Meteor docs
Without error handling (and untested):
Meteor.call('getTicketSettings', function(error, result){
console.log(result);
_.map(result.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
Do a console.log(App.config.tickets) and see if it returns a valid object. If it doesn't then you have defined the object App.config.tickets only on server side. If this is intentional and you only want this to be accessible on server side then then add a if(Meteor.isServer) or move the definition it to a file inside /server directory.

Accessing this.userId not working when calling from within Meteor.SetTimeout

I've been trying to access the this.userId variable from within a Meteor.methods call, but it doesn't seem to work when I try to call the method via Meteor.setTimeout or Meteor.setInterval.
This is what I've got:
if (Meteor.is_server) {
Meteor.methods({
getAccessToken : function() {
try {
console.log(this.userId);
return Meteor.users.findOne({_id: this.userId}).services.facebook.accessToken;
} catch(e) {
return null;
}
}
});
var fetch_feed = function() {
console.log(Meteor.call("getAccessToken"));
[...] // A bunch of other code
};
Meteor.startup(function() {
Meteor.setInterval(fetch_feed, 60000); // fetch a facebook group feed every minute
Meteor.setTimeout(fetch_feed, 3000); // initially fetch the feed after 3 seconds
});
}
Watching the terminal log, the this.userId always returns a null. But if I try calling the method from the client side, or through the console, it returns the correct ID.
How come this doesn't work from within a Meteor.setInterval? Is it a bug or am I doing something wrong?
Meteor userId's are associated with client connections. The server may interact with many clients and this.userId inside a method will tell you which client has asked for the method to be run.
If the server uses Meteor.call() to run a method then it will not have a userId since it is not running for any client.
The methods allow clients to call for functions to be run on the server. For things the server will trigger itself a javascript function will do.
There is a solution I used - sometimes you do not want to make the method a function but really want it to remain a method. In that case, a hack to make this work:
var uniqueVar_D8kMWHtMMZJRCraiJ = Meteor.userId();
Meteor.setTimeout(function() {
// hack to make Meteor.userId() work on next async
// call to current method
if(! Meteor._userId) Meteor._userId = Meteor.userId;
Meteor.userId = function() {
return Meteor._userId() || uniqueVar_D8kMWHtMMZJRCraiJ
};
Meteor.apply(methodName, args);
}
, 100);
Some brief explanation: we save Meteor.userId in Meteor._userId and overwrite Meteor.userId with a function that returns Meteor._userId() if it is true and otherwise the historic value of Meteor.userId() before any of this happened. That historic value is saved in an impossible to occur twice var name so that no context conflicts can happen.

How to get current user in custom route?

As per this answer I created my own route so that I could handle file uploads. Here's what I've got:
var router = Connect.middleware.router(function(route) {
route.post('/upload', function(req, res) {
var filename = req.headers['x-filename'];
var path = Path.join('.uploads', filename);
var writeStream = FileSystem.createWriteStream(path);
writeStream.on('error', function(e) {
console.error(e);
res.writeHead(500);
res.end();
}).on('close', function() {
Fiber(function() {
console.log(Meteor.user());
}).run();
res.writeHead(200);
res.end();
});
req.pipe(writeStream);
});
});
app.use(router);
This works great for uploading files, but when I try to acess Meteor.user() it gives me:
app/server/main.js:24
}).run();
^
Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
at Object.Meteor.userId (app/packages/accounts-base/accounts_server.js:95:13)
at Object.Meteor.user (app/packages/accounts-base/accounts_server.js:100:25)
at app/server/main.js:23:36
Exited with code: 1
I can't see anything in the req object that might help me out.
Is there any way to get access to the user object?
For the time being, I'm getting the user ID client side and passing it along through the headers which I then use to look up server side:
route.post('/upload', function(req, res) {
Fiber(function() {
var userId = req.headers['x-userid'];
var user = Meteor.users.findOne({_id:userId});
if(user) {
...
} else {
res.writeHead(403,'User not logged in');
res.end();
}
}).run();
});
I don't like this because it's not at all secure. It would be easy to upload something under a different user's account.
Edit: Nevermind. The very act of calling Meteor.users.findOne({_id:userId}); somehow breaks the upload stream. Every file gets corrupt as soon as I put that in; they upload up to about 700 KB and then just stop and close the connection without error.
If it's still valid question.
The problem is that there is no way how to get Meteor.user() in this part of code.
But you can always reach Meteor.userId .. and it's not null if user is logged in .. so you can upload only for logged user. (if req.headers['x-userid'] == Meteor.userId)
The very act of calling Meteor.users.findOne({_id:userId}); somehow
breaks the upload stream.
Because it's reactive part.. so every time if Meteor.users collection is updated this part of code is executed again.
So if you can use only Meteor.userId (which is changed only if user is logged in/out) it should work fine.
I've run into this quite a few times, and it's frustrating. I don't think you can make Meteor.userId() calls from inside a fiber. I usually do a var userId = Meteor.userId(); before I call the fiber, and then reference that variable instead.

Resources