I have
epilogue.resource({
model: db.Question,
endpoints: ['/api/questions', '/api/questions/:id'],
associations: true
});
So when I hit /api/questions, I get back all the associations with the resources. Is there something I can pass to not get the associations in certain cases? Or should I create a new endpoint:
epilogue.resource({
model: db.Question,
endpoints: ['/api/questions2', '/api/questions2/:id']
});
One way of doing is by using milestones you can define milestone for list and read behaviour in certain case, you have access to req object so you can do changes accordingly
https://github.com/dchester/epilogue#customize-behavior
Here is an example of list call modification
// my-middleware.js
module.exports = {
list: {
write: {
before: function(req, res, context) {
// modify data before writing list data
return context.continue;
},
action: function(req, res, context) {
// change behavior of actually writing the data
return context.continue;
},
after: function(req, res, context) {
// set some sort of flag after writing list data
return context.continue;
}
}
}
};
// my-app.js
var epilogue = require('epilogue'),
restMiddleware = require('my-middleware');
epilogue.initialize({
app: app,
sequelize: sequelize
});
var userResource = epilogue.resource({
model: User,
endpoints: ['/users', '/users/:id']
});
userResource.use(restMiddleware);
Related
I'm trying to store some log data for my models on create, update, delete calls. I want to store some data from the request along with some user data also in the request (using express.js).
In the hooks I have some modules for logging.
hooks: {
afterCreate: function (order, options, done) {
// How to get user data stored in express request.
return app.log.set('event', [{message: 'created', data: order, userId: 1}, done]);
}
}
...
The module just makes a record in a table. However it's the userId part I'm having trouble with. I'm using the passport module and it's stored in the request, so how can I get a user object (or any external object for that matter) into the model hooks?
I would like to avoid doing it in a controller or anywhere else as there could be some scripts or other commands that may also enter data.
I also encountered similar problems, which I myself resolved as follows:
First: I declared a Global (universal) hook:
module.exports = sequelize.addHook('beforeCreate',
function(model, options, done) {//hook 2
//handle what you want
//return app.log.set('event', [{message: 'created', data: order, userId: 1}, done]);
});
Then, Before calling model, use call hooks (beforeCreate, beforeBulkUpdate,...) and assigned param request
module.exports = {
CreateUser: function(req, res) {
User.beforeCreate(function(model, options, done) {//hook1
model.request = req;
});
User.create({
id: 1,
username: 'thanh9999',
password: '31231233123'
//ex.....
})
.then(function(success) {
//response success
}, function(err) {
//response error
});
}
};
order hooks called: hook declaration in model → hook 1 → hook 2`.
In addition, you also have to declare hooks for each model.
See more information here.
I need some advice for building a correct role schema and management in my meteor-app.
Structure
Im using alanning:roles#1.2.13 for adding role management functionallity to the app.
There are four different user-types: Admin, Editor, Expert and User.
Furthermore there are several modules with different content, i.e. Cars, Maths and Images. Every module is organized in an own meteor-package.
In every module there are several categories, which can be added dynamically by editors.
Categories in modules
Module is structured like this:
elementSchema = new SimpleSchema({
element: {type: String, optional: true}
});
Cars.attachSchema(new SimpleSchema({
title: { type: String },
content: { type: String },
category: { type: [elementSchema], optional: true },
});
As you can see, all available categories are inside of the Collection of the module.
Rights
Admin: Complete rights
Editor: Can edit elements in selected moduls (i.e. editor_1 can edit elements in Cars and Images but not for Maths)
Expert: Can get rights to a complete module or just to some categories of a module (i.e.) expert_1 can edit Images, but only the elements in category "Honda" and "Mercedes" in Cars; no editing to Maths)
User: No editing
This is how I do the authentification technically:
router.js
var filters = {
authenticate: function () {
var user;
if (Meteor.loggingIn()) {
this.layout('login');
this.render('loading');
} else {
user = Meteor.user();
if (!user) {
this.layout('login');
this.render('signin');
return;
}
this.layout('Standard');
this.next();
}
}
}
Router.route('/car/:_id', {
name: 'car',
before: filters.authenticate,
data: function () {
return {
cars: Cars.findOne({ _id: this.params._id })
};
}
});
template
<template name="car">
{{#if isInRole 'cars'}}
Some form for editing
{{else}}
<h1>Restricted area</h1>
{{/if}}
</template>
I put this router.js to every package. Only change is the data function which uses the Collection of each package (Cars, Maths, Images).
Update: As 'Eliezer Steinbock' commented it is necessary to restrict acces to the mongoDB itself. But until now I only did that on the routes.
permissions.js
Cars.allow({
insert: function(userId) {
var loggedInUser = Meteor.user()
if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
},
update: function(userId) {
var loggedInUser = Meteor.user()
if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
}
});
My problems
1) My first problem is how to use roles and groups. What would be the best way for using groups? And the second problem is, that there are no fixed categories in the modules. Right now I have no idea for a useful role/group schema.
2) How do I check for the roles? As there are different roles which can get access: admin, editor and expert. Also I got the problem with these experts who just get access to defined categories of this module.
3) Wouldn't it be better to make the permission.js more general. I mean, is it possible to make a dynamic function, so I don't have to put everywhere the same code? How do I implement the roles in the permission.js in a useful way?
if the logic for the permissions is the same you could just define it once in permissions.js
App = App || {}; // We are using Namespaces, so you don't have to.. but it's good
App.Permissions = {
insert: function(userId) {
var loggedInUser = Meteor.user()
if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
},
update: function(userId) {
var loggedInUser = Meteor.user()
if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
}
}
And then you can use it for your Collections:
Cars.allow(App.Permissions); // Or
Cars.allow(App.Permissions.getPermissionsForGroup('cars'))
Define roles somewhere..
Roles
// Give user the role "editor" in "cars" group
Roles.addUsersToRoles(someUserId, ['editor'], 'cars');
Roles.addUsersToRoles(someOtherId, ['admin'], 'cars');
Which you can prepare in permissions.js like this:
Permissions
App = App || {};
App.Permissions = {
insert: function(userId) {...},
update: function(userId) {...},
getPermissionsForGroup: function(group) {
return {
insert: function(userId, doc) {
// Only admin can insert
return Roles.userIsInRole(userId, "admin", group);
},
update: function(userId, doc, fields, modifier) {
// Editor & Admin can edit
return Roles.userIsInRole(userId, ["editor","admin"], group);
},
remove: function(userId, doc) {
// Only admin can remove
return Roles.userIsInRole(userId, "admin", group);
}
}
}
In this example admins can insert and update.. and editors can only update, but insert.
Regarding the documentation of alanning:roles you define and use roles like this:
// Super Admin definition..
Roles.addUsersToRoles(superAdminId, ['admin'], Roles.GLOBAL_GROUP);
Roles.addUsersToRoles(joesUserId, ['manage-team','schedule-game'], 'manchester-united.com')
Roles.addUsersToRoles(joesUserId, ['player','goalie'], 'real-madrid.com')
Roles.userIsInRole(joesUserId, 'manage-team', 'manchester-united.com') // => true
Roles.userIsInRole(joesUserId, 'manage-team', 'real-madrid.com') // => false
Yeah, make sure, that the permission logic will be included before your Collection definition.. obviously :)
I am building an application using MongoDB, Angular, Express, and Node (MEAN stack).
I used the MEAN.JS generator to scaffold my application.
I will use the articles module as a reference.
Suppose I have 7000 records in my articles collection, and each record has a date associated with it. It is inefficient to load all 7000 records into memory every time I load the page to view the records in a table and I am seeing terrible performance losses because of it. For this reason, I would only like to load records with a date in the range of (1 Month Ago) to (1 Year From Now) and display them in the table. I can currently do this with the following:
In my articles.client.controller.js:
$scope.find = function() {
$articles = Articles.query();
};
...and in my articles.server.controller.js:
var now = new Date();
var aYearFromNow = new Date(now.getTime() + 86400000*365); //add a year
var aMonthAgo = new Date(now.getTime() - 86400000*30); //subtract roughly a month
exports.list = function(req, res) { Article.find().where('date').lt(aYearFromNow).gt(aMonthAgo).sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
return res.send(400, {
message: getErrorMessage(err)
});
} else {
res.jsonp(articles);
}
});
};
The problem is that this is not a dynamic way of doing things. In other words, I want the user to be able to specify how far back and how far forward they want to see.
How can I bind to variables (e.g. 'aYearFromNow' and 'aMonthAgo') in my client view that will change the query parameters in my server controller?
Another way is to just pass the search parameters in the query method, like this:
$scope.searchart = function() {
Articles.query({start:$scope.startDate, end:$scope.endDate}, function(articles) {
$scope.articles = articles;
});
};
and then at the server side controller, read your query string parameters like this:
exports.searcharticle = function(req, res) {
Article.find().where('date').gt(req.query['start']).lt(req.query['end']).exec(function(err, articles) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(articles);
}
});
};
This way doesn't require more routes or services.
It's probably not the cleanest way, but you can create a new service (or edit the current one to work with several parameters):
.factory('ArticlesService2', ['$resource',
function($resource) {
return $resource('articles/:param1/:param2', {
param1: '',
param2: ''
}, {
update: {
method: 'PUT'
}
});
}
]);
Then call it in your controller :
$scope.findWithParams = function() {
$scope.article = ArticlesService2.query({
param1: $scope.aYearFromNow,
param2: $scope.aMonthAgo
});
};
On the back-end, you'll need to prepare a route :
app.route('/articles/:param1/:param2')
.get(articles.listWithParams)
Add a function to your back-end controller :
exports.listWithParams = function(req, res) {
Article.find()
.where('date')
.lt(req.params.param1)
.gt(req.params.param2)
.sort('-created').populate('user', 'displayName')
.exec(function(err, articles) {
if (err) {
return res.send(400, {
message: getErrorMessage(err)
});
} else {
res.jsonp(articles);
}
});
};
Should work, haven't tested it though.
i need to provide something like an association in my Model. So I have a Model called Posts with an userid and want to get the username from this username and display it.
So my ForumPosts.js Model looks like the following:
module.exports = {
schema: true,
attributes: {
content: {
type: 'text',
required: true
},
forumTopicId: {
type: 'text',
required: true
},
userId: {
type: 'integer',
required: true
},
getUsername: function(){
User.findOne(this.userId, function foundUser(err, user) {
var username = user.username;
});
console.log(username);
return username;
}
}
};
I know that this return will not work because it is asynchronus... But how can i display it in my view? At the Moment i retrive the value with:
<%= forumPost.getUsername() %>
And for sure get an undefined return...
So the question is: How can I return this value - or is there a better solution than an instanced Model?
Thanks in advance!
Off the top of my head, you can just load associated user asynchronously before rendering:
loadUser: function(done){
var that = this;
User.findOne(this.userId, function foundUser(err, user) {
if ((err)||(!user))
return done(err);
that.user = user;
done(null);
});
}
then in your controller action:
module.exports = {
index: function(req, res) {
// Something yours…
forumPost.loadUser(function(err) {
if (err)
return res.send(err, 500);
return res.view({forumPost: forumPost});
});
}
}
and in your view:
<%= forumPost.user.username %>
This is kind of a quick and dirty way. For a more solid and long-term solution (which is still in development so far) you can check out the alpha of Sails v0.10.0 with the Associations API.
So this particularly case of associations between your models. So here you have a User model and ForumPost model and you need the user object in place of your user_id as user_id works as a relationship mapping field to your User model.
So if your are using sails V0.9.8 or below you need to handle this logic in your controller where ever you want to access User model attributes in your view.
In your controller write your logic as:
model.export = {
//your getForumPosts method
getForumPosts : function(req,res){
var filters = {};
forumPost.find(filters).done(function(err,posts){
if(err) return res.send(500,err);
// Considering only one post obj
posts = posts[0];
postByUser(posts.user_id,function(obj){
if(obj.status)
{
posts.user = obj.msg;
delete posts.user_id;
res.view({post:posts});
}
else
{
res.send(500,obj.msg);
}
});
}
}
}
function postByUser(user_id,cb){
User.findOne(user_id).done(function(err,user){
if(err) return cb({status:false, msg:err});
if(user){
cb({status:true, msg:user});
}
}
}
and then you can access your post object in your view.
Or else you can keep watch (at GitHub) on next version of sails as they have announced associations in V0.10 n it is in beta testing phase as if now.
I'm not sure what is the elegant way to pass server variables in to my Model.
For example, i have an id of user that has to be implemented on my Model. But seems like Backbone with require are not able to do that.
My two options are:
Get a json file with Ajax.
Add the variable on my index.php as a global.
Someone know if exists a other way. Native on the clases?
Trying to make work the example of backbonetutorials. I am not able to throw a callback when the method fetch().
$(document).ready(function() {
var Timer = Backbone.Model.extend({
urlRoot : 'timeserver/',
defaults: {
name: '',
email: ''
}
});
var timer = new Timer({id:1});
timer.fetch({
success: function(data) {
alert('success')
},
fail: function(model, response) {
alert('fail');
},
sync: function(data) {
alert('sync')
}
});
});
The ajax request it has been threw. But does not work at all. Because any alert its dispatched.
var UserModel = Backbone.Model.extend({
urlRoot: '/user',
defaults: {
name: '',
email: ''
}
});
// Here we have set the `id` of the model
var user = new Usermodel({id: 1});
// The fetch below will perform GET /user/1
// The server should return the id, name and email from the database
user.fetch({
success: function (user) {
console.log(user);
}
})
The server will reply with a json object then you can leave the rendering part for your backbone. Based on a template for the user.
You may also want to check these out: http://backbonetutorials.com/