renamed plugins

This commit is contained in:
Ritchie Martori
2011-11-26 14:43:47 -08:00
parent 196246055f
commit 96e3a9be23
13 changed files with 507 additions and 0 deletions

54
lib/plugins/apps/app.js Normal file
View File

@@ -0,0 +1,54 @@
var Model = require('../../model');
module.exports = Model.spawn({
collection: 'apps',
plugin: 'apps',
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();
}
});

9
lib/plugins/apps/apps.js Normal file
View File

@@ -0,0 +1,9 @@
var Collection = require('../../collection')
, App = require('./app')
;
module.exports = Collection.spawn({
collection: 'apps',
plugin: 'apps',
model: App
});

View File

@@ -0,0 +1,74 @@
// 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 = '<h1>Not Found</h1>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';
}

49
lib/plugins/apps/index.js Normal file
View File

@@ -0,0 +1,49 @@
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');
});

View File

@@ -0,0 +1,29 @@
<div class="authed">
<h2>My Apps</h2>
<h3>alpha!</h3>
<ul id="apps">
</ul>
<h4>Create an app...</h4>
<input type="text" id="name" />
<button id="create">create new</button>
</div>
<script>
d('/search/apps', {creator: 'skawful@gmail.com'}, function(res) {
$.each(res.results, function(i, item) {
$('#apps').append(
$('<li />').text(item.name + ' @ ' + item.host + '.deploydapp.com')
)
})
});
$('#create').click(function() {
var name = $('#name').val();
d('/app', {name: name}, function(res) {
console.log(res);
if(res._id) window.location.reload();
})
});
</script>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>index</title>
<meta name="generator" content="TextMate http://macromates.com/">
<meta name="author" content="ritchie">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="/deployd.js"></script>
<script src="/libs/jquery.cookie.js"></script>
<style>
.authed, .not-authed {display: none;}
</style>
</head>
<body>
<h1>Deployd</h1>
<%- body %>
<script>
var active = $.cookie('deployd.sid') ? '.authed' : '.not-authed';
console.log(active);
$(active).show();
</script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
<div id="login-form">
Username
<input id="uid" type="text" />
Password
<input id="password" type="password" />
<button id="login">login</button>
</div>
<script>
d('/me', function(res) {
if(res && res.uid)
$('#login-form').html('Logged in as ' + res.uid);
})
$('#login').click(function() {
var info = {
email: $('#uid').val(),
password: $('#password').val()
};
d('/user/login', info, function(res) {
if(res.auth) {
window.location = '/my/apps';
} else {
alert('login failed...');
}
});
});
$('#logout').click(function() {
d('/user/logout', function() {
window.location.reload();
});
})
</script>

View File

@@ -0,0 +1,32 @@
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: 'models'})
.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: 'models', name: 'models'})
.save()
;

View File

@@ -0,0 +1,34 @@
var Model = require('../../model');
var Group = module.exports = Model.spawn({
collection: 'groups',
plugin: 'users',
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()
;
});

View File

@@ -0,0 +1,9 @@
var Collection = require('../../collection')
, Group = require('./group')
;
module.exports = Collection.spawn({
collection: 'groups',
plugin: 'users',
model: Group
});

View File

@@ -0,0 +1,83 @@
var app = require('../../app')
, Group = require('./group')
, Groups = require('./groups')
, 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('/users', function(req, res) {
user('save', req.body, req, res);
});
app.post('/users/login', function(req, res) {
user('login', req.body, req, {
send: function(u) {
u.auth = req.sessionID;
res.send(req.session.user = u);
}
});
});
app.get('/users/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('/users/:id', function(req, res) {
User
.spawn()
.for(req)
.find({_id: req.param('id')})
.notify(res)
.fetch()
;
});
app.post('/users/: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()
;
});

62
lib/plugins/users/user.js Normal file
View File

@@ -0,0 +1,62 @@
var Model = require('../../model')
, ObjectID = require('mongodb').BSONPure.ObjectID
, _ = require('underscore')
;
module.exports = Model.spawn({
collection: 'users',
plugin: 'users',
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!';
},
defineRoutes: function() {
// routes in ./index.js
}
});

View File

@@ -0,0 +1,9 @@
var Collection = require('../../collection')
, User = require('./user')
;
module.exports = Collection.spawn({
collection: 'users',
plugin: 'users',
model: User
});