├── screenshot.png ├── README.md ├── package.json └── index.js /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackergrrl/gitverse/HEAD/screenshot.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitverse 2 | 3 | > local offline p2p social git frontend 4 | 5 | ## usage 6 | 7 | Install [hypergit](https://github.com/noffle/hypergit) and run `hypergit web` to 8 | start a localhost web server. 9 | 10 | ## screenshot 11 | 12 | ![gitverse screenshot](screenshot.png) 13 | 14 | ## discuss 15 | 16 | IRC: freenode `#gitverse` 17 | 18 | ## contributors 19 | 20 | With hypergit, each project participant has their own hypergit repo. There is no 21 | "origin" central authority. 22 | 23 | - @noffle: `hypergit://80644c2380623f090bc6384b1792d3e61ddb2951d9145f23e8a47708145da503` 24 | 25 | If you'd like your hypergit added, open a PR. 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitverse", 3 | "description": "p2p git social frontend", 4 | "author": "Stephen Whitmore ", 5 | "version": "1.0.2", 6 | "repository": { 7 | "url": "git://github.com/noffle/gitverse.git" 8 | }, 9 | "homepage": "https://github.com/noffle/gitverse", 10 | "bugs": "https://github.com/noffle/gitverse/issues", 11 | "main": "index.js", 12 | "scripts": { 13 | "test": "tape test/*.js", 14 | "lint": "standard" 15 | }, 16 | "keywords": [], 17 | "dependencies": { 18 | "http": "0.0.0", 19 | "hyperdb-git-repo": "^1.0.2", 20 | "marked": "^0.4.0", 21 | "pull-cat": "^1.1.11", 22 | "pull-git-repo": "^1.2.1", 23 | "pull-stream": "^3.6.8", 24 | "routes": "^2.1.0", 25 | "stream-to-pull-stream": "^1.7.2" 26 | }, 27 | "devDependencies": { 28 | "tape": "~4.6.2", 29 | "standard": "~10.0.0" 30 | }, 31 | "license": "non-commercial" 32 | } 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var pull = require('pull-stream') 2 | var toPull = require('stream-to-pull-stream') 3 | var cat = require('pull-cat') 4 | var Repo = require('hyperdb-git-repo') 5 | var RepoPlusPlus = require('pull-git-repo') 6 | var marked = require('marked') 7 | var routes = require('routes') 8 | 9 | module.exports = function (dbs) { 10 | var keyToDb = {} 11 | dbs.forEach(function (db) { 12 | keyToDb[db.key.toString('hex')] = { 13 | db: db, 14 | repo: RepoPlusPlus(Repo(db)) 15 | } 16 | }) 17 | 18 | var router = routes() 19 | router.addRoute('/', serveRoot) 20 | router.addRoute('/:hash([0-9a-f]{64})', serveRepo) 21 | router.addRoute('/:hash/blob/*', serveBlob) 22 | 23 | var state = { 24 | dbs: dbs, 25 | keyToDb: keyToDb 26 | } 27 | 28 | return function (req, res) { 29 | res.setHeader('content-type', 'text/html') 30 | 31 | var m = router.match(req.url) 32 | pull( 33 | m ? m.fn(req, res, state, m) : pull.once('404'), 34 | toPull.sink(res) 35 | ) 36 | } 37 | } 38 | 39 | function serveRoot (req, res, state, m) { 40 | return pull( 41 | cat([ 42 | writeTitle(), 43 | pull.once('

Local repositories:

'), 44 | writeRepos(state.dbs) 45 | ]) 46 | ) 47 | } 48 | 49 | function serveRepo (req, res, state, m) { 50 | var e = state.keyToDb[m.params.hash] 51 | return cat([ 52 | writeTitle(), 53 | writeRepo(e.db, e.repo) 54 | ]) 55 | } 56 | 57 | function serveBlob (req, res, state, m) { 58 | var e = state.keyToDb[m.params.hash] 59 | return writeBlob(e.db, e.repo, m.splats[0]) 60 | } 61 | 62 | function writeTitle () { 63 | return pull.once('

gitverse

scrappy p2p git with friends

') 64 | } 65 | 66 | function writeRepos (dbs) { 67 | return pull( 68 | pull.values(dbs), 69 | pull.map(function (db) { 70 | var key = db.key.toString('hex') 71 | return '
  • ' + key + '
  • ' 72 | }) 73 | ) 74 | } 75 | 76 | function writeRepo (db, repo) { 77 | return cat([ 78 | pull.once('

    ' + db.key.toString('hex') + '

    '), 79 | 80 | //pull.once('

    Tags

    '), 81 | //pull( 82 | // getTags(db), 83 | // pull.map(function (tag) { 84 | // return `
  • ${tag.name}: ${tag.hash}
  • ` 85 | // }) 86 | //), 87 | 88 | //pull.once('

    Branches

    '), 89 | //pull( 90 | // getBranches(db), 91 | // pull.map(function (branch) { 92 | // return `
  • ${branch.name}: ${branch.hash}
  • ` 93 | // }) 94 | //), 95 | 96 | // files list 97 | deferred(function (cb) { 98 | repo.resolveRef('HEAD', function (err, hash) { 99 | if (err) return cb(null, pull.once('empty repo')) 100 | pull( 101 | repo.readCommit(hash), 102 | pull.drain(function (field) { 103 | if (field.name === 'tree') { 104 | cb(null, writeTree(db, repo, field.value)) 105 | } 106 | }) 107 | ) 108 | }) 109 | }), 110 | // readme 111 | deferred(function (cb) { 112 | repo.resolveRef('HEAD', function (err, hash) { 113 | if (err) return cb(null, pull.once('')) 114 | pull( 115 | repo.readCommit(hash), 116 | pull.drain(function (field) { 117 | if (field.name === 'tree') { 118 | pull( 119 | repo.readTree(field.value), 120 | pull.filter(function (elm) { 121 | return /(readme|README).(md|markdown)/.test(elm.name) 122 | }), 123 | pull.collect(function (err, elms) { 124 | var hash = (elms && elms.length && elms[0].id) || null 125 | cb(null, writeReadme(db, repo, hash)) 126 | }) 127 | ) 128 | } 129 | }) 130 | ) 131 | }) 132 | }), 133 | ]) 134 | } 135 | 136 | function getTags (db) { 137 | return pull( 138 | toPull.source(db.createReadStream('git/refs')), 139 | pull.map(function (nodes) { 140 | var node = nodes[0] 141 | if (!/tag/.test(node.key)) return null 142 | var tag = node.key.replace('git/refs/tags/', '') 143 | return { 144 | name: tag, 145 | hash: node.value 146 | } 147 | }), 148 | pull.filter(function (data) { 149 | return !!data 150 | }) 151 | ) 152 | } 153 | 154 | function getBranches (db) { 155 | return pull( 156 | toPull.source(db.createReadStream('git/refs/heads')), 157 | pull.map(function (nodes) { 158 | var node = nodes[0] 159 | var branch = node.key.replace('git/refs/heads/', '') 160 | return { 161 | name: branch, 162 | hash: node.value 163 | } 164 | }), 165 | pull.filter(function (data) { 166 | return !!data 167 | }) 168 | ) 169 | } 170 | 171 | function deferredValue (fn) { 172 | var ended 173 | return function (end, cb) { 174 | fn(function (err, data) { 175 | if (err || ended) return cb(err || ended) 176 | ended = true 177 | cb(null, data) 178 | }) 179 | } 180 | } 181 | 182 | function deferred (fn) { 183 | var next 184 | return function (end, cb) { 185 | if (next) return next(end, cb) 186 | fn(function (err, _next) { 187 | if (err) return cb(err) 188 | next = _next 189 | next(null, cb) 190 | }) 191 | } 192 | } 193 | 194 | function writeTree (db, repo, hash) { 195 | var repoId = db.key.toString('hex') 196 | return cat([ 197 | pull.once('
    '), 198 | 199 | // tree data 200 | pull( 201 | repo.readTree(hash), 202 | pull.map(function (elm) { 203 | return `
  • 204 | ${elm.name}
  • 205 | ` 206 | }) 207 | ), 208 | 209 | pull.once('
    ') 210 | ]) 211 | } 212 | 213 | function writeReadme (db, repo, hash) { 214 | if (hash === null) return pull.once('no readme found') 215 | 216 | return deferred(function (cb) { 217 | repo.getRef(hash, function (err, object, id) { 218 | if (err) throw err 219 | pull( 220 | object.read, 221 | pull.collect(function (err, bufs) { 222 | if (err) return cb(null, pull.once(err.toString())) 223 | var text = Buffer.concat(bufs).toString() 224 | var body = `${marked(text)}` 225 | cb(null, pull.once(body)) 226 | }) 227 | ) 228 | }) 229 | }) 230 | } 231 | 232 | function writeBlob (db, repo, hash) { 233 | return deferred(function (cb) { 234 | repo.getRef(hash, function (err, object, id) { 235 | if (err) throw err 236 | pull( 237 | object.read, 238 | pull.collect(function (err, bufs) { 239 | if (err) return cb(null, pull.once(err.toString())) 240 | var text = Buffer.concat(bufs).toString() 241 | var body = `${text}` 242 | cb(null, pull.once(body)) 243 | }) 244 | ) 245 | }) 246 | }) 247 | } 248 | --------------------------------------------------------------------------------