UNPKG

pushhub

Version:

A github inspired interface to browse your Git repositories, built on top of expressjs and pushover

278 lines (234 loc) 7.14 kB
"use strict"; var path = require('path'); var fs = require('fs'); var express = require('express'); var mime = require('mime'); var pushover = require('pushover'); var Extensions = require('./lib/extensions'); var Repo = require('./lib/repo'); var utils = require('./lib/utils'); var middleware = require('./lib/middleware.js'); var join = path.join; var extname = path.extname; var debug = require('debug')('pushhub'); var gitServer; var repos = {}; var app = module.exports = express(); // Helpers function cache(repo) { repo.mtime(utils.noop); repo.branches(function(err, branches) { if(branches) { branches.forEach(function(branch) { repo.total(branch, utils.noop); repo.tip(branch, utils.noop); }); } }); repo.tags(function(err, tags) { if(tags) { tags.forEach(function(tag) { repo.total(tag, utils.noop); repo.tip(tag, utils.noop); }); } }); } // Actions function home(req, res) { var repo, date; for (var entry in repos) { repo = repos[entry]; date = repo.cache.get('mtime').toString().split(' '); repo.mtime = date.slice(1, -2).join(' '); repos[entry] = repo; } res.render('home.jade', { repos: repos }); } function tree(req, res) { var name = req.params.name, ref = req.params.ref || 'master', path = req.params[0] || '.', repo = repos[name]; if(repo) { repo.tree(ref, path, function(err, items) { if(err || !items.length) { return res.render('404.jade'); } res.render('tree.jade', { 'view': 'tree', 'repo': name, 'description': repo.description(), 'ref': ref, 'commit': repo.cache.get('tip:' + ref), 'parents': utils.parents(name, ref, path), 'items': items, 'branches': repo.cache.get('branches'), 'tags': repo.cache.get('tags') }); }); } else { res.render('404.jade'); } } function blob(req, res) { var name = req.params.name, ref = req.params.ref || 'master', path = req.params[0], repo = repos[name]; if(repo) { repo.blob(ref, path, function(err, data) { if(err) { throw err; } res.render('blob.jade', { 'view': 'blob', 'repo': name, 'description': repo.description(), 'ref': ref, 'mime': mime.lookup(path), 'parents': utils.parents(name, ref, path), 'filetype': Extensions[extname(path)], 'rawURL': req.url.replace('blob', 'raw'), 'data': data, 'branches': repo.cache.get('branches'), 'tags': repo.cache.get('tags') }); }); } else { res.render('404.jade'); } } function raw(req, res) { var name = req.params.name, ref = req.params.ref || 'master', path = req.params[0], repo = repos[name]; if(repo) { repo.blob(ref, path, function(err, data) { if(err) { throw err; } var m = mime.lookup(path); if(m.indexOf('text') === 0) { m = 'text/plain'; } res.setHeader('content-type', m); res.end(data); }); } else { res.render('404.jade'); } } function history(req, res) { var name = req.params.name, ref = req.params.ref, page = parseInt(req.query['page'], 10), skip = 0, repo = repos[name], bypage = app.get('history by page'); if(!isNaN(page)) { skip = (page - 1) * bypage; } else { page = 1; } var count = repo.cache.get('total:' + ref); var next = page * bypage + bypage <= count ? page + 1 : null; var previous = page * bypage - bypage > 0 ? page - 1 : null; if(repo) { repo.stats(ref, '.', { maxcount: bypage, skip: skip}, function(err, entry) { if(err) { throw err; } res.render('history.jade', { 'view': 'history', 'repo': name, 'ref': ref, 'description': repo.description(), 'current': page, 'pages': Math.floor(count / bypage), 'next': next, 'previous': previous, 'history': entry.commits.asArray(), 'branches': repo.cache.get('branches'), 'tags': repo.cache.get('tags') }); }); } else { res.render('404.jade') } } function archive(req, res) { var name = req.params.name, ref = req.params.ref, format = req.params.format === 'zipball' ? 'zip' : 'tar.gz', repo = repos[name]; if(repo) { repo.archive(ref, format, function(err, archive) { if(err) { throw err; } res.end(archive); }); } else { res.render('404.jade'); } } function description(req, res) { var name = req.params.name, repo = repos[name], method = req.method, description = req.body.description; if(method == 'POST') { if(!description) { return res.send(400); } repo.description(description); return res.json({ok: true}); } else { res.json({'description': repo.description()}); } } function setup(parent) { app.locals.app = parent || app; // Setting up pushover var gitRoot = app.get('git root'); gitServer = pushover(gitRoot); function register() { fs.readdirSync(gitRoot).forEach(function(dir) { var p = join(gitRoot, dir); if(!utils.isGitDir(p)) { return; } if(!repos[dir]) { debug('Registering "%s"', dir); repos[dir] = new Repo(p); cache(repos[dir]); } }); } gitServer.on('create', function(dir) { if(!repos[dir]) { debug('Creating "%s"', dir); register(dir); } }); gitServer.on('push', function(push) { debug('Pushed to "%s", flushing cache', push.repo); push.accept(); }); fs.watch(gitRoot, register); register(); app.use(express.bodyParser()); app.use(express.favicon()); app.use(express.static(join(__dirname, 'public'))); app.use(express.logger('dev')); app.use(middleware(gitServer)); app.get('/', home); app.get('/:name', tree); app.get('/:name/tree/:ref/', tree); app.get('/:name/tree/:ref/*', tree); app.get('/:name/blob/:ref/*', blob); app.get('/:name/raw/:ref/*', raw); app.get('/:name/commits/:ref', history); app.get('/:name/:format/:ref', archive); app.get('/:name/description', description); app.post('/:name/description', description); } app.set('views', join(__dirname, 'views')); app.set('view options', {layout: false}); app.set('git root', process.cwd()); app.set('history by page', 10); app._listen = app.listen; app.listen = function listen() { app.emit('listening'); app._listen.apply(app, arguments); }; app.on('listening', setup); app.on('mount', setup);