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 a2b27c9..7831118 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();
@@ -149,6 +150,7 @@ Model = module.exports = emitter.spawn({
, actor = this.actor()
, allowed = true
, model = this
+ , User = require('./plugins/users/user')
;
if(requiresUser && !actor) {
@@ -159,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
@@ -207,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);
}
@@ -258,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];
@@ -347,7 +349,7 @@ Model = module.exports = emitter.spawn({
return this;
},
- plugin: 'graph',
+ plugin: 'models',
updateSettings: function() {
var settings = Model.spawn()
@@ -359,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,
@@ -394,12 +395,53 @@ Model = module.exports = emitter.spawn({
}
}
}
+ },
+
+ 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'
+ }
+ ;
+
+ function handler(req, res) {
+ var query = req.params
+ , action = methodMap[req.method] || 'fetch'
+ ;
+
+ model
+ .spawn()
+ .for(req)
+ .find(query)
+ .set(req.body)
+ .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) {
@@ -407,10 +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] = 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(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 bacdc59..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'