Skip to content

Commit a06d47e

Browse files
committed
Merge branch 'PR5'
2 parents c9b8bf2 + a714f32 commit a06d47e

101 files changed

Lines changed: 1644 additions & 10131 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.bowerrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"directory": "public/lib",
3+
"storage": {
4+
"packages": ".bower-cache",
5+
"registry": ".bower-registry"
6+
},
7+
"tmp": ".bower-tmp"
8+
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
workspaces/*
22
node_modules
33
config.js
4+
.bower-*/
5+
public/lib
6+
.c9revisions
7+
.settings
8+
public/css/*

bower.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "cloud9hub",
3+
"version": "0.0.1",
4+
"dependencies": {
5+
"angular": "~1.2.15",
6+
"topcoat": "~0.8.0",
7+
"angular-route": "~1.2.15",
8+
"fontawesome": "~4.0.3",
9+
"flat-ui-official": "~2.1.3"
10+
}
11+
}

config.js.example

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
exports.GITHUB_CLIENT_ID = '3909fc1ee9f8ed9e87f2';
22
exports.GITHUB_CLIENT_SECRET = 'e5b3802ef773d8d32a8b29fe958d04e591be850d';
3+
34
// This allows everybody to sign up
45
exports.PERMITTED_USERS = false;
5-
// This restricts signup to the users alice and bob:
6+
// This would only allow alice and bob to sign up
67
//exports.PERMITTED_USERS = ['alice', 'bob'];
7-
exports.BASE_URL = 'http://localhost';
8+
89
// Add SSL Certificates to use Cloud9Hub over SSL
910
// (Cloud9 Workspaces will still be unsecured standard HTTP)
1011
//exports.SSL = {
1112
// key: "/path/to/ssl.key",
1213
// cert: "/path/to/ssl.pem"
1314
//};
15+
16+
exports.BASE_URL = 'http://localhost';

controllers/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
/*
3+
* GET home page.
4+
*/
5+
6+
exports.index = function(req, res){
7+
res.render('index', { title: 'Express' });
8+
};

controllers/login.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
/*
3+
* GET home page.
4+
*/
5+
6+
exports.login = function(req, res){
7+
res.render('login', {});
8+
};

controllers/workspaces.js

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
var fs = require('fs'),
2+
path = require('path'),
3+
rimraf = require('rimraf'),
4+
_ = require('lodash'),
5+
spawn = require('child_process').spawn;
6+
7+
var respondInvalidWorkspace = function(res) {
8+
res.status(400);
9+
res.json({msg: "Invalid workspace name"});
10+
};
11+
12+
var createWorkspace = function(params, req, res) {
13+
var potentiallyBadPathName = params.name.split(path.sep);
14+
var workspaceName = potentiallyBadPathName[potentiallyBadPathName.length-1];
15+
16+
if(workspaceName === '..') {
17+
respondInvalidWorkspace(res);
18+
return;
19+
}
20+
21+
fs.mkdir(__dirname + '/../workspaces/' + req.user + "/" + workspaceName, '0700', function(err) {
22+
if(err) {
23+
respondInvalidWorkspace(res);
24+
return;
25+
}
26+
27+
res.json({msg: "Workspace " + workspaceName + " was created."});
28+
});
29+
}
30+
31+
var createWorkspaceKillTimeout = function(req, workspaceProcess, workspaceName) {
32+
var timeout = setTimeout(function() {
33+
process.kill(-workspaceProcess.pid, 'SIGTERM');
34+
req.app.get('runningWorkspaces')[req.user + '/' + workspaceName] = undefined;
35+
console.info("Killed workspace " + workspaceName);
36+
}, 900000); //Workspaces have a lifetime of 15 minutes
37+
38+
return timeout;
39+
};
40+
41+
/*
42+
* POST/GET create a new workspace
43+
*/
44+
exports.create = function(req, res) {
45+
if(req.body.name) {
46+
createWorkspace(req.body, req, res);
47+
} else {
48+
respondInvalidWorkspace(res);
49+
}
50+
}
51+
52+
/*
53+
* GET workspaces listing.
54+
*/
55+
exports.list = function(req, res){
56+
fs.readdir(__dirname + '/../workspaces/' + req.user, function(err, files) {
57+
if(err) {
58+
res.status(500);
59+
res.json({error: err});
60+
} else {
61+
var workspaces = [];
62+
for(var i=0; i< files.length; i++) {
63+
// Skip hidden files
64+
if(files[i][0] === '.') continue;
65+
66+
workspaces.push({name: files[i]})
67+
}
68+
res.json({workspaces: workspaces});
69+
}
70+
});
71+
};
72+
73+
/**
74+
* DELETE destroys a workspace
75+
*/
76+
exports.destroy = function(req, res) {
77+
var potentiallyBadPathName = req.params.name.split(path.sep);
78+
var workspaceName = potentiallyBadPathName[potentiallyBadPathName.length-1];
79+
80+
if(workspaceName === '..') {
81+
respondInvalidWorkspace(res);
82+
return;
83+
}
84+
85+
rimraf(__dirname + "/../workspaces/" + req.user + "/" + workspaceName, function(err) {
86+
if(err) {
87+
res.status("500");
88+
res.json({msg: "Something went wrong :("});
89+
return;
90+
}
91+
res.json({msg: "Successfully deleted " + workspaceName});
92+
})
93+
};
94+
95+
/*
96+
* GET run a workspace
97+
*/
98+
exports.run = function(req, res) {
99+
var potentiallyBadPathName = req.params.name.split(path.sep);
100+
var workspaceName = potentiallyBadPathName[potentiallyBadPathName.length-1];
101+
102+
var isPortTaken = function(port, fn) {
103+
console.log('checking if port', port, 'is taken');
104+
var net = require('net')
105+
var tester = net.createServer()
106+
.once('error', function (err) {
107+
if (err.code != 'EADDRINUSE') return fn(err)
108+
console.log('port', port, 'seems to be taken');
109+
fn(null, true)
110+
})
111+
.once('listening', function() {
112+
tester.once('close', function() {
113+
console.log('port', port, 'seems to be available');
114+
fn(null, false)
115+
})
116+
.close()
117+
})
118+
.listen(port)
119+
};
120+
121+
var getNextAvailablePort = function(callback){
122+
var nextFreeWorkspacePort = req.app.get('nextFreeWorkspacePort');
123+
124+
if(nextFreeWorkspacePort > 10000) {
125+
nextFreeWorkspacePort = 5000;
126+
}
127+
128+
nextFreeWorkspacePort = nextFreeWorkspacePort + 1;
129+
console.log('setting nextFreeWorkspacePort to', nextFreeWorkspacePort);
130+
req.app.set('nextFreeWorkspacePort', nextFreeWorkspacePort);
131+
132+
isPortTaken(nextFreeWorkspacePort, function(err, taken){
133+
if(taken){
134+
getNextAvailablePort(callback);
135+
} else {
136+
req.app.set('nextFreeWorkspacePort', nextFreeWorkspacePort);
137+
callback(nextFreeWorkspacePort);
138+
}
139+
});
140+
};
141+
142+
if(workspaceName === '..') {
143+
respondInvalidWorkspace(res);
144+
return;
145+
}
146+
147+
if(typeof req.app.get('runningWorkspaces')[req.user + '/' + workspaceName] === 'undefined'){
148+
getNextAvailablePort(function(nextFreePort){
149+
console.log("Starting " + __dirname + '/../../c9/bin/cloud9.sh for workspace ' + workspaceName + " on port " + nextFreePort);
150+
151+
var workspace = spawn(__dirname + '/../../c9/bin/cloud9.sh', ['-w', __dirname + '/../workspaces/' + req.user + '/' + workspaceName, '-l', '0.0.0.0', '-p', nextFreePort], {detached: true});
152+
workspace.stderr.on('data', function (data) {
153+
console.log('stdERR: ' + data);
154+
});
155+
156+
req.app.get('runningWorkspaces')[req.user + '/' + workspaceName] = {
157+
killTimeout: createWorkspaceKillTimeout(req, workspace, workspaceName),
158+
process: workspace,
159+
name: workspaceName,
160+
url: req.app.settings.baseUrl + ":" + nextFreePort,
161+
user: req.user
162+
};
163+
164+
res.json({msg: "Attempted to start workspace", user: req.user, url: req.app.settings.baseUrl + ":" + nextFreePort});
165+
});
166+
} else {
167+
console.log("Found running workspace", req.app.get('runningWorkspaces')[req.user + '/' + workspaceName].url);
168+
res.json({msg: "Found running workspace", user: req.user, url: req.app.get('runningWorkspaces')[req.user + '/' + workspaceName].url});
169+
}
170+
171+
}
172+
173+
/*
174+
* POST to keep the workspace alive
175+
*/
176+
exports.keepAlive = function(req, res) {
177+
var workspace = req.app.get('runningWorkspaces')[req.user + '/' + req.params.name];
178+
clearTimeout(workspace.killTimeout);
179+
workspace.killTimeout = createWorkspaceKillTimeout(req, workspace.process, workspace.name);
180+
res.send();
181+
}

gruntfile.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict';
2+
3+
module.exports = function(grunt) {
4+
// Project Configuration
5+
grunt.initConfig({
6+
pkg: grunt.file.readJSON('package.json'),
7+
watch: {
8+
options: {
9+
livereload: true,
10+
},
11+
js: {
12+
files: ['gruntfile.js', 'config.js', 'server.js', 'controllers/**', 'routes/**', 'public/js/**']
13+
},
14+
html: {
15+
files: ['views/**', 'public/*.html', 'public/partials/**']
16+
},
17+
css: {
18+
files: ['public/css/**']
19+
},
20+
sass: {
21+
files: ['public/scss/**'],
22+
tasks: ['sass'],
23+
options: {
24+
livereload: false
25+
}
26+
}
27+
},
28+
sass: {
29+
dist: {
30+
files: {
31+
'public/css/style.css': 'public/scss/style.scss'
32+
}
33+
}
34+
},
35+
nodemon: {
36+
dev: {
37+
script: 'server.js',
38+
options: {
39+
args: [],
40+
ignore: ['public/**'],
41+
ext: 'js',
42+
nodeArgs: ['--debug'],
43+
delayTime: 1,
44+
env: {
45+
PORT: 3105
46+
},
47+
cwd: __dirname
48+
}
49+
}
50+
},
51+
concurrent: {
52+
tasks: ['nodemon', 'watch'],
53+
options: {
54+
logConcurrentOutput: true
55+
}
56+
}
57+
});
58+
59+
//Load NPM tasks
60+
grunt.loadNpmTasks('grunt-contrib-watch');
61+
grunt.loadNpmTasks('grunt-nodemon');
62+
grunt.loadNpmTasks('grunt-concurrent');
63+
grunt.loadNpmTasks('grunt-sass');
64+
65+
//Making grunt default to force in order not to break the project.
66+
grunt.option('force', true);
67+
68+
//Default task(s).
69+
grunt.registerTask('default', ['sass', 'concurrent']);
70+
};

install.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ echo "-----------------------"
66
git clone https://github.com/ajaxorg/cloud9.git c9
77
cd c9
88
npm install
9+
npm install -g bower
10+
bower install
911
cd ..
1012

1113
echo "Success."

package.json

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,24 @@
66
"start": "node server.js"
77
},
88
"dependencies": {
9+
"connect-flash": "^0.1.1",
10+
"consolidate": "^0.10.0",
911
"express": "3.2.6",
12+
"forever": "~0.10.11",
13+
"grunt": "~0.4.4",
14+
"grunt-cli": "~0.1.13",
15+
"grunt-concurrent": "^0.5.0",
16+
"grunt-contrib-watch": "~0.6.1",
17+
"grunt-env": "~0.4.1",
18+
"grunt-nodemon": "~0.2.1",
19+
"grunt-sass": "^0.11.0",
20+
"jade": "*",
21+
"lodash": "^2.4.1",
1022
"passport": "0.1.17",
1123
"passport-github": "0.1.5",
12-
"jade": "*",
24+
"rimraf": "2.1.4",
1325
"stylus": "*",
14-
"rimraf": "2.1.4"
26+
"swig": "^1.3.2",
27+
"view-helpers": "^0.1.4"
1528
}
16-
}
29+
}

0 commit comments

Comments
 (0)