From 81a12ca0204716602d8e40365b409b6c7ef1c651 Mon Sep 17 00:00:00 2001 From: etaypere Date: Wed, 1 May 2013 02:17:58 +0300 Subject: [PATCH] first commit --- .gitignore | 2 + README.md | 4 ++ example/app.js | 52 ++++++++++++++++ example/models/index.js | 13 ++++ example/models/users.js | 67 +++++++++++++++++++++ example/public/css/style.css | 8 +++ index.js | 1 + lib/index.js | 19 ++++++ lib/resors.js | 113 +++++++++++++++++++++++++++++++++++ package.json | 18 ++++++ 10 files changed, 297 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 example/app.js create mode 100644 example/models/index.js create mode 100644 example/models/users.js create mode 100644 example/public/css/style.css create mode 100644 index.js create mode 100644 lib/index.js create mode 100644 lib/resors.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..479880f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/.idea \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fdbac77 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +Resors +===== + +Resources for mongoose. Check `\example` for now. \ No newline at end of file diff --git a/example/app.js b/example/app.js new file mode 100644 index 0000000..3714695 --- /dev/null +++ b/example/app.js @@ -0,0 +1,52 @@ +var express = require('express'), + http = require('http'), + path = require('path'), + models = require('./models'); + +var app = express(); + +app.set('site', 'resors'); +app.set('port', 80); +app.set('mongo', 'mongodb://localhost/resors'); +app.set('admin', { username: 'admin', password: 'admin'}); + +// dust +/*app.engine('dust', require('consolidate').dust); +app.set('view engine', 'dust'); +app.set('views', path.join(__dirname, 'views')); +require('dustjs-linkedin').optimizers.format = function(ctx, node) { return node };*/ + +app.use(express.favicon()); +//app.use(express.logger('dev')); +app.use(express.bodyParser()); +app.use(express.methodOverride()); +app.use(express.cookieParser('magical resors')); +app.use(express.cookieSession({cookie: { maxAge: 60 * 1000 * 20 }})); +app.use(app.router); +app.use(express.static(path.join(__dirname, 'public'))); + +// development only +if ('development' == app.get('env')) { + app.use(express.errorHandler()); +} + +// formage-admin +require('formage-admin').init(app, express, require('./models'), { + title: app.get('site') + ' Admin' +}); + +// mock user +app.use(function(req, res, next) { + req.user = { name: 'me', admin: true }; + next(); +}); + +require('mongoose').connect(app.get('mongo')); +app.use('/api', require('../')(express, models)); +app.get('/', function(req, res) { + res.redirect('/api'); +}); + +http.createServer(app).listen(app.get('port'), function(){ + console.log('Server listening on port ' + app.get('port')); +}); \ No newline at end of file diff --git a/example/models/index.js b/example/models/index.js new file mode 100644 index 0000000..f20176f --- /dev/null +++ b/example/models/index.js @@ -0,0 +1,13 @@ +var path = require('path'), + fs = require('fs'), + files = fs.readdirSync(__dirname); + +require('formage-admin').forms.loadTypes(require('mongoose')); + +files.forEach(function(file) { + var name = path.basename(file, '.js'); + if (name === 'index') + return; + + exports[name] = require('./' + name); +}); \ No newline at end of file diff --git a/example/models/users.js b/example/models/users.js new file mode 100644 index 0000000..1285ffa --- /dev/null +++ b/example/models/users.js @@ -0,0 +1,67 @@ +var mongoose = require('mongoose'), + Types = mongoose.Schema.Types; + +var schema = new mongoose.Schema({ + name: { type: String, required: true, lowercase: true, trim: true, match: /^\w+$/ }, + email: String +}); +schema.methods.toString = function(){ + return this.name; +}; +schema.statics.checkName = function(value, cb){ + this.count().where('username', value).exec(function(err, count){ + cb(err, count === 0) + }) +}; +var users = module.exports = mongoose.model('users', schema); + + +/* + Resors + */ +users.resors = { + allow: [ 'get', 'post', 'put', 'delete' ], + select: 'email', // TODO + editable: 'email', // TODO + filtering: 'name', // TODO + sorting: 'name', // TODO + before: function(req, res, next) { + var resors = req.resors; + + // Authentication + if (!req.user) + return res.redirect('/login'); + + // Authorization + if (!req.user.admin) + req.resors.allow = [ 'get' ]; + + // Validation or sanitation (use mongoose if you can!) + if (resors.validation) { + if (!req.body.email) { + console.log('validation', req.body); + return res.status(400).json({ + email: 'Email is required.' + }); + } + } + + next(); + }, + query: function(req, res, next) { + var q = res.query; +// q = q.select('name'); + + // Authorization + if (req.user && !req.user.admin) { + q = q.where('name', req.user.name); + } + + res.query = q; + next(); + }, + after: function(req, res, next) { +// console.log('after', res.result); + next(); + } +}; diff --git a/example/public/css/style.css b/example/public/css/style.css new file mode 100644 index 0000000..30e047d --- /dev/null +++ b/example/public/css/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..4cc88b3 --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..af541f0 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,19 @@ +var Resors = require('./resors'); + +var resors = module.exports = function(express, models) { + var app = express(); + + app.get('/', function(req, res) { + res.json({ + models: Object.keys(models) + }); + }); + + Object.keys(models).forEach(function(name) { + var resource = Resors.fetch(models[name]); + resource.routes(app); + }); + + return app; +}; +resors.Resors = Resors; \ No newline at end of file diff --git a/lib/resors.js b/lib/resors.js new file mode 100644 index 0000000..bcdb99e --- /dev/null +++ b/lib/resors.js @@ -0,0 +1,113 @@ +var extend = require('xtend'); + +/* + A do-nothing middleware + */ +var middleware = function(req, res, next) { + next(); +}; + + +/* + Resors Class + */ +var Resors = module.exports = function(model, options) { + this.model = model; + this.name = model.modelName; + this.options = extend(Resors.defaults, options); + + Resors.register(this); +}; +Resors.fn = Resors.prototype; +Resors.fn.index = function(req, res, next) { + res.query = this.model.find(); + next(); +}; +Resors.fn.show = function(req, res, next) { + res.query = this.model.findById(req.params.id); + next(); +}; +Resors.fn.create = function(req, res, next) { + this.model.create(req.body, function(err, result) { + res.err = err; + res.result = result; + next(); + }); +}; +Resors.fn.update = function(req, res, next) { + res.query = this.model.findByIdAndUpdate(req.params.id, req.body); + next(); +}; +Resors.fn.destroy = function(req, res, next) { + res.query = this.model.findByIdAndRemove(req.params.id); + next(); +}; +Resors.fn.exec = function(req, res, next) { + res.query.exec(function(err, result) { + delete res.query; + res.err = err; + res.result = result; + next(); + }); +}; +Resors.fn.finish = function(req, res) { + if (res.err) + res.status(400).json({ err: res.err }); + else + res.json(res.result); +}; +Resors.fn.middlewares = function(route) { + var self = this; + + return [ + function(req, res, next) { + req.resors = extend({}, self.options, { + validation: ~[ 'POST', 'PUT' ].indexOf(req.method) + }); + next(); + }, + this.options.before, + function(req, res, next) { + if (!~req.resors.allow.indexOf(req.method.toLowerCase())) + return res.status(403).end('No permissions.'); + next(); + }, + this[route], + (~[ 'index', 'show' ].indexOf(route) ? this.options.query : middleware), + ('create' != route ? this.exec : middleware), + this.options.after, + this.finish + ].map(function(m) { return m.bind(self) }); +}; +Resors.fn.routes = function(app) { + var name = this.name; + + app.get('/' + name, this.middlewares('index')); + app.get('/' + name + '/:id', this.middlewares('show')); + app.post('/' + name + '', this.middlewares('create')); + app.put('/' + name + '/:id', this.middlewares('update')); + app.delete('/' + name + '/:id', this.middlewares('destroy')); +}; + + +/* + Resors Registry + */ +var resources = {}; +Resors.register = function(resource) { + resources[resource.name] = resource; +}; +Resors.fetch = function(model) { + var name = model.modelName; + + if (resources[name]) + return resources[name]; + else + return new Resors(model, model.resors); +}; +Resors.defaults = { + allow: [ 'get' ], + before: middleware, + query: middleware, + after: middleware +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9e1617c --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "resors", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node example/app" + }, + "dependencies": { + "express": "3.2.0", + "formage-admin": "", + "jade": "*", + "consolidate": "", + "dustjs-linkedin": "", + "dustjs-helpers": "", + "mongoose": "", + "xtend": "" + } +} \ No newline at end of file