diff options
author | Johannes Zellner <johannes@nebulon.de> | 2015-06-27 17:07:42 +0200 |
---|---|---|
committer | Johannes Zellner <johannes@nebulon.de> | 2015-06-27 17:07:42 +0200 |
commit | 8c3ae0719e1f7d266ee04d86e7e1c3756745d372 (patch) | |
tree | b5c2c0e9d7d339490c3c8384dcffe264ae67e7de | |
parent | 08b2ad7f716b3323ab8c168eb8a00a47fda03d66 (diff) | |
download | Surfer-8c3ae0719e1f7d266ee04d86e7e1c3756745d372.tar.gz Surfer-8c3ae0719e1f7d266ee04d86e7e1c3756745d372.tar.zst Surfer-8c3ae0719e1f7d266ee04d86e7e1c3756745d372.zip |
Support recursive put
-rwxr-xr-x | app.js | 3 | ||||
-rw-r--r-- | cli/actions.js | 96 | ||||
-rw-r--r-- | cli/config.js | 62 | ||||
-rwxr-xr-x | cli/surfer.js | 39 | ||||
-rw-r--r-- | package.json | 10 | ||||
-rw-r--r-- | src/actions.js | 34 | ||||
-rw-r--r-- | src/files.js | 27 | ||||
-rwxr-xr-x | surfer.js | 25 |
8 files changed, 224 insertions, 72 deletions
@@ -4,11 +4,12 @@ | |||
4 | 4 | ||
5 | var express = require('express'), | 5 | var express = require('express'), |
6 | morgan = require('morgan'), | 6 | morgan = require('morgan'), |
7 | path = require('path'), | ||
7 | compression = require('compression'), | 8 | compression = require('compression'), |
8 | bodyParser = require('body-parser'), | 9 | bodyParser = require('body-parser'), |
9 | lastMile = require('connect-lastmile'), | 10 | lastMile = require('connect-lastmile'), |
10 | multipart = require('./src/multipart'), | 11 | multipart = require('./src/multipart'), |
11 | files = require('./src/files.js'); | 12 | files = require('./src/files.js')(path.resolve(__dirname, 'files')); |
12 | 13 | ||
13 | var app = express(); | 14 | var app = express(); |
14 | var router = new express.Router(); | 15 | var router = new express.Router(); |
diff --git a/cli/actions.js b/cli/actions.js new file mode 100644 index 0000000..592d809 --- /dev/null +++ b/cli/actions.js | |||
@@ -0,0 +1,96 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | exports.login = login; | ||
4 | exports.put = put; | ||
5 | exports.get = get; | ||
6 | exports.del = del; | ||
7 | |||
8 | var superagent = require('superagent'), | ||
9 | config = require('./config'), | ||
10 | async = require('async'), | ||
11 | fs = require('fs'), | ||
12 | path = require('path'); | ||
13 | |||
14 | require('colors'); | ||
15 | |||
16 | var API = '/api/files/'; | ||
17 | |||
18 | function checkConfig() { | ||
19 | if (!config.server()) { | ||
20 | console.log('You have run "login" first'); | ||
21 | process.exit(1); | ||
22 | } | ||
23 | } | ||
24 | |||
25 | function collectFiles(filesOrFolders) { | ||
26 | var tmp = []; | ||
27 | |||
28 | filesOrFolders.forEach(function (filePath) { | ||
29 | var stat = fs.statSync(filePath); | ||
30 | |||
31 | if (stat.isFile()) { | ||
32 | tmp.push(filePath); | ||
33 | } else if (stat.isDirectory()) { | ||
34 | var files = fs.readdirSync(filePath).map(function (file) { return path.join(filePath, file); }); | ||
35 | tmp = tmp.concat(collectFiles(files)); | ||
36 | } else { | ||
37 | console.log('Skipping %s', filePath.cyan); | ||
38 | } | ||
39 | }); | ||
40 | |||
41 | return tmp; | ||
42 | } | ||
43 | |||
44 | function login(server) { | ||
45 | console.log('Using server', server); | ||
46 | config.set('server', server); | ||
47 | } | ||
48 | |||
49 | function put(filePath, otherFilePaths) { | ||
50 | checkConfig(); | ||
51 | |||
52 | var files = collectFiles([ filePath ].concat(otherFilePaths)); | ||
53 | |||
54 | async.eachSeries(files, function (file, callback) { | ||
55 | var relativeFilePath = path.resolve(file).slice(process.cwd().length + 1); | ||
56 | |||
57 | console.log('Uploading file %s', relativeFilePath.cyan); | ||
58 | |||
59 | superagent.put(config.server() + API + relativeFilePath).attach('file', file).end(callback); | ||
60 | }, function (error) { | ||
61 | if (error) { | ||
62 | console.log('Failed to put file.', error); | ||
63 | process.exit(1); | ||
64 | } | ||
65 | |||
66 | console.log('Done'); | ||
67 | }); | ||
68 | } | ||
69 | |||
70 | function get(filePath) { | ||
71 | checkConfig(); | ||
72 | |||
73 | var relativeFilePath = path.resolve(filePath).slice(process.cwd().length + 1); | ||
74 | superagent.get(config.server() + API + relativeFilePath).end(function (error, result) { | ||
75 | if (error) return console.log('Failed', result ? result.body : error); | ||
76 | |||
77 | if (result.body && result.body.entries) { | ||
78 | console.log('Files:'); | ||
79 | result.body.entries.forEach(function (entry) { | ||
80 | console.log('\t %s', entry); | ||
81 | }); | ||
82 | } else { | ||
83 | console.log(result.text); | ||
84 | } | ||
85 | }); | ||
86 | } | ||
87 | |||
88 | function del(filePath) { | ||
89 | checkConfig(); | ||
90 | |||
91 | var relativeFilePath = path.resolve(filePath).slice(process.cwd().length + 1); | ||
92 | superagent.del(config.server() + API + relativeFilePath).end(function (error, result) { | ||
93 | if (error) return console.log('Failed', result ? result.body : error); | ||
94 | console.log('Success', result.body); | ||
95 | }); | ||
96 | } | ||
diff --git a/cli/config.js b/cli/config.js new file mode 100644 index 0000000..a3708b8 --- /dev/null +++ b/cli/config.js | |||
@@ -0,0 +1,62 @@ | |||
1 | /* jshint node:true */ | ||
2 | |||
3 | 'use strict'; | ||
4 | |||
5 | var fs = require('fs'), | ||
6 | path = require('path'), | ||
7 | safe = require('safetydance'), | ||
8 | _ = require('underscore'); | ||
9 | |||
10 | exports = module.exports = { | ||
11 | clear: clear, | ||
12 | set: set, | ||
13 | get: get, | ||
14 | unset: unset, | ||
15 | has: has, | ||
16 | |||
17 | // convenience | ||
18 | server: function () { return get('server'); } | ||
19 | }; | ||
20 | |||
21 | var HOME = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; | ||
22 | var CONFIG_FILE_PATH = path.join(HOME, '.surfer.json'); | ||
23 | |||
24 | var gConfig = (function () { | ||
25 | return safe.JSON.parse(safe.fs.readFileSync(CONFIG_FILE_PATH)) || {}; | ||
26 | })(); | ||
27 | |||
28 | function save() { | ||
29 | fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(gConfig, null, 4)); | ||
30 | } | ||
31 | |||
32 | function clear() { | ||
33 | safe.fs.unlinkSync(CONFIG_FILE_PATH); | ||
34 | } | ||
35 | |||
36 | function set(key, value) { | ||
37 | if (typeof key === 'object') { | ||
38 | _.extend(gConfig, key); | ||
39 | } else { | ||
40 | safe.set(gConfig, key, value); | ||
41 | } | ||
42 | save(); | ||
43 | } | ||
44 | |||
45 | function get(key) { | ||
46 | return safe.query(gConfig, key); | ||
47 | } | ||
48 | |||
49 | function unset(key /*, .... */) { | ||
50 | for (var i = 0; i < arguments.length; i++) { | ||
51 | gConfig = safe.unset(gConfig, arguments[i]); | ||
52 | } | ||
53 | |||
54 | save(); | ||
55 | } | ||
56 | |||
57 | function has(key /*, ... */) { | ||
58 | for (var i = 0; i < arguments.length; i++) { | ||
59 | if (!(arguments[i] in gConfig)) return false; | ||
60 | } | ||
61 | return true; | ||
62 | } | ||
diff --git a/cli/surfer.js b/cli/surfer.js new file mode 100755 index 0000000..d906d62 --- /dev/null +++ b/cli/surfer.js | |||
@@ -0,0 +1,39 @@ | |||
1 | #!/usr/bin/env node | ||
2 | |||
3 | 'use strict'; | ||
4 | |||
5 | var program = require('commander'), | ||
6 | actions = require('./actions'); | ||
7 | |||
8 | // Allow self signed certs! | ||
9 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | ||
10 | |||
11 | program.version('0.1.0'); | ||
12 | |||
13 | program.command('login') | ||
14 | .description('Login to server') | ||
15 | .action(actions.login); | ||
16 | |||
17 | program.command('put <file> [files...]') | ||
18 | .description('Put a file') | ||
19 | .action(actions.put); | ||
20 | |||
21 | program.command('get') | ||
22 | .description('Get a file or directory') | ||
23 | .action(actions.get); | ||
24 | |||
25 | program.command('del') | ||
26 | .description('Delete a file') | ||
27 | .action(actions.del); | ||
28 | |||
29 | program.parse(process.argv); | ||
30 | |||
31 | if (!process.argv.slice(2).length) { | ||
32 | program.outputHelp(); | ||
33 | } else { // https://github.com/tj/commander.js/issues/338 | ||
34 | var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2]; }); | ||
35 | if (!knownCommand) { | ||
36 | console.error('Unknown command: ' + process.argv[2]); | ||
37 | process.exit(1); | ||
38 | } | ||
39 | } | ||
diff --git a/package.json b/package.json index e5eb25d..5e97c28 100644 --- a/package.json +++ b/package.json | |||
@@ -10,20 +10,28 @@ | |||
10 | "file", | 10 | "file", |
11 | "server" | 11 | "server" |
12 | ], | 12 | ], |
13 | "bin": { | ||
14 | "surfer": "./cli/surfer.js" | ||
15 | }, | ||
13 | "author": "Johannes Zellner <johannes@nebulon.de>", | 16 | "author": "Johannes Zellner <johannes@nebulon.de>", |
14 | "license": "MIT", | 17 | "license": "MIT", |
15 | "dependencies": { | 18 | "dependencies": { |
19 | "async": "^1.2.1", | ||
16 | "body-parser": "^1.13.1", | 20 | "body-parser": "^1.13.1", |
21 | "colors": "^1.1.2", | ||
17 | "commander": "^2.8.1", | 22 | "commander": "^2.8.1", |
18 | "compression": "^1.5.0", | 23 | "compression": "^1.5.0", |
19 | "connect-lastmile": "0.0.10", | 24 | "connect-lastmile": "0.0.10", |
20 | "connect-timeout": "^1.6.2", | 25 | "connect-timeout": "^1.6.2", |
26 | "debug": "^2.2.0", | ||
21 | "ejs": "^2.3.1", | 27 | "ejs": "^2.3.1", |
22 | "express": "^4.12.4", | 28 | "express": "^4.12.4", |
23 | "mkdirp": "^0.5.1", | 29 | "mkdirp": "^0.5.1", |
24 | "morgan": "^1.6.0", | 30 | "morgan": "^1.6.0", |
25 | "multiparty": "^4.1.2", | 31 | "multiparty": "^4.1.2", |
26 | "rimraf": "^2.4.0", | 32 | "rimraf": "^2.4.0", |
27 | "superagent": "^1.2.0" | 33 | "safetydance": "0.0.16", |
34 | "superagent": "^1.2.0", | ||
35 | "underscore": "^1.8.3" | ||
28 | } | 36 | } |
29 | } | 37 | } |
diff --git a/src/actions.js b/src/actions.js deleted file mode 100644 index 33e47aa..0000000 --- a/src/actions.js +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | exports.put = put; | ||
4 | exports.get = get; | ||
5 | exports.del = del; | ||
6 | |||
7 | var superagent = require('superagent'), | ||
8 | path = require('path'); | ||
9 | |||
10 | var server = 'http://localhost:3000/api/files/'; | ||
11 | |||
12 | function put(filePath) { | ||
13 | var relativeFilePath = path.resolve(filePath).slice(process.cwd().length + 1); | ||
14 | superagent.put(server + relativeFilePath).attach('file', filePath).end(function (error, result) { | ||
15 | if (error) return console.log('Failed', result ? result.body : error); | ||
16 | console.log('Success', result.body); | ||
17 | }); | ||
18 | } | ||
19 | |||
20 | function get(filePath) { | ||
21 | var relativeFilePath = path.resolve(filePath).slice(process.cwd().length + 1); | ||
22 | superagent.get(server + relativeFilePath).end(function (error, result) { | ||
23 | if (error) return console.log('Failed', result ? result.body : error); | ||
24 | console.log('Success', result.body); | ||
25 | }); | ||
26 | } | ||
27 | |||
28 | function del(filePath) { | ||
29 | var relativeFilePath = path.resolve(filePath).slice(process.cwd().length + 1); | ||
30 | superagent.del(server + relativeFilePath).end(function (error, result) { | ||
31 | if (error) return console.log('Failed', result ? result.body : error); | ||
32 | console.log('Success', result.body); | ||
33 | }); | ||
34 | } | ||
diff --git a/src/files.js b/src/files.js index 3812d21..48f91a8 100644 --- a/src/files.js +++ b/src/files.js | |||
@@ -4,17 +4,22 @@ var fs = require('fs'), | |||
4 | path = require('path'), | 4 | path = require('path'), |
5 | ejs = require('ejs'), | 5 | ejs = require('ejs'), |
6 | rimraf = require('rimraf'), | 6 | rimraf = require('rimraf'), |
7 | debug = require('debug')('files'), | ||
7 | mkdirp = require('mkdirp'), | 8 | mkdirp = require('mkdirp'), |
8 | HttpError = require('connect-lastmile').HttpError, | 9 | HttpError = require('connect-lastmile').HttpError, |
9 | HttpSuccess = require('connect-lastmile').HttpSuccess; | 10 | HttpSuccess = require('connect-lastmile').HttpSuccess; |
10 | 11 | ||
11 | exports = module.exports = { | 12 | var gBasePath; |
12 | get: get, | 13 | |
13 | put: put, | 14 | exports = module.exports = function (basePath) { |
14 | del: del | 15 | gBasePath = basePath; |
15 | }; | ||
16 | 16 | ||
17 | var FILE_BASE = path.resolve(__dirname, '../files'); | 17 | return { |
18 | get: get, | ||
19 | put: put, | ||
20 | del: del | ||
21 | }; | ||
22 | }; | ||
18 | 23 | ||
19 | // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js | 24 | // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js |
20 | function copyFile(source, target, cb) { | 25 | function copyFile(source, target, cb) { |
@@ -54,9 +59,9 @@ function render(view, options) { | |||
54 | } | 59 | } |
55 | 60 | ||
56 | function getAbsolutePath(filePath) { | 61 | function getAbsolutePath(filePath) { |
57 | var absoluteFilePath = path.resolve(FILE_BASE, filePath); | 62 | var absoluteFilePath = path.resolve(gBasePath, filePath); |
58 | 63 | ||
59 | if (absoluteFilePath.indexOf(FILE_BASE) !== 0) return null; | 64 | if (absoluteFilePath.indexOf(gBasePath) !== 0) return null; |
60 | return absoluteFilePath; | 65 | return absoluteFilePath; |
61 | } | 66 | } |
62 | 67 | ||
@@ -68,7 +73,7 @@ function get(req, res, next) { | |||
68 | fs.stat(absoluteFilePath, function (error, result) { | 73 | fs.stat(absoluteFilePath, function (error, result) { |
69 | if (error) return next(new HttpError(404, error)); | 74 | if (error) return next(new HttpError(404, error)); |
70 | 75 | ||
71 | console.log('get', absoluteFilePath); | 76 | debug('get', absoluteFilePath); |
72 | 77 | ||
73 | if (result.isFile()) return res.sendfile(absoluteFilePath); | 78 | if (result.isFile()) return res.sendfile(absoluteFilePath); |
74 | if (result.isDirectory()) return res.status(200).send({ entries: fs.readdirSync(absoluteFilePath) }); | 79 | if (result.isDirectory()) return res.status(200).send({ entries: fs.readdirSync(absoluteFilePath) }); |
@@ -88,7 +93,7 @@ function put(req, res, next) { | |||
88 | fs.stat(absoluteFilePath, function (error, result) { | 93 | fs.stat(absoluteFilePath, function (error, result) { |
89 | if (error && error.code !== 'ENOENT') return next(new HttpError(500, error)); | 94 | if (error && error.code !== 'ENOENT') return next(new HttpError(500, error)); |
90 | 95 | ||
91 | console.log('put', absoluteFilePath, req.files.file); | 96 | debug('put', absoluteFilePath, req.files.file); |
92 | 97 | ||
93 | if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories')); | 98 | if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories')); |
94 | if (!result || result.isFile()) { | 99 | if (!result || result.isFile()) { |
@@ -106,7 +111,7 @@ function del(req, res, next) { | |||
106 | var filePath = req.params[0]; | 111 | var filePath = req.params[0]; |
107 | var absoluteFilePath = getAbsolutePath(filePath); | 112 | var absoluteFilePath = getAbsolutePath(filePath); |
108 | if (!absoluteFilePath) return next(new HttpError(404, 'Not found')); | 113 | if (!absoluteFilePath) return next(new HttpError(404, 'Not found')); |
109 | if (absoluteFilePath.slice(FILE_BASE.length) === '') return next(new HttpError(403, 'Forbidden')); | 114 | if (absoluteFilePath.slice(gBasePath.length) === '') return next(new HttpError(403, 'Forbidden')); |
110 | 115 | ||
111 | fs.stat(absoluteFilePath, function (error, result) { | 116 | fs.stat(absoluteFilePath, function (error, result) { |
112 | if (error) return next(new HttpError(404, error)); | 117 | if (error) return next(new HttpError(404, error)); |
diff --git a/surfer.js b/surfer.js deleted file mode 100755 index 703fc59..0000000 --- a/surfer.js +++ /dev/null | |||
@@ -1,25 +0,0 @@ | |||
1 | #!/usr/bin/env node | ||
2 | |||
3 | 'use strict'; | ||
4 | |||
5 | var program = require('commander'), | ||
6 | actions = require('./src/actions'); | ||
7 | |||
8 | // Allow self signed certs! | ||
9 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | ||
10 | |||
11 | program.version('0.1.0'); | ||
12 | |||
13 | program.command('put') | ||
14 | .description('Put a file') | ||
15 | .action(actions.put); | ||
16 | |||
17 | program.command('get') | ||
18 | .description('Get a file or directory') | ||
19 | .action(actions.get); | ||
20 | |||
21 | program.command('del') | ||
22 | .description('Delete a file') | ||
23 | .action(actions.del); | ||
24 | |||
25 | program.parse(process.argv); | ||