├── .gitignore ├── LICENSE ├── README.md ├── bin.js ├── help.txt ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | demo 2 | node_modules 3 | Dockerfile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mathias Buus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # expose-fs 2 | 3 | Expose a file system over http 4 | 5 | ``` 6 | npm install -g expose-fs 7 | ``` 8 | 9 | ## Usage 10 | 11 | This install a command line tool called `expose-fs` 12 | 13 | ``` 14 | expose-fs . # expose . on port 8441 15 | ``` 16 | 17 | Then do 18 | 19 | ``` 20 | curl localhost:8441 # returns a directory listing in JSON format 21 | curl localhost:8441/some-file.txt # returns the file content 22 | curl -X PUT --data 'hello world' localhost:8441/some-file.txt # writes a file 23 | curl -X POST localhost/some-dir # creates a new directory 24 | ``` 25 | 26 | Run `expose-fs --help` for additional options 27 | 28 | ## License 29 | 30 | MIT -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var minimist = require('minimist') 4 | var fs = require('fs') 5 | var path = require('path') 6 | var localtunnel = require('localtunnel') 7 | var createServer = require('./') 8 | 9 | var argv = minimist(process.argv.slice(2), {alias:{port:'p', quiet:'q', help:'h', tunnel:'t'}}) 10 | var p = argv._[0] || '.' 11 | var port = argv.port || 8441 12 | 13 | if (argv.help) { 14 | console.log(fs.readFileSync(path.join(__dirname, 'help.txt'), 'utf-8')) 15 | return 16 | } 17 | 18 | var server = createServer(p) 19 | 20 | if (argv.tunnel) { 21 | var opts = {}; 22 | 23 | localtunnel(port, function(err, tunnel) { 24 | if(argv.quiet) return; 25 | if (err) return console.log("localtunnel error", e); 26 | console.log('Availabe at %s', tunnel.url); 27 | }); 28 | } 29 | 30 | if (!argv.quiet) { 31 | console.log('Exposing %s on port %d', server.root, port) 32 | 33 | server.on('file', function(name) { 34 | console.log('Returning file: %s', name) 35 | }) 36 | 37 | server.on('directory', function(name) { 38 | console.log('Returning directory: %s', name) 39 | }) 40 | } 41 | 42 | server.listen(port) -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | expose-fs [folder] [options] 2 | 3 | --port, -p Port to listen on. Defaults to 8441 4 | --quiet, -q Be quiet 5 | --tunnel, -t Use localtunnel to expose via a public url 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expose-fs", 3 | "version": "1.5.1", 4 | "description": "expose your filesystem using a http server", 5 | "main": "server.js", 6 | "dependencies": { 7 | "after-all": "^2.0.1", 8 | "cors": "^2.4.2", 9 | "localtunnel": "^1.4.0", 10 | "minimist": "^1.1.0", 11 | "mkdirp": "^0.5.0", 12 | "pump": "^1.0.0" 13 | }, 14 | "bin": { 15 | "expose-fs": "./bin.js" 16 | }, 17 | "scripts": { 18 | "start": "node server.js" 19 | }, 20 | "author": "Mathias Buus", 21 | "license": "MIT" 22 | } 23 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | var fs = require('fs') 3 | var after = require('after-all') 4 | var pump = require('pump') 5 | var path = require('path') 6 | var mkdirp = require('mkdirp') 7 | var cors = require('cors') 8 | var url = require('url') 9 | 10 | module.exports = function(root) { 11 | if (!root) root = '/' 12 | root = fs.realpathSync(root) 13 | 14 | var onrequest = function(req, res) { 15 | var qs = url.parse(req.url, true).query 16 | 17 | var trim = function(u) { 18 | u = u.replace(root, '') 19 | if (u[0] !== '/') u = '/'+u 20 | return u 21 | } 22 | 23 | var onerror = function(err) { 24 | if (!err) return res.end() 25 | res.statusCode = err.code === 'ENOENT' ? 404 : 500 26 | res.end(err.message) 27 | } 28 | 29 | var name = path.join('/', req.url.split('?')[0]).replace(/%20/g, '\ ') 30 | var u = path.join(root, name) 31 | 32 | if (req.method === 'POST') return mkdirp(u, onerror) 33 | if (req.method === 'PUT') return pump(req, fs.createWriteStream(u), onerror) 34 | 35 | var onfile = function(st) { 36 | server.emit('file', u, st) 37 | res.setHeader('Content-Length', st.size) 38 | pump(fs.createReadStream(u), res) 39 | } 40 | 41 | var ondirectory = function(st) { 42 | server.emit('directory', u, st) 43 | fs.readdir(u, function(err, files) { 44 | if (err) return onerror(err) 45 | 46 | var next = after(function() { 47 | res.end(JSON.stringify(files)) 48 | }) 49 | 50 | files.forEach(function(file, i) { 51 | var n = next() 52 | 53 | fs.stat(path.join(u, file), function(err, st) { 54 | if (err) return n(err) 55 | 56 | files[i] = { 57 | path: trim(path.join(u, file)), 58 | mountPath: path.join(u, file), 59 | type: st.isDirectory() ? 'directory' : 'file', 60 | size: st.size 61 | } 62 | 63 | n() 64 | }) 65 | }) 66 | }) 67 | } 68 | 69 | fs.stat(u, function(err, st) { 70 | if (err) return onerror(err) 71 | if (st.isDirectory()) return ondirectory(st) 72 | onfile(st) 73 | }) 74 | } 75 | 76 | var c = cors() 77 | var server = http.createServer(function(req, res) { 78 | c(req, res, function() { 79 | onrequest(req, res) 80 | }) 81 | }) 82 | 83 | server.root = root 84 | 85 | return server 86 | } --------------------------------------------------------------------------------