2019-01-11 10:28:09 +00:00
|
|
|
const http = require('http');
|
2019-01-11 06:59:42 +00:00
|
|
|
const Git = require('nodegit');
|
|
|
|
const express = require('express');
|
|
|
|
const bodyParser = require('body-parser');
|
|
|
|
const url = require('url');
|
2019-01-11 08:01:48 +00:00
|
|
|
const mkdirp = require('mkdirp');
|
2019-01-11 10:28:09 +00:00
|
|
|
const socket_io = require('socket.io');
|
|
|
|
const crypto = require('crypto');
|
2019-01-12 05:54:30 +00:00
|
|
|
const program = require('commander');
|
2019-01-11 06:59:42 +00:00
|
|
|
|
|
|
|
const app = express();
|
|
|
|
const port = 9119;
|
2019-01-11 10:28:09 +00:00
|
|
|
|
|
|
|
const server = http.createServer(app);
|
|
|
|
const io = socket_io(server);
|
|
|
|
|
2019-01-12 07:36:30 +00:00
|
|
|
const brown = '\033[33m',
|
|
|
|
green = '\033[32m',
|
|
|
|
red = '\033[32m',
|
|
|
|
reset = '\033[0m';
|
|
|
|
const progname = `${brown}reveal-sync:${reset}`
|
|
|
|
|
2019-01-12 05:54:30 +00:00
|
|
|
program
|
|
|
|
.version('1.0')
|
|
|
|
.option('-s, --secret <s>', 'Secret from Git Webhook')
|
|
|
|
.option('-p, --port <n>', 'Port to listen to', parseInt)
|
|
|
|
.option('-r, --repo-root <root>', 'Root for repo and web stuff')
|
|
|
|
.option('-m, --repo-prefix <prefix>', 'Add a URI to an allowed repo prefix', (val, memo) => { memo.push(val) }, [])
|
|
|
|
.parse(process.argv);
|
|
|
|
|
|
|
|
const default_prefixes = process.env['RV_PREFIXES'] ? process.env['RV_PREFIXES'].split(',') : [ 'https://git.xobs.io/xobs' ];
|
2019-01-11 10:28:09 +00:00
|
|
|
const config = {
|
2019-01-12 07:49:58 +00:00
|
|
|
secret: program.secret || process.env['RV_SECRET'] || null,
|
2019-01-12 05:54:30 +00:00
|
|
|
port: program.port || process.env['RV_LISTEN_PORT'] || 9119,
|
|
|
|
addr: process.env['RV_LISTEN_ADDR'] || '0.0.0.0',
|
|
|
|
repo_root: program.repoRoot || process.env['RV_ROOT'] || 'repo-root',
|
|
|
|
repo_prefixes: program.repoPrefix.length ? program.repoPrefix : default_prefixes
|
2019-01-11 10:28:09 +00:00
|
|
|
};
|
2019-01-11 06:59:42 +00:00
|
|
|
|
2019-01-11 08:01:48 +00:00
|
|
|
function update_repo(repo) {
|
2019-01-12 07:49:58 +00:00
|
|
|
console.log(`${progname} updating repo ${green}${repo}${reset}`);
|
2019-01-11 08:01:48 +00:00
|
|
|
repo.fetch('origin').then(function() {
|
|
|
|
repo.setHead('FETCH_HEAD').then(function() {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} checking out origin/master`);
|
2019-01-11 10:28:09 +00:00
|
|
|
repo.checkoutBranch('origin/master').then(() => {
|
|
|
|
return repo.getReferenceCommit('refs/remotes/origin/master');
|
|
|
|
}).catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} ${red}unable to get reference commit`);
|
2019-01-11 10:28:09 +00:00
|
|
|
console.log(e);
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${reset}`);
|
2019-01-11 10:28:09 +00:00
|
|
|
}).then(function (commit) {
|
|
|
|
Git.Reset.reset(repo, commit, 3, {});
|
|
|
|
}).catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} ${red}unable to check out master${reset}`);
|
2019-01-11 10:28:09 +00:00
|
|
|
});
|
|
|
|
}).catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} ${red}unable to set head`);
|
2019-01-11 10:28:09 +00:00
|
|
|
console.log(e);
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${reset}`);
|
2019-01-11 08:01:48 +00:00
|
|
|
});
|
2019-01-11 10:28:09 +00:00
|
|
|
}).catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} ${red}unable to set origin`);
|
2019-01-11 10:28:09 +00:00
|
|
|
console.log(e);
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${reset}`);
|
2019-01-11 08:01:48 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function redeploy_repo(url, path, name) {
|
|
|
|
mkdirp.sync(config.repo_root);
|
|
|
|
var new_path = path + '/' + name;
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} cloning ${green}${url}${reset} into ${green}${new_path}${reset}`);
|
2019-01-11 08:01:48 +00:00
|
|
|
|
|
|
|
var repo = Git.Repository.open(new_path)
|
|
|
|
.then(update_repo)
|
2019-01-11 10:28:09 +00:00
|
|
|
.catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} unable to update ${green}${url}${reset}, trying clone...`);
|
2019-01-11 10:28:09 +00:00
|
|
|
Git.Clone(url, new_path).then(update_repo).catch((e) => {
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(`${progname} ${red}unable to clone:`);
|
2019-01-11 08:01:48 +00:00
|
|
|
console.log(e);
|
2019-01-12 07:36:30 +00:00
|
|
|
console.log(reset);
|
2019-01-11 08:01:48 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-01-11 06:59:42 +00:00
|
|
|
function webhook(config, req, res) {
|
|
|
|
if (req.get('X-GitHub-Event') !== 'push')
|
|
|
|
return res.status(200).send('X-GitHub-Event was not "push"');
|
|
|
|
|
|
|
|
const wh = req.body;
|
|
|
|
|
|
|
|
// Ensure the secret is present, and matches
|
2019-01-12 07:49:58 +00:00
|
|
|
if (config['secret']) {
|
|
|
|
if (!wh['secret'] || config['secret'] !== wh['secret'])
|
|
|
|
return res.status(403).send('invalid secret token');
|
|
|
|
}
|
2019-01-11 06:59:42 +00:00
|
|
|
|
|
|
|
// Reference the repository node, which must exist
|
|
|
|
const repository = wh['repository'];
|
|
|
|
if (!repository)
|
|
|
|
return res.status(403).send('no repository found');
|
|
|
|
|
|
|
|
// Grab the git repository, if it exists
|
|
|
|
const html_url = repository['html_url'];
|
|
|
|
if (!html_url)
|
|
|
|
return res.status(403).send('no html_url found');
|
|
|
|
|
|
|
|
// Ensure the prefix is one that we recognize
|
|
|
|
found_prefix = false;
|
|
|
|
config.repo_prefixes.forEach(function(prefix) {
|
2019-01-11 08:01:48 +00:00
|
|
|
if (html_url.startsWith(prefix)) {
|
2019-01-11 06:59:42 +00:00
|
|
|
found_prefix = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!found_prefix)
|
|
|
|
return res.status(403).send('prefix does not match');
|
|
|
|
|
|
|
|
// Figure out where to place the repo
|
2019-01-11 08:01:48 +00:00
|
|
|
const website_url = url.parse(repository['website']);
|
2019-01-12 07:49:58 +00:00
|
|
|
// console.log('website_url: ' + website_url + ', repo: ' + repository['website'] + ', pathname: ' + website_url.pathname);
|
2019-01-11 06:59:42 +00:00
|
|
|
if (!website_url || !website_url.pathname)
|
|
|
|
return res.status(403).send('missing "website" parameter');
|
|
|
|
|
|
|
|
const path = website_url.pathname.split('/').slice(-1)[0];
|
|
|
|
if (!path || path === '.' || path === '..' || path === '')
|
|
|
|
return res.status(403).send('"website" parameter is not valid');
|
|
|
|
|
2019-01-12 07:49:58 +00:00
|
|
|
// console.log(`deploying to ${path} at ${config.repo_root} from ${html_url}`);
|
2019-01-11 08:01:48 +00:00
|
|
|
|
|
|
|
redeploy_repo(html_url, config.repo_root, path);
|
2019-01-11 06:59:42 +00:00
|
|
|
res.send('Ok');
|
|
|
|
}
|
|
|
|
|
2019-01-11 10:28:09 +00:00
|
|
|
var createHash = function(secret) {
|
|
|
|
var cipher = crypto.createCipher('blowfish', secret);
|
|
|
|
return(cipher.final('hex'));
|
2019-01-11 06:59:42 +00:00
|
|
|
};
|
|
|
|
|
2019-01-11 10:28:09 +00:00
|
|
|
io.on('connection', (socket) => {
|
|
|
|
socket.on('multiplex-statechanged', (data) => {
|
|
|
|
if (typeof data.secret == 'undefined'
|
|
|
|
|| data.secret == null
|
2019-01-12 07:36:30 +00:00
|
|
|
|| data.secret === '') {
|
2019-01-12 07:42:12 +00:00
|
|
|
console.log(`${progname} no secret specified`);
|
2019-01-11 10:28:09 +00:00
|
|
|
return;
|
2019-01-12 07:36:30 +00:00
|
|
|
}
|
2019-01-11 10:28:09 +00:00
|
|
|
if (createHash(data.secret) === data.socketId) {
|
|
|
|
data.secret = null;
|
|
|
|
socket.broadcast.emit(data.socketId, data);
|
2019-01-12 07:52:41 +00:00
|
|
|
console.log(`${progname} master connected on ${green}${data.socketId}${reset}`);
|
2019-01-11 10:28:09 +00:00
|
|
|
}
|
2019-01-12 07:36:30 +00:00
|
|
|
else {
|
2019-01-12 07:42:12 +00:00
|
|
|
console.log(`${progname} given secret ${red}${data.secret}${reset} doesn't match`);
|
2019-01-12 07:36:30 +00:00
|
|
|
}
|
2019-01-11 10:28:09 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-01-12 07:36:30 +00:00
|
|
|
function sendIndex(req, res) {
|
|
|
|
res.writeHead(200, {'Content-Type': 'text/html'});
|
|
|
|
res.write('<style>body{font-family: sans-serif;}</style><h2>reveal.js multiplex server.</h2><a href="/token">Generate token</a>');
|
|
|
|
res.end();
|
|
|
|
}
|
|
|
|
|
2019-01-11 06:59:42 +00:00
|
|
|
app.use(bodyParser.json()); // Parse application/json
|
|
|
|
app.use(bodyParser.urlencoded({ extended: true })); // Parse application/x-www-form-urlencoded
|
|
|
|
|
2019-01-12 07:36:30 +00:00
|
|
|
app.all(/\/\.git/, (req, res) => {
|
2019-01-12 06:01:41 +00:00
|
|
|
res.sendStatus(404);
|
|
|
|
});
|
2019-01-11 10:28:09 +00:00
|
|
|
app.post('/webhook', (req, res) => { return webhook(config, req, res); });
|
2019-01-12 07:36:30 +00:00
|
|
|
app.get("/token", (req, res) => {
|
2019-01-11 10:28:09 +00:00
|
|
|
var ts = new Date().getTime();
|
|
|
|
var rand = Math.floor(Math.random()*9999999);
|
2019-01-12 07:36:30 +00:00
|
|
|
var originalSecret = ts.toString() + rand.toString();
|
|
|
|
var cipher = crypto.createCipher('blowfish', originalSecret);
|
2019-01-11 10:28:09 +00:00
|
|
|
var secret = cipher.final('hex');
|
|
|
|
res.send({secret: secret, socketId: createHash(secret)});
|
|
|
|
});
|
2019-01-12 07:36:30 +00:00
|
|
|
app.get('/', sendIndex);
|
2019-01-11 06:59:42 +00:00
|
|
|
app.use(express.static(config.repo_root));
|
2019-01-12 07:36:30 +00:00
|
|
|
server.listen(config.port, config.addr, () => console.log(`${progname} listening on port ${green}${config.port}${reset}`));
|