secure-scuttlebot classic
at main 4.6 kB view raw
1var fs = require('fs') 2var path = require('path') 3var pull = require('pull-stream') 4var toPull = require('stream-to-pull-stream') 5 6exports.name = 'frontend' 7exports.version = '1.0.0' 8exports.manifest = {} 9 10exports.init = function (sbot, config) { 11 if (!sbot.ws || typeof sbot.ws.use !== 'function') 12 return {} 13 14 var patchbayDir = path.join(__dirname, '..', 'patchbay') 15 var patchbayIndex = path.join(patchbayDir, 'build', 'index.html') 16 var hasPatchbay = fs.existsSync(patchbayIndex) 17 var wsCfg = config && config.ws ? config.ws : {} 18 var wsHost = typeof wsCfg.host === 'string' ? wsCfg.host : '127.0.0.1' 19 var wsPort = typeof wsCfg.port === 'number' ? wsCfg.port : 8989 20 21 if (hasPatchbay) { 22 console.log('Patchbay launched at http://' + wsHost + ':' + wsPort + '/') 23 } 24 25 function serveFile (res, filePath) { 26 var ext = path.extname(filePath) 27 var contentType = 'text/plain' 28 if (ext === '.html') contentType = 'text/html; charset=utf-8' 29 else if (ext === '.js') contentType = 'application/javascript; charset=utf-8' 30 else if (ext === '.css') contentType = 'text/css; charset=utf-8' 31 else if (ext === '.json') contentType = 'application/json; charset=utf-8' 32 33 res.writeHead(200, {'Content-Type': contentType}) 34 fs.createReadStream(filePath).pipe(res) 35 } 36 37 sbot.ws.use(function (req, res, next) { 38 var url = req.url.split('?')[0] 39 40 if (req.method === 'OPTIONS' && url === '/blobs/add') { 41 res.writeHead(204, { 42 'Access-Control-Allow-Origin': '*', 43 'Access-Control-Allow-Methods': 'POST, OPTIONS', 44 'Access-Control-Allow-Headers': 'Content-Type' 45 }) 46 return res.end() 47 } 48 49 if (req.method === 'POST' && url === '/blobs/add' && sbot.blobs && typeof sbot.blobs.add === 'function') { 50 return pull( 51 toPull.source(req), 52 sbot.blobs.add(function (err, hash) { 53 if (err) { 54 res.writeHead(500, { 55 'Content-Type': 'text/plain; charset=utf-8', 56 'Access-Control-Allow-Origin': '*' 57 }) 58 return res.end(err.message) 59 } 60 res.writeHead(200, { 61 'Content-Type': 'text/plain; charset=utf-8', 62 'Access-Control-Allow-Origin': '*' 63 }) 64 res.end(hash) 65 }) 66 ) 67 } 68 69 if (req.method === 'GET' && url.indexOf('/blobs/get/') === 0 && sbot.blobs && typeof sbot.blobs.get === 'function') { 70 var hash = decodeURIComponent(url.substring('/blobs/get/'.length)) 71 72 return sbot.blobs.has(hash, function (err, has) { 73 if (err || !has) { 74 res.writeHead(404, {'Content-Type': 'application/json'}) 75 return res.end(JSON.stringify({error: 'blob not found', id: hash})) 76 } 77 78 res.writeHead(200, {'Content-Type': 'application/octet-stream'}) 79 pull( 80 sbot.blobs.get({key: hash}), 81 toPull.sink(res) 82 ) 83 }) 84 } 85 86 if (req.method !== 'GET' && req.method !== 'HEAD') return next() 87 88 if ((url === '/' || url === '/index.html') && hasPatchbay) { 89 var host = null 90 if (req && req.headers) { 91 host = req.headers['x-forwarded-host'] || req.headers.host 92 } 93 var remote = null 94 95 if (host && sbot.id && typeof sbot.id === 'string') { 96 var i = sbot.id.indexOf('.') 97 var key = i === -1 ? sbot.id.substring(1) : sbot.id.substring(1, i) 98 99 var proto = 'http' 100 if (req.connection && req.connection.encrypted) 101 proto = 'https' 102 else if (req.headers && typeof req.headers['x-forwarded-proto'] === 'string') 103 proto = req.headers['x-forwarded-proto'].split(',')[0].trim() 104 105 var wsProto = proto === 'https' ? 'wss' : 'ws' 106 107 remote = wsProto + '://' + host + '~shs:' + key 108 } 109 110 return fs.readFile(patchbayIndex, 'utf8', function (err, html) { 111 if (err) return next(err) 112 113 if (remote) { 114 var script = '<script>window.PATCHBAY_REMOTE = ' + 115 JSON.stringify(remote) + 116 ';</script>' 117 html = html.replace('</head>', script + '</head>') 118 } 119 120 res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'}) 121 res.end(html) 122 }) 123 } 124 125 if (url === '/') url = '/index.html' 126 127 var relPath = url.replace(/^\/+/, '') 128 var patchbayPath = path.join(patchbayDir, relPath) 129 130 if (patchbayPath.indexOf(patchbayDir) !== 0 || relPath.indexOf('..') !== -1) 131 patchbayPath = null 132 133 if (!patchbayPath) return next() 134 135 fs.stat(patchbayPath, function (err2, stat2) { 136 if (err2 || !stat2 || !stat2.isFile()) return next() 137 serveFile(res, patchbayPath) 138 }) 139 }) 140 141 return {} 142}