From b1534c69d2eb97e115b83748573196b3e462a4ef Mon Sep 17 00:00:00 2001 From: Ritchie Date: Sat, 26 Nov 2011 10:26:16 -0700 Subject: [PATCH 1/2] generic model routes --- lib/model.js | 32 +++++++++++++++++++++++++++++++- lib/plugins/user/index.js | 20 ++++++++++---------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/lib/model.js b/lib/model.js index a2b27c9..e501a80 100644 --- a/lib/model.js +++ b/lib/model.js @@ -9,6 +9,7 @@ var Model , db = require('./db') , compile = require('./types').compile , _ = require('underscore') + , app = require('./app') ; var emitter = new EventEmitter(); @@ -394,6 +395,34 @@ Model = module.exports = emitter.spawn({ } } } + }, + + defineRoutes: function() { + var collection = this.collection + , model = this + , methodMap = { + get: 'fetch', + post: 'save', + put: 'save', + delete: 'remove' + } + ; + + app.all('/' + (this.plugin || 'graph') + '/' + collection, function(req, res) { + var query = req.params + , action = methodMap[req.method] + ; + + model + .spawn() + .for(req) + .find(query) + .set(req.body) + .notify(res) + [action]() + ; + }); + } }); @@ -408,9 +437,10 @@ module.exports.refreshSettings = function(collection) { module.exports.spawn = function(model) { var instance = spawn.apply(this, arguments); - if(model && model.collection) { + if(model && model.collection && !_models[instance.collection]) { _models[instance.collection] = instance; instance.updateSettings(); + instance.defineRoutes(); } return instance; }; \ No newline at end of file diff --git a/lib/plugins/user/index.js b/lib/plugins/user/index.js index bacdc59..915f462 100644 --- a/lib/plugins/user/index.js +++ b/lib/plugins/user/index.js @@ -70,13 +70,13 @@ app.post('/user/:email/group', function(req, res) { ; }); -app.get('/users', function(req, res) { - Users - .spawn({ - query: req.params - }) - .for(req) - .notify(res) - .fetch() - ; -}); +// app.get('/users', function(req, res) { +// Users +// .spawn({ +// query: req.params +// }) +// .for(req) +// .notify(res) +// .fetch() +// ; +// }); From 196246055f93c09fa4c58530ec43715b0ea9b30b Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Sat, 26 Nov 2011 12:34:19 -0800 Subject: [PATCH 2/2] generic routes fixes --- lib/app.js | 103 ------------------------------- lib/collection.js | 29 +++++++++ lib/db.js | 2 +- lib/model.js | 56 +++++++++++------ lib/plugins/app/app.js | 54 ---------------- lib/plugins/app/apps.js | 8 --- lib/plugins/app/balancer.js | 74 ---------------------- lib/plugins/app/index.js | 49 --------------- lib/plugins/app/views/index.ejs | 29 --------- lib/plugins/app/views/layout.ejs | 26 -------- lib/plugins/app/views/login.ejs | 37 ----------- lib/plugins/graph/index.js | 32 ---------- lib/plugins/settings/index.js | 13 +--- lib/plugins/settings/setting.js | 2 +- lib/plugins/settings/settings.js | 3 +- lib/plugins/user/group.js | 34 ---------- lib/plugins/user/index.js | 82 ------------------------ lib/plugins/user/user.js | 59 ------------------ lib/plugins/user/users.js | 8 --- public/test/deployd.test.js | 8 +-- 20 files changed, 75 insertions(+), 633 deletions(-) delete mode 100644 lib/plugins/app/app.js delete mode 100644 lib/plugins/app/apps.js delete mode 100644 lib/plugins/app/balancer.js delete mode 100644 lib/plugins/app/index.js delete mode 100644 lib/plugins/app/views/index.ejs delete mode 100644 lib/plugins/app/views/layout.ejs delete mode 100644 lib/plugins/app/views/login.ejs delete mode 100644 lib/plugins/graph/index.js delete mode 100644 lib/plugins/user/group.js delete mode 100644 lib/plugins/user/index.js delete mode 100644 lib/plugins/user/user.js delete mode 100644 lib/plugins/user/users.js diff --git a/lib/app.js b/lib/app.js index 5c7cba3..e9bb4fd 100644 --- a/lib/app.js +++ b/lib/app.js @@ -55,107 +55,4 @@ app.get('/routes', function(req, res) { app.get('/', function(req, res) { res.send({home: true}); -}); - -app.get('/config', function(req, res) { - res.send({ - name: 'Hello World', - host: 'foo.bar.com' - }) -}); - -app.get('/models', function(req, res) { - res.send({ - results: [{ - plugin: 'Users', - name: 'Users', - _id: 5432 - }] - }) -}); - -app.get('/plugins', function(req, res) { - res.send({ - results: [{ - name: 'Users', - overview_html: '

The Users plugin lets you configure users...

', - configurable_objects: [ - { - name: "Roles and Permissions" - , id: 1 - }, - { - name: "Existing Users" - , id: 2 - , list: "User List" - , source: "/users" - }, - { - name: "Model", - id: 3, - model_description: { - password:"password", - uid:"email", - removed:"boolean", - name:"string", - auth:"string" - }, - plugin: "graph", - _id: "4ec48fa3d1a11cd925000007" - }, - { - /* - description: { - uid: 'email', - password: 'password', - name: 'string', - auth: 'string', - removed: 'boolean' - }, - - */ - name: "New User" - , id: 4 - , helper_text: "Fill out the form below to create a new user" - , form: { - action: "/user" - , method: "POST" - , fields: [ - { - name: "name" - , label: "Full Name" - , type: "text" - , required: true - }, - { - name: "uid" - , label: "Email" - , type: "email" - , required: true - }, - { - name: "password" - , label: "Password" - , type: "password" - , required: true - } - ] - } - } - ], - _id: 1234 - }, - { - name: "Phone", - overview_html: '

The Phone plugin lets you configure phone stuff...

', - configurable_objects: [ - { - name: "Numbers" - , id: 1 - } - ], - - _id: 2345 - }] - }) }); \ No newline at end of file diff --git a/lib/collection.js b/lib/collection.js index 22286f4..39f96c1 100644 --- a/lib/collection.js +++ b/lib/collection.js @@ -4,6 +4,8 @@ var Model = require('./model') module.exports = Model.spawn({ + isCollection: true, + initialize: function() { this.models = []; }, @@ -47,6 +49,33 @@ module.exports = Model.spawn({ this.models = changes; this.state = this._states.ready; this.emit('change:state'); + }, + + updateSettings: function() { + // TODO, implement basic settings + }, + + defineRoutes: function(app) { + var collection = this.collection + , model = this + , plugin = this.plugin + , base = (plugin === collection) ? '' : ('/' + plugin) + , route = [base, collection].join('/') + ; + + // one model + app.get(route, function(req, res) { + var query = req.query; + + model + .spawn() + .for(req) + .find(query) + .set(req.body) + .notify(res) + .fetch() + ; + }); } }); diff --git a/lib/db.js b/lib/db.js index 6d4dcc1..16becb2 100644 --- a/lib/db.js +++ b/lib/db.js @@ -30,7 +30,7 @@ module.exports = { find: ready(function(model) { var query = model.toQuery() || {} - , id = model.get('_id') + , id = model.get('_id') || (query && query._id) , _id = id && ObjectID(id) ; diff --git a/lib/model.js b/lib/model.js index e501a80..7831118 100644 --- a/lib/model.js +++ b/lib/model.js @@ -150,6 +150,7 @@ Model = module.exports = emitter.spawn({ , actor = this.actor() , allowed = true , model = this + , User = require('./plugins/users/user') ; if(requiresUser && !actor) { @@ -160,9 +161,9 @@ Model = module.exports = emitter.spawn({ if(permissions && rights && rights !== 'public') { // check permission against actor - Model - .spawn({collection: 'users', allowed: false}) - .set({_id: actor}) + User + .spawn() + .find({_id: actor}) .notify(function(json) { // TODO cache groups on the req var groups = json.groups @@ -208,7 +209,7 @@ Model = module.exports = emitter.spawn({ fn(); }, - for: function(req) { + for: function(req) { if(req.session && req.session.user) { this.actor(req.session.user._id); } @@ -259,7 +260,7 @@ Model = module.exports = emitter.spawn({ set: function(changes) { var _self = this; - + if(!changes) return this; Object.getOwnPropertyNames(changes).forEach(function(p) { if(_self.attributes[p] != changes[p]) { _self.attributes[p] = changes[p]; @@ -348,7 +349,7 @@ Model = module.exports = emitter.spawn({ return this; }, - plugin: 'graph', + plugin: 'models', updateSettings: function() { var settings = Model.spawn() @@ -360,9 +361,8 @@ Model = module.exports = emitter.spawn({ if(this.description) { settings - .find({name: this.name || this.collection, plugin: this.plugin}) + .find({collection: this.collection, plugin: this.plugin}) .set({ - name: this.name || this.collection, description: this.description, plugin: this.plugin, collection: this.collection, @@ -397,20 +397,24 @@ Model = module.exports = emitter.spawn({ } }, - defineRoutes: function() { + defineRoutes: function(app) { var collection = this.collection , model = this + , plugin = this.plugin + , base = (plugin === collection) ? '' : ('/' + plugin) + , route = [base, collection].join('/') + , idRoute = [route, ':id'].join('/') , methodMap = { - get: 'fetch', - post: 'save', - put: 'save', - delete: 'remove' + GET: 'fetch', + POST: 'save', + PUT: 'save', + DELETE: 'remove' } ; - app.all('/' + (this.plugin || 'graph') + '/' + collection, function(req, res) { + function handler(req, res) { var query = req.params - , action = methodMap[req.method] + , action = methodMap[req.method] || 'fetch' ; model @@ -421,14 +425,23 @@ Model = module.exports = emitter.spawn({ .notify(res) [action]() ; - }); + } + // create + app.post(route, handler); + // read + app.get(idRoute, handler); + // update + app.put(idRoute, handler); + // delete + app.del(idRoute, handler); } }); var spawn = module.exports.spawn , _models = {} + , _collections = {} ; module.exports.refreshSettings = function(collection) { @@ -436,11 +449,14 @@ module.exports.refreshSettings = function(collection) { } module.exports.spawn = function(model) { - var instance = spawn.apply(this, arguments); - if(model && model.collection && !_models[instance.collection]) { - _models[instance.collection] = instance; + var instance = spawn.apply(this, arguments) + , cache = instance.isCollection ? _collections : _models; + + if(model && model.collection && !cache[instance.collection]) { + cache[instance.collection] = instance; instance.updateSettings(); - instance.defineRoutes(); + instance.defineRoutes(app); } + return instance; }; \ No newline at end of file diff --git a/lib/plugins/app/app.js b/lib/plugins/app/app.js deleted file mode 100644 index 2b6b471..0000000 --- a/lib/plugins/app/app.js +++ /dev/null @@ -1,54 +0,0 @@ -var Model = require('../../model'); - -module.exports = Model.spawn({ - - collection: 'apps', - plugin: 'app', - - description: { - name: {type: 'string', unique: true}, - creator: 'string', - db: 'string', - host: 'string' - }, - - allowed: { - read: 'creator', - write: 'creator', - remove: 'creator', - create: 'user' - }, - - save: function() { - var name = this.get('name') - , host = this.toHostName() - , creator = this.get('creator') - ; - - if(!creator) this.error('Must be logged in to create an app.', 'App'); - - if(host && creator) { - if(this.isNew()) { - this.set({name: name, host: host, db: this.toDatabaseName(), creator: creator}); - } else { - this.restart(host); - } - } - - Model.save.apply(this, arguments); - }, - - toHostName: function() { - var name = this.get('name') - , app = name && name.replace(/ /g, '-') - , creator = this.get('creator') - ; - - return app.toLowerCase(); - }, - - toDatabaseName: function() { - return this.toHostName().replace(/\./g, ':').toLowerCase(); - } - -}); \ No newline at end of file diff --git a/lib/plugins/app/apps.js b/lib/plugins/app/apps.js deleted file mode 100644 index dae726f..0000000 --- a/lib/plugins/app/apps.js +++ /dev/null @@ -1,8 +0,0 @@ -var Collection = require('../../collection') - , App = require('./app') -; - -module.exports = Collection.spawn({ - collection: 'apps', - model: App -}); \ No newline at end of file diff --git a/lib/plugins/app/balancer.js b/lib/plugins/app/balancer.js deleted file mode 100644 index d16405a..0000000 --- a/lib/plugins/app/balancer.js +++ /dev/null @@ -1,74 +0,0 @@ -// Balancer tells requests where to go -// - If a internal host (ihost) is not found within its cache, it boots a deployd -// app at a new ihost and saves this location to the central deployd db -// - Subsequent requests should be mapped from host to ihost and routed - -var bouncy = require('bouncy') - , apps = {} - , exec = require('child_process').exec - , ERROR = '

Not Found

There is no app configured to listen to this host. Perhaps you mistyped the url?\n' - , App = require('./app') -; - -bouncy(function (req, bounce) { - var host = req.headers.host - , app = App.spawn() - ; - - if(apps[host]) { - bounce(apps[host].ihost || currentHost(), apps[host].port); - return; - } - - app.query = {host: host.replace('.deploydapp.com', '')}; - app - .notify(function(json) { - apps[host] = json; - if(json.ihost && json.port) { - bounce(app.ihost, json.port); - } else if(json._id) { - // app exists, but not on any internal hosts - // set the internal host to the machine that spawned it - json.ihost = currentHost(); - json.port = nextPort(); - - console.log(['deployd', "'" + JSON.stringify(json) + "'"].join(' ')); - - // then boot - var d = exec(['deployd', "'" + JSON.stringify(json) + "'"].join(' ')); - - d.stdout - .on('data', function(data) { - console.log(data.toString()); - if(data.toString().indexOf('listening') > -1) { - bounce(json.port); - } - }); - - d.stderr.on('data', function(data) { - console.log(data.toString()); - }); - - - app.set(json).save(); - } else { - var res = bounce.respond(); - res.writeHead(500, { - 'Content-Length': ERROR.length, - 'Content-Type': 'text/html' - }); - res.end(ERROR); - } - }) - .fetch() - ; -}).listen(80); - -function nextPort() { - process.last = (3001 || process.last); - return process.last++; -} - -function currentHost() { - return 'localhost'; -} \ No newline at end of file diff --git a/lib/plugins/app/index.js b/lib/plugins/app/index.js deleted file mode 100644 index 03802b5..0000000 --- a/lib/plugins/app/index.js +++ /dev/null @@ -1,49 +0,0 @@ -var app = require('../../app') - , App = require('./app') -; - -if(process.argv.length < 3) { - require('./balancer'); -} - -app.post('/app', function(req, res) { - var session = req.session - , me = session && session.user && session.user.email - ; - - App - .spawn() - .for(req) - .set({name: req.param('name'), creator: me}) - .notify(res) - .save() - ; -}); - -app.get('/app/:id', function(req, res) { - App - .spawn() - .for(req) - .set({_id: req.param('id')}) - .notify(res) - .fetch() - ; -}); - -app.del('/app/:id', function(req, res) { - App - .spawn() - .for(req) - .notify(res) - .remove() - ; -}); - -// views -app.get('/my/apps', function(req, res) { - res.render(__dirname + '/views/index.ejs'); -}); - -app.get('/login', function(req, res) { - res.render(__dirname + '/views/login.ejs'); -}); \ No newline at end of file diff --git a/lib/plugins/app/views/index.ejs b/lib/plugins/app/views/index.ejs deleted file mode 100644 index 5f28a6b..0000000 --- a/lib/plugins/app/views/index.ejs +++ /dev/null @@ -1,29 +0,0 @@ -
-

My Apps

-

alpha!

-
    -
-

Create an app...

- - -
- - - - diff --git a/lib/plugins/app/views/layout.ejs b/lib/plugins/app/views/layout.ejs deleted file mode 100644 index 34a69d8..0000000 --- a/lib/plugins/app/views/layout.ejs +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - index - - - - - - - - -

Deployd

- <%- body %> - - - diff --git a/lib/plugins/app/views/login.ejs b/lib/plugins/app/views/login.ejs deleted file mode 100644 index 1634309..0000000 --- a/lib/plugins/app/views/login.ejs +++ /dev/null @@ -1,37 +0,0 @@ -
- Username - - Password - - -
- - \ No newline at end of file diff --git a/lib/plugins/graph/index.js b/lib/plugins/graph/index.js deleted file mode 100644 index c681a34..0000000 --- a/lib/plugins/graph/index.js +++ /dev/null @@ -1,32 +0,0 @@ -var Settings = require('../settings/settings') - , Setting = require('../settings/settings') - , Model = require('../../model') -; - -var models = module.exports.models = {}; -var refresh = module.exports.refresh = function() { - Settings - .spawn() - .find({plugin: 'graph'}) - .notify(function(model) { - var m = models[model.name] = Model.spawn(); - m.description = model.description; - m.allowed = model.allowed; - m.collection = model.collection; - m.plugin = model.plugin; - }) - .fetch() - ; -} - -// initial refresh -refresh(); - -// create or update general graph settings -Setting - .spawn() - .set({plugin: 'graph', name: 'graph'}) - .save() -; - - diff --git a/lib/plugins/settings/index.js b/lib/plugins/settings/index.js index 52500c5..4cb9b43 100644 --- a/lib/plugins/settings/index.js +++ b/lib/plugins/settings/index.js @@ -2,19 +2,10 @@ var app = require('../../app') , config = require('../../config').load() , Settings = require('./settings') , Setting = require('./setting') - , graph = require('../graph') + , models = require('../models') , Model = require('../../model') ; -app.get('/settings', function(req, res) { - Settings - .spawn() - .for(req) - .notify(res) - .fetch() - ; -}); - app.post('/settings', function(req, res) { Settings .spawn() @@ -22,7 +13,7 @@ app.post('/settings', function(req, res) { .find({name: req.body.name, plugin: req.body.plugin}) .set(req.body) .notify(function(json) { - if(req.body.plugin === 'graph') graph.refresh(); + if(req.body.plugin === 'models') models.refresh(); else if(json.collection) Model.refreshSettings(json.collection); res.send(json); }) diff --git a/lib/plugins/settings/setting.js b/lib/plugins/settings/setting.js index edc227a..ae58584 100644 --- a/lib/plugins/settings/setting.js +++ b/lib/plugins/settings/setting.js @@ -3,7 +3,7 @@ var Model = require('../../model'); module.exports = Model.spawn({ collection: 'settings', - plugin: 'setting', + plugin: 'settings', description: { name: {type: 'string'}, diff --git a/lib/plugins/settings/settings.js b/lib/plugins/settings/settings.js index b7f6a43..f5983e4 100644 --- a/lib/plugins/settings/settings.js +++ b/lib/plugins/settings/settings.js @@ -1,5 +1,6 @@ var Collection = require('../../collection'); module.exports = Collection.spawn({ - collection: 'settings' + collection: 'settings', + plugin: 'settings' }); \ No newline at end of file diff --git a/lib/plugins/user/group.js b/lib/plugins/user/group.js deleted file mode 100644 index e64d90b..0000000 --- a/lib/plugins/user/group.js +++ /dev/null @@ -1,34 +0,0 @@ -var Model = require('../../model'); - -var Group = module.exports = Model.spawn({ - - collection: 'groups', - plugin: 'user', - - description: { - name: {type: 'string', unique: true}, - creator: 'string' - }, - - allowed: { - read: 'root', - write: 'creator', - remove: 'creator', - create: 'root' - } - -}); - -var defaults = ['root', 'admin', 'public']; - -defaults.forEach(function(group) { - var g = {name: group, creator: 'root'}; - - Group - .spawn() - .unlock() - .find(g) - .set(g) - .save() - ; -}); \ No newline at end of file diff --git a/lib/plugins/user/index.js b/lib/plugins/user/index.js deleted file mode 100644 index 915f462..0000000 --- a/lib/plugins/user/index.js +++ /dev/null @@ -1,82 +0,0 @@ -var app = require('../../app') - , Group = require('./group') - , User = require('./user') - , Users = require('./users') - , ObjectID = require('mongodb').BSON -; - -function user(action, params, req, res) { - User - .spawn() - .for(req) - .set(params) - .notify(res) - [action]() - ; -} - -app.post('/user', function(req, res) { - user('save', req.body, req, res); -}); - -app.post('/user/login', function(req, res) { - user('login', req.body, req, { - send: function(u) { - u.auth = req.sessionID; - res.send(req.session.user = u); - } - }); -}); - -app.get('/user/logout', function(req, res) { - req.session.destroy(function() { - res.send({auth: null}); - }); -}); - -app.get('/me', function(req, res) { - user('fetch', {email: req.session.user && req.session.user.email}, req, res); -}); - -app.del('/me', function(req, res) { - var u = req.session.user; - if(u) user('remove', u, req, res); -}); - -app.get('/user/:id', function(req, res) { - User - .spawn() - .for(req) - .find({_id: req.param('id')}) - .notify(res) - .fetch() - ; -}); - -app.post('/user/:email/group', function(req, res) { - var changes = {} - , group = req.body && req.body.group - ; - - // TODO validate group - changes['groups.' + group] = 1; - User - .spawn() - .for(req) - .find({email: req.param('email')}) - .set({$set: changes}) - .notify(res) - .save() - ; -}); - -// app.get('/users', function(req, res) { -// Users -// .spawn({ -// query: req.params -// }) -// .for(req) -// .notify(res) -// .fetch() -// ; -// }); diff --git a/lib/plugins/user/user.js b/lib/plugins/user/user.js deleted file mode 100644 index 156ac3d..0000000 --- a/lib/plugins/user/user.js +++ /dev/null @@ -1,59 +0,0 @@ -var Model = require('../../model') - , ObjectID = require('mongodb').BSONPure.ObjectID - , _ = require('underscore') -; - -module.exports = Model.spawn({ - - collection: 'users', - - plugin: 'user', - - description: { - email: {type: 'email', unique: true}, - password: 'password', - name: 'string', - auth: 'string', - removed: 'boolean', - groups: 'object' - }, - - allowed: { - read: 'public', - write: 'creator', - remove: 'creator', - create: 'public', - special: { - groups: {read: 'public', write: 'root'}, - email: {read: 'creator'} - } - }, - - toJSON: function() { - var j = Model.toJSON.apply(this, arguments); - - // remove password before sending to clients - delete j.password; - - return j; - }, - - set: function(changes) { - // prevent ever storing a real password - changes.password && (changes.password = this.hash(changes.password)); - - return Model.set.apply(this, arguments); - }, - - login: function() { - if(!this.get('email') || !this.get('password')) this.error('User email and password are required', 'Required Field'); - - return this.fetch(); - }, - - hash: function(password) { - return password + 'hash!'; - } - -}); - diff --git a/lib/plugins/user/users.js b/lib/plugins/user/users.js deleted file mode 100644 index 793a8f1..0000000 --- a/lib/plugins/user/users.js +++ /dev/null @@ -1,8 +0,0 @@ -var Collection = require('../../collection') - , User = require('./user') -; - -module.exports = Collection.spawn({ - collection: 'users', - model: User -}); \ No newline at end of file diff --git a/public/test/deployd.test.js b/public/test/deployd.test.js index 4cd1473..29b4460 100644 --- a/public/test/deployd.test.js +++ b/public/test/deployd.test.js @@ -23,7 +23,7 @@ var app = { var tests = { '1. creating a user': { - route: '/user', + route: '/users', data: user, expect: { _id: 'toExist', @@ -34,7 +34,7 @@ var tests = { }, '3. add a user to group': { - route: '/user/' + user.email + '/group', + route: '/users/' + user.email + '/group', data: {group: 'root'}, expect: { errors: 'toNotExist' @@ -42,7 +42,7 @@ var tests = { }, '4. login a user': { - route: '/user/login', + route: '/users/login', data: user, expect: { _id: 'toExist', @@ -102,7 +102,7 @@ var tests = { }, '9. validate users': { - route: '/user', + route: '/users', data: {asdf: 1234, uid: {foo: 'bar'}, password: 1111}, expect: { errors: 'toExist'