diff --git a/lib/fileManager.js b/lib/fileManager.js index 3230a2e..9556f3e 100644 --- a/lib/fileManager.js +++ b/lib/fileManager.js @@ -1,13 +1,11 @@ -var fs = require('co-fs'); -var co = require('co'); -var fse = require('co-fs-extra'); -var path = require('path'); -var JSZip = require('jszip'); +const fs = require('fs-extra'); +const path = require('path'); +const JSZip = require('jszip'); -var FileManager = {}; +const FileManager = {}; -FileManager.getStats = function *(p) { - var stats = yield fs.stat(p); +FileManager.getStats = async p => { + const stats = fs.statSync(p); return { folder: stats.isDirectory(), size: stats.size, @@ -15,44 +13,44 @@ FileManager.getStats = function *(p) { } }; -FileManager.list = function *(dirPath) { - var files = yield fs.readdir(dirPath); - var stats = []; - for (var i=0; i { + const files = await fs.readdir(dirPath); + const stats = []; + for (let i = 0; i < files.length; ++i) { + const fPath = path.join(dirPath, files[i]); + const stat = await FileManager.getStats(fPath); stat.name = files[i]; stats.push(stat); } return stats; }; -FileManager.remove = function *(p) { - yield fse.remove(p); +FileManager.remove = async p => { + await fs.remove(p); }; -FileManager.mkdirs = function *(dirPath) { - yield fse.mkdirs(dirPath); +FileManager.mkdirs = async dirPath => { + await fs.mkdirs(dirPath); }; -FileManager.move = function *(srcs, dest) { - for (var i=0; i { + for (let i = 0; i < srcs.length; ++i) { + const basename = path.basename(srcs[i]); + await fs.move(srcs[i], path.join(dest, basename)); } }; -FileManager.rename = function *(src, dest) { - yield fse.move(src, dest); +FileManager.rename = async (src, dest) => { + await fs.move(src, dest); }; -FileManager.archive = function *(src, archive, dirPath, embedDirs) { - var zip = new JSZip(); - var baseName = path.basename(archive, '.zip'); +FileManager.archive = async (src, archive, dirPath, embedDirs) => { + const zip = new JSZip(); + const baseName = path.basename(archive, '.zip'); - function* addFile(file) { - var data = yield fs.readFile(file); - var name; + const addFile = async file => { + const data = await fs.readFile(file); + let name; if (embedDirs) { name = file; if (name.indexOf(dirPath) === 0) { @@ -65,30 +63,30 @@ FileManager.archive = function *(src, archive, dirPath, embedDirs) { C.logger.info('Added ' + name + ' ' + data.length + ' bytes to archive ' + archive); } - function* addDir(dir) { - var contents = yield fs.readdir(dir); - for (var file of contents) { - yield * process(path.join(dir, file)); + const addDir = async dir => { + const contents = await fs.readdir(dir); + for (const file of contents) { + await process(path.join(dir, file)); } } - function* process(fp) { - var stat = yield fs.stat(fp); + const process = async fp => { + const stat = await fs.stat(fp); if (stat.isDirectory()) { - yield * addDir(fp); + await addDir(fp); } else { - yield addFile(fp); + await addFile(fp); } } // Add each src. For directories, do the entire recursive dir. - for (var file of src) { - yield * process(file); + for (const file of src) { + await process(file); } // Generate the zip and store the final. - var data = yield zip.generateAsync({type:'nodebuffer',compression:'DEFLATE'}); - yield fs.writeFile(archive, data, 'binary'); + const data = await zip.generateAsync({type:'nodebuffer',compression:'DEFLATE'}); + await fs.writeFile(archive, data, 'binary'); }; module.exports = FileManager; diff --git a/lib/fileMap.js b/lib/fileMap.js index 50fabe2..1da0379 100644 --- a/lib/fileMap.js +++ b/lib/fileMap.js @@ -1,15 +1,16 @@ -var path = require('path'); +const path = require('path'); -var DATA_ROOT = C.data.root; +const DATA_ROOT = C.data.root; -exports.filePath = function (relPath, decodeURI) { - if (decodeURI) relPath = decodeURIComponent(relPath); - if (relPath.indexOf('..') >= 0){ - var e = new Error('Do Not Contain .. in relPath!'); +exports.filePath = (relPath, decodeURI) => { + if (decodeURI) { + relPath = decodeURIComponent(relPath); + } + if (relPath.indexOf('..') >= 0) { + const e = new Error('Do Not Contain .. in relPath!'); e.status = 400; throw e; - } - else { + } else { return path.join(DATA_ROOT, relPath); } }; diff --git a/lib/index.js b/lib/index.js index 94c6a8f..187040e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,14 +1,15 @@ #!/usr/bin/env node -var koa =require('koa'); -var path = require('path'); -var tracer = require('tracer'); -var mount = require('koa-mount'); -var morgan = require('koa-morgan'); -var koaStatic = require('koa-static'); +const path = require('path'); +const koa = require('koa'); +const tracer = require('tracer'); +const morgan = require('koa-morgan'); +const koaStatic = require('koa-static'); + +const dev = process.env.NODE_ENV !== 'production'; // Config -var argv = require('optimist') +const argv = require('optimist') .usage([ 'USAGE: $0 [-p ] [-d ]'] ) @@ -27,7 +28,7 @@ var argv = require('optimist') }) .option('help', { alias: 'h', - description: "Display This Help Message" + description: 'Display This Help Message' }) .argv; @@ -50,22 +51,20 @@ global.C = { }; // Start Server -var Tools = require('./tools'); - -var startServer = function (app, port) { - app.listen(port); - C.logger.info('listening on *.' + port); -}; +const Tools = require('./tools'); -var app = koa(); +const app = new koa(); app.proxy = true; -app.use(Tools.handelError); app.use(Tools.realIp); -app.use(morgan.middleware(C.morganFormat)); +app.use(Tools.handelError); +app.use(morgan(C.morganFormat)); -var IndexRouter = require('./routes'); -app.use(mount('/', IndexRouter)); +const router = require('./routes'); +app.use(router.routes()).use(router.allowedMethods()); app.use(koaStatic(path.join(__dirname,'./public/'))); - -startServer(app, +argv.port); - +app.listen(+argv.port, err => { + if (err) { + throw err; + } + C.logger.info('listening on *.' + argv.port); +}); diff --git a/lib/public/js/app.js b/lib/public/js/app.js index 8875c6e..81e9de9 100644 --- a/lib/public/js/app.js +++ b/lib/public/js/app.js @@ -22,7 +22,7 @@ FMApp.controller('FileManagerCtr', ['$scope', '$http', '$location', var hash2paths = function (relPath) { var paths = []; var names = relPath.split('/'); - var path = '#/'; + var path = '#!/'; paths.push({name: 'Home', path: path}); for (var i=0; i { + ctx.body = await fs.readFile(path.join(__dirname, './views/files.html')); + ctx.set('Content-Type', 'text/html'); + await next(); }); -router.get('/files', function *() { - this.body = yield render('files'); -}); - -router.get('/api/(.*)', Tools.loadRealPath, Tools.checkPathExists, function *() { - var p = this.request.fPath; - var stats = yield fs.stat(p); +const handleGet = async (ctx, next) => { + const p = ctx.req.fPath; + const stats = await fs.stat(p); if (stats.isDirectory()) { - this.body = yield * FileManager.list(p); - } - else { - //this.body = yield fs.createReadStream(p); - this.body = origFs.createReadStream(p); + ctx.body = await FileManager.list(p); + } else { + ctx.body = fs.createReadStream(p); } -}); - -router.del('/api/(.*)', Tools.loadRealPath, Tools.checkPathExists, function *() { - var p = this.request.fPath; - yield * FileManager.remove(p); - this.body = 'Delete Succeed!'; -}); + await next(); +}; -router.put('/api/(.*)', Tools.loadRealPath, Tools.checkPathExists, bodyParser(), function* () { - var type = this.query.type; - var p = this.request.fPath; +const handlePost = async (ctx, next) => { + const type = ctx.query.type; + const p = ctx.req.fPath; + const { files, src, embedDirs } = ctx.request.body; if (!type) { - this.status = 400; - this.body = 'Lack Arg Type' - } - else if (type === 'MOVE') { - var src = this.request.body.src; - if (!src || ! (src instanceof Array)) return this.status = 400; - var src = src.map(function (relPath) { - return FilePath(relPath, true); + ctx.status = 400; + ctx.body = 'Lack Arg Type!'; + } else if (type === 'CREATE_FOLDER') { + await FileManager.mkdirs(p); + ctx.body = 'Create Folder Succeed!'; + } else if (type === 'UPLOAD_FILE') { + if (files) { + const file = files.upload; + const is = fs.createReadStream(file.path); + const os = fs.createWriteStream(p); + is.pipe(os); + is.on('end', async () => { + await fs.unlink(file.path); + }); + ctx.body = 'Upload File Succeed!'; + } else { + ctx.status = 400; + ctx.body = 'Lack Upload File!'; + } + } else if (type === 'CREATE_ARCHIVE') { + if (!src) { + return ctx.status = 400; + } + const srcPath = src.map(function(file) { + return FilePath(file, true); }); - yield * FileManager.move(src, p); - this.body = 'Move Succeed!'; - } - else if (type === 'RENAME') { - var target = this.request.body.target; - if (!target) return this.status = 400; - yield * FileManager.rename(p, FilePath(target, true)); - this.body = 'Rename Succeed!'; + await FileManager.archive(srcPath, p, C.data.root, !!embedDirs); + ctx.body = 'Create Archive Succeed!'; + } else { + ctx.status = 400; + ctx.body = 'Arg Type Error!'; } - else { - this.status = 400; - this.body = 'Arg Type Error!'; - } -}); + await next(); +}; -router.post('/api/(.*)', Tools.loadRealPath, Tools.checkPathNotExists, bodyParser(), function *() { - var type = this.query.type; - var p = this.request.fPath; +const handlePut = async (ctx, next) => { + const type = ctx.query.type; + const p = ctx.req.fPath; + const { src, target } = ctx.request.body; if (!type) { - this.status = 400; - this.body = 'Lack Arg Type!'; - } - else if (type === 'CREATE_FOLDER') { - yield * FileManager.mkdirs(p); - this.body = 'Create Folder Succeed!'; - } - else if (type === 'UPLOAD_FILE') { - var formData = yield formParser(this.req); - if (formData.fieldname === 'upload'){ - var writeStream = origFs.createWriteStream(p); - formData.pipe(writeStream); - this.body = 'Upload File Succeed!'; + ctx.status = 400; + ctx.body = 'Lack Arg Type' + } else if (type === 'MOVE') { + if (!src || !(src instanceof Array)) { + return ctx.status = 400; } - else { - this.status = 400; - this.body = 'Lack Upload File!'; + const srcPath = src.map(relPath => FilePath(relPath, true)); + await FileManager.move(srcPath, p); + ctx.body = 'Move Succeed!'; + } else if (type === 'RENAME') { + if (!target) { + return ctx.status = 400; } + await FileManager.rename(p, FilePath(target, true)); + ctx.body = 'Rename Succeed!'; + } else { + ctx.status = 400; + ctx.body = 'Arg Type Error!'; } - else if (type === 'CREATE_ARCHIVE') { - var src = this.request.body.src; - if (!src) return this.status = 400; - src = src.map(function(file) { - return FilePath(file, true); - }) - var archive = p; - yield * FileManager.archive(src, archive, C.data.root, !!this.request.body.embedDirs); - this.body = 'Create Archive Succeed!'; - } - else { - this.status = 400; - this.body = 'Arg Type Error!'; - } -}); + await next(); +}; + +const handleDelete = async (ctx, next) => { + await FileManager.remove(ctx.req.fPath); + ctx.body = 'Delete Succeed!'; +}; + +router.get(/^\/api\/(?:.*)/, Tools.loadRealPath, Tools.checkPathExists, handleGet); +router.post(/^\/api\/(?:.*)/, Tools.loadRealPath, Tools.checkPathNotExists, koaBody({ + multipart: true +}), handlePost); +router.put(/^\/api\/(?:.*)/, Tools.loadRealPath, Tools.checkPathExists, koaBody(), handlePut); +router.delete(/^\/api\/(?:.*)/, Tools.loadRealPath, Tools.checkPathExists, koaBody(), handleDelete); -module.exports = router.middleware(); +module.exports = router; diff --git a/lib/tools.js b/lib/tools.js index 9546456..ed59802 100644 --- a/lib/tools.js +++ b/lib/tools.js @@ -1,50 +1,68 @@ -var fs = require('co-fs'); -var FilePath = require('./fileMap').filePath; +const fs = require('fs-extra'); +const FilePath = require('./fileMap').filePath; + +const truncate = require('truncate-utf8-bytes'); + +const illegalRe = /[\?<>\\:\*\|":]/g; +const controlRe = /[\x00-\x1f\x80-\x9f]/g; +const reservedRe = /^\.+$/; +const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; +const windowsTrailingRe = /[\. ]+$/; + +function sanitize(input, replacement) { + // Source: https://github.com/parshap/node-sanitize-filename + const sanitized = input + .replace(illegalRe, replacement) + .replace(controlRe, replacement) + .replace(reservedRe, replacement) + .replace(windowsReservedRe, replacement) + .replace(windowsTrailingRe, replacement); + return truncate(sanitized, 255); +} module.exports = { - realIp: function * (next) { - this.req.ip = this.headers['x-forwarded-for'] || this.ip; - yield *next; + realIp: async (ctx, next) => { + ctx.req.ip = ctx.req.headers['x-forwarded-for'] || ctx.ip; + await next(); }, - handelError: function * (next) { + handelError: async (ctx, next) => { try { - yield * next; + await next(); } catch (err) { - this.status = err.status || 500; - this.body = err.message; + ctx.res.statusCode = err.status || 500; + ctx.body = err.message; C.logger.error(err.stack); - this.app.emit('error', err, this); + ctx.app.emit('error', err, ctx); } }, - loadRealPath: function *(next) { + loadRealPath: async (ctx, next) => { // router url format must be /api/(.*) - this.request.fPath = FilePath(this.params[0]); - C.logger.info(this.request.fPath); - yield * next; + const idx = ctx.url.indexOf('?'); + const path = ctx.url.slice(4, idx > 0 ? idx : undefined); + ctx.req.fPath = FilePath(sanitize(path || '/')); + C.logger.info(ctx.req.fPath); + await next(); }, - checkPathExists: function *(next) { + checkPathExists: async (ctx, next) => { // Must after loadRealPath - if (!(yield fs.exists(this.request.fPath))) { - this.status = 404; - this.body = 'Path Not Exists!'; - } - else { - yield * next; + if (!await fs.exists(ctx.req.fPath)) { + ctx.status = 404; + ctx.body = 'Path Not Exists!'; + } else { + await next(); } }, - checkPathNotExists: function *(next) { + checkPathNotExists: async (ctx, next) => { // Must after loadRealPath - if (yield fs.exists(this.request.fPath)) { - this.status = 400; - this.body = 'Path Has Exists!'; - } - else { - yield * next; + if (await fs.exists(ctx.req.fPath)) { + ctx.status = 400; + ctx.body = 'Path Has Exists!'; + } else { + await next(); } } - }; diff --git a/lib/views/files.html b/lib/views/files.html index 3d2529d..8f11496 100644 --- a/lib/views/files.html +++ b/lib/views/files.html @@ -6,10 +6,10 @@ File Manager - - - - + + + + @@ -48,7 +48,7 @@

Upload File - Just for small file now