Full Code of efeiefei/node-file-manager for AI

master b28dd01f2279 cached
13 files
35.7 KB
9.8k tokens
2 symbols
1 requests
Download .txt
Repository: efeiefei/node-file-manager
Branch: master
Commit: b28dd01f2279
Files: 13
Total size: 35.7 KB

Directory structure:
gitextract_ghzjq0v0/

├── .gitignore
├── LICENSE
├── README.md
├── bin/
│   └── node-file-manager.sh
├── lib/
│   ├── fileManager.js
│   ├── fileMap.js
│   ├── index.js
│   ├── public/
│   │   └── js/
│   │       ├── angular-file.js
│   │       └── app.js
│   ├── routes.js
│   ├── tools.js
│   └── views/
│       └── files.html
└── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules/
.idea

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2016 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Screen Shot
![screenshot](https://raw.githubusercontent.com/efeiefei/node-file-manager/master/example/screenshot.png)

# Usage

```sh
  npm install -g node-file-manager
  node-file-manager -p 8080 -d /path/to/
```

Or

```sh
  git clone https://github.com/efeiefei/node-file-manager.git
  cd node-file-manager
  npm i
  cd lib
  node --harmony index.js -p 8080 -d /path/to
```

We can run node-file-manager in terminal directly. We can specify prot add data root dir by `-p` and `-d`, default with 5000 and scripts directory.

Then, we can view localhost:8080/ in our browr.


================================================
FILE: bin/node-file-manager.sh
================================================
#!/usr/bin/env sh

SHELL_PATH=`dirname $0`
cd $SHELL_PATH/../lib/node_modules/node-file-manager/lib

node --harmony index.js $*


================================================
FILE: lib/fileManager.js
================================================
var fs = require('co-fs');
var co = require('co');
var fse = require('co-fs-extra');
var path = require('path');
var JSZip = require('jszip');

var FileManager = {};

FileManager.getStats = function *(p) {
  var stats = yield fs.stat(p);
  return {
    folder: stats.isDirectory(),
    size: stats.size,
    mtime: stats.mtime.getTime()
  }
};

FileManager.list = function *(dirPath) {
  var files = yield fs.readdir(dirPath);
  var stats = [];
  for (var i=0; i<files.length; ++i) {
    var fPath = path.join(dirPath, files[i]);
    var stat = yield FileManager.getStats(fPath);
    stat.name = files[i];
    stats.push(stat);
  }
  return stats;
};

FileManager.remove = function *(p) {
  yield fse.remove(p);
};

FileManager.mkdirs = function *(dirPath) {
  yield fse.mkdirs(dirPath);
};

FileManager.move = function *(srcs, dest) {
  for (var i=0; i<srcs.length; ++i) {
    var basename = path.basename(srcs[i]);
    yield fse.move(srcs[i], path.join(dest, basename));
  }
};

FileManager.rename = function *(src, dest) {
  yield fse.move(src, dest);
};

FileManager.archive = function *(src, archive, dirPath, embedDirs) {
  var zip = new JSZip();
  var baseName = path.basename(archive, '.zip');

  function* addFile(file) {
    var data = yield fs.readFile(file);
    var name;
    if (embedDirs) {
      name = file;
      if (name.indexOf(dirPath) === 0) {
        name = name.substring(dirPath.length);
      }
    } else {
      name = path.basename(file);
    }
    zip.file(name, data);
    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));
    }
  }

  function* process(fp) {
    var stat = yield fs.stat(fp);
    if (stat.isDirectory()) {
      yield * addDir(fp);
    } else {
      yield addFile(fp);
    }
  }

  // Add each src.  For directories, do the entire recursive dir.
  for (var file of src) {
    yield * process(file);
  }

  // Generate the zip and store the final.
  var data = yield zip.generateAsync({type:'nodebuffer',compression:'DEFLATE'});
  yield fs.writeFile(archive, data, 'binary');
};

module.exports = FileManager;


================================================
FILE: lib/fileMap.js
================================================
var path = require('path');

var 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!');
    e.status = 400;
    throw e;
  }
  else {
    return path.join(DATA_ROOT, relPath);
  }
};


================================================
FILE: lib/index.js
================================================
#!/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');

// Config
var argv = require('optimist')
  .usage([
    'USAGE: $0 [-p <port>] [-d <directory>]']
  )
  .option('port', {
    alias: 'p',
    'default': 5000,
    description: 'Server Port'
  })
  .option('directory', {
    alias: 'd',
    description: 'Root Files Directory'
  })
  .option('version', {
    alias: 'v',
    description: 'Server Version'
  })
  .option('help', {
    alias: 'h',
    description: "Display This Help Message"
  })
  .argv;

if (argv.help) {
  require('optimist').showHelp(console.log);
  process.exit(0);
}

if (argv.version) {
  console.log('FileManager', require('./package.json').version);
  process.exit(0);
}

global.C = {
  data: {
    root: argv.directory || path.dirname('.')
  },
  logger: require('tracer').console({level: 'info'}),
  morganFormat: ':date[iso] :remote-addr :method :url :status :res[content-length] :response-time ms'
};

// Start Server
var Tools = require('./tools');

var startServer = function (app, port) {
  app.listen(port);
  C.logger.info('listening on *.' + port);
};

var app = koa();
app.proxy = true;
app.use(Tools.handelError);
app.use(Tools.realIp);
app.use(morgan.middleware(C.morganFormat));

var IndexRouter = require('./routes');
app.use(mount('/', IndexRouter));
app.use(koaStatic(path.join(__dirname,'./public/')));

startServer(app, +argv.port);



================================================
FILE: lib/public/js/angular-file.js
================================================
/**
 * ur.file: Native HTML5-based file input bindings for AngularJS
 *
 * @version 0.9a
 * @copyright (c) 2013 Union of RAD, LLC http://union-of-rad.com/
 * @license: BSD
 */


/**
 * The ur.file module implements native support for file uploads in AngularJS.
 */
angular.module('ur.file', []).config(['$provide', function($provide) {

  /**
   * XHR initialization, copied from Angular core, because it's buried inside $HttpProvider.
   */
  var XHR = window.XMLHttpRequest || function() {
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
    throw new Error("This browser does not support XMLHttpRequest.");
  };

  /**
   * Initializes XHR object with parameters from $httpBackend.
   */
  function prepXHR(method, url, headers, callback, withCredentials, type, manager) {
    var xhr = new XHR();
    var status;

    xhr.open(method, url, true);

    if (type) {
      xhr.type = type;
      headers['Content-Type'] = type;
    }

    angular.forEach(headers, function(value, key) {
      (value) ? xhr.setRequestHeader(key, value) : null;
    });

    manager.register(xhr);

    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        manager.unregister(xhr);
        var response = xhr.response || xhr.responseText;
        callback(status = status || xhr.status, response, xhr.getAllResponseHeaders());
      }
    };

    if (withCredentials) {
      xhr.withCredentials = true;
    }
    return xhr;
  }

  /**
   * Hook into $httpBackend to intercept requests containing files.
   */
  $provide.decorator('$httpBackend', ['$delegate', '$window', 'uploadManager', function($delegate, $window, uploadManager) {
    return function(method, url, post, callback, headers, timeout, wc) {
      var containsFile = false, result = null, manager = uploadManager;

      if (post && angular.isObject(post)) {
        containsFile = hasFile(post);
      }

      if (angular.isObject(post)) {
        if (post && post.name && !headers['X-File-Name']) {
          headers['X-File-Name'] = encodeURI(post.name);
        }

        angular.forEach({
          size: 'X-File-Size',
          lastModifiedDate: 'X-File-Last-Modified'
        }, function(header, key) {
          if (post && post[key]) {
            if (!headers[header]) headers[header] = post[key];
          }
        });
      }

      if (post && post instanceof Blob) {
        return prepXHR(method, url, headers, callback, wc, post.type, manager).send(post);
      }
      $delegate(method, url, post, callback, headers, timeout, wc);
    };
  }]);

  /**
   * Checks an object hash to see if it contains a File object, or, if legacy is true, checks to
   * see if an object hash contains an <input type="file" /> element.
   */
  var hasFile = function(data) {
    for (var n in data) {
      if (data[n] instanceof Blob) {
        return true;
      }
      if ((angular.isObject(data[n]) || angular.isArray(data[n])) && hasFile(data[n])) {
        return true;
      }
    }
    return false;
  };

  /**
   * Prevents $http from executing its default transformation behavior if the data to be
   * transformed contains file data.
   */
  $provide.decorator('$http', ['$delegate', function($delegate) {
    var transformer = $delegate.defaults.transformRequest[0];

    $delegate.defaults.transformRequest = [function(data) {
      return data instanceof Blob ? data : transformer(data);
    }];
    return $delegate;
  }]);

}]).service('fileHandler', ['$q', '$rootScope', function($q, $rootScope) {

  return {

    /**
     * Loads a file as a data URL and returns a promise representing the file's value.
     */
    load: function(file) {
      var deferred = $q.defer();

      var reader = angular.extend(new FileReader(), {
        onload: function(e) {
          deferred.resolve(e.target.result);
          if (!$rootScope.$$phase) $rootScope.$apply();
        },
        onerror: function(e) {
          deferred.reject(e);
          if (!$rootScope.$$phase) $rootScope.$apply();
        },
        onabort: function(e) {
          deferred.reject(e);
          if (!$rootScope.$$phase) $rootScope.$apply();
        }
        // onprogress: Gee, it'd be great to get some progress support from $q...
      });
      reader.readAsDataURL(file);

      return angular.extend(deferred.promise, {
        abort: function() { reader.abort(); }
      });
    },

    /**
     * Returns the metadata from a File object, including the name, size and last modified date.
     */
    meta: function(file) {
      return {
        name: file.name,
        size: file.size,
        lastModifiedDate: file.lastModifiedDate
      };
    },

    /**
     * Converts a File object or data URL to a Blob.
     */
    toBlob: function(data) {
      var extras = {};

      if (data instanceof File) {
        extras = this.meta(data);
        data = data.toDataURL();
      }
      var parts = data.split(","), headers = parts[0].split(":"), body;

      if (parts.length !== 2 || headers.length !== 2 || headers[0] !== "data") {
        throw new Error("Invalid data URI.");
      }
      headers = headers[1].split(";");
      body = (headers[1] === "base64") ? atob(parts[1]) : decodeURI(parts[1]);
      var length = body.length, buffer = new ArrayBuffer(length), bytes = new Uint8Array(buffer);

      for (var i = 0; i < length; i++) {
        bytes[i] = body.charCodeAt(i);
      }
      return angular.extend(new Blob([bytes], { type: headers[0] }), extras);
    }
  };

}]).service('uploadManager', ['$rootScope', function($rootScope) {

  angular.extend(this, {
    id : null,
    uploads: {},
    capture: function(id) {
      this.id = id;
      this.uploads[id] = {
        loaded: 0,
        total: 0,
        percent: 0,
        object: null
      };
    },
    register: function(xhr) {
      if (this.id === null) {
        return false;
      }
      xhr._idXhr = this.id;
      this.id = null;
      this.uploads[xhr._idXhr]['object'] = xhr;
      var self = this;

      xhr.upload.onprogress = function(e) {
        if (e.lengthComputable) {
          self.uploads[xhr._idXhr]['loaded'] = e.loaded;
          self.uploads[xhr._idXhr]['total'] = e.total;
          self.uploads[xhr._idXhr]['percent'] = Math.round(e.loaded / e.total * 100);
          $rootScope.$apply();
        }
      };
      return true;
    },
    unregister: function(xhr) {
      delete this.uploads[xhr._idXhr];
    },
    get: function(id) {
      if (this.uploads[id]) {
        return this.uploads[id];
      }
      return false;
    },
    abort: function(id) {
      if (this.uploads[id]) {
        return this.uploads[id]['object'].abort();
      }
      return false;
    }
  });

}]).directive('type', ['$parse', function urModelFileFactory($parse) {

  /**
   * Binding for file input elements
   */
  return {
    scope: false,
    priority: 1,
    require: "?ngModel",
    link: function urFilePostLink(scope, element, attrs, ngModel) {

      if (attrs.type.toLowerCase() !== 'file' || !ngModel) {
        return;
      }

      element.bind('change', function(e) {
        if (!e.target.files || !e.target.files.length || !e.target.files[0]) {
          return true;
        }
        var index, fileData = attrs.multiple ? e.target.files : e.target.files[0];
        ngModel.$render = function() {};

        scope.$apply(function(scope) {
          index = scope.$index;
          $parse(attrs.ngModel).assign(scope, fileData);
        });
        scope.$index = index;

        // @todo Make sure this can be replaced by ngChange.
        // For that to work, this event handler must have a higher priority than the one
        // defined by ngChange
        attrs.change ? scope.$eval(attrs.change) : null;
      });
    }
  };

}]).directive('dropTarget', ['$parse', 'fileHandler', function urDropTargetFactory($parse, fileHandler) {

  return {
    scope: false,
    restrict: "EAC",
    require: "?ngModel",
    link: function urDropTargetLink(scope, element, attrs, ngModel) {
      var multiple  = attrs.multiple,
          dropExpr  = attrs.drop ? $parse(attrs.drop) : null,
          modelExpr = attrs.ngModel ? $parse(attrs.ngModel) : null;

      if (ngModel) ngModel.$render = function() {};

      function stop(e) {
        e.stopPropagation();
        e.preventDefault();
      }

      var toIgnore = [], isOver = false;

      element.bind("dragenter", function dragEnter(e) {
        stop(e);
        if (e.target === this && !isOver) {
          if (attrs.overClass) element.addClass(attrs.overClass);
          isOver = true;
          return;
        }
        toIgnore.push(e.target);
      });

      element.bind("dragleave", function dragExit(e) {
        stop(e);
        if (toIgnore.length === 0 && isOver) {
          if (attrs.overClass) element.removeClass(attrs.overClass);
          isOver = false;
          return;
        }
        toIgnore.pop();
      });

      element.bind("dragover", function(e) {
        stop(e);
      });

      element.bind("drop", function(e) {
        stop(e);
        if (attrs.overClass) element.removeClass(attrs.overClass);
        isOver = false;
        e = e.originalEvent || e;
        var files = e.dataTransfer.files;

        if (!files.length) return;
        files = multiple ? files : files[0];

        if (modelExpr) modelExpr.assign(scope, files);
        if (!dropExpr) return (scope.$$phase) ? null : scope.$apply();

        var local = { $event: e };
        local['$file' + (multiple ? 's' : '')] = files;
        var result = function() { dropExpr(scope, local); };
        (scope.$$phase) ? result() : scope.$apply(result);
      });
    }
  };

}]);


================================================
FILE: lib/public/js/app.js
================================================
var FMApp = angular.module('FMApp', ['ur.file']);

FMApp.controller('FileManagerCtr', ['$scope', '$http', '$location',
  function ($scope, $http, $location) {
    var FM = this;
    FM.curHashPath = '#/';          // hash in browser url
    FM.curFolderPath = '/';         // current relative folder path
    FM.curBreadCrumbPaths = [];     // items in breadcrumb list, for each level folder
    FM.curFiles = [];               // files in current folder

    FM.selecteAll = false;          // if select all files
    FM.selection = [];              // selected files
    FM.renameName = '';             // new name for rename action
    FM.uploadFile = null;           // will upload file
    FM.newFolderName = '';
    FM.successData = '__init__';
    FM.errorData = '__init__';
    FM.updateArchiveName = function() {
      FM.archiveTarget = 'files_' + FM.curFolderPath.substring(1).replace(/\//g, '_') + new Date().toISOString().replace(/:/g, '.') + '.zip';
    };

    var hash2paths = function (relPath) {
      var paths = [];
      var names = relPath.split('/');
      var path = '#/';
      paths.push({name: 'Home', path: path});
      for (var i=0; i<names.length; ++i) {
        var name = names[i];
        if (name) {
          path = path + name + '/';
          paths.push({name: name, path: path});
        }
      }
      return paths;
    };

    var humanSize = function (size) {
      var hz;
      if (size < 1024) hz = size + ' B';
      else if (size < 1024*1024) hz = (size/1024).toFixed(2) + ' KB';
      else if (size < 1024*1024*1024) hz = (size/1024/1024).toFixed(2) + ' MB';
      else hz = (size/1024/1024/1024).toFixed(2) + ' GB';
      return hz;
    };

    var humanTime = function (timestamp) {
      var t = new Date(timestamp);
      return t.toLocaleDateString() + ' ' + t.toLocaleTimeString();
    };

    var setCurFiles = function (relPath) {
      $http.get('api' + relPath)
        .success(function (data) {
          var files = data;
          files.forEach(function (file) {
            file.relPath = relPath + encodeURIComponent(file.name);
            if (file.folder) file.relPath += '/';
            file.selected = false;
            file.humanSize = humanSize(file.size);
            file.humanTime = humanTime(file.mtime);
          });
          FM.curFiles = files;
          console.log('Current Files:');
          console.log(FM.curFiles);
        })
        .error(function (data, status) {
          alert('Error: ' + status + data);
        });
    };

    var handleHashChange = function (hash) {
      if (!hash) {
        return $location.path('/');
      }
      console.log('Hash change: ' + hash);
      var relPath = hash.slice(1);
      FM.curHashPath = hash;
      FM.curFolderPath = relPath;
      FM.curBreadCrumbPaths = hash2paths(relPath);
      setCurFiles(relPath);
    };

    $scope.$watch(function () {
      return location.hash;
    }, function (val) {
      handleHashChange(val);
    });

    // listening on file checkbox
    $scope.$watch('FM.curFiles|filter:{selected:true}', function (nv) {
      FM.selection = nv.map(function (file) {
        return file;
      });
    }, true);

    $scope.$watch('FM.selectAll', function (nv) {
      FM.curFiles.forEach(function (file) {
        file.selected = nv;
      });
    });

    $scope.$watch('FM.successData', function () {
      if (FM.successData === '__init__') return;
      $('#successAlert').show();
      $('#successAlert').fadeIn(3000);
      $('#successAlert').fadeOut(3000);
    });

    $scope.$watch('FM.errorData', function () {
      if (FM.errorData === '__init__') return;
      $('#errorAlert').show();
    });

    var httpRequest = function (method, url, params, data, config) {
      var conf = {
        method: method,
        url: url,
        params: params,
        data: data,
        timeout: 10000
      };
      for (var k in config) {
        if (config.hasOwnProperty(k)) {
          conf[k] = config[k];
        }
      }
      console.log('request url', url);
      $http(conf)
        .success(function (data) {
          FM.successData = data;
          handleHashChange(FM.curHashPath);
        })
        .error(function (data, status) {
          FM.errorData = ' ' + status + ': ' + data;
        });
    };

    var downloadFile = function (file) {
      window.open('api' + file.relPath);
    };

    FM.clickFile = function (file) {
      if (file.folder) {
        // open folder by setting url hash
        $location.path(decodeURIComponent(file.relPath));
      }
      else {
        // download file
        downloadFile(file);
      }
    };

    FM.download = function () {

      // Technique for downloading multiple files adapted from here:
      //  [1] http://stackoverflow.com/questions/2339440/download-multiple-files-with-a-single-action

      var link = document.createElement('a');
      link.style.display = 'none';
      document.body.appendChild(link);

      for (var i in FM.selection) {
        link.setAttribute('href', 'api' + FM.selection[i].relPath);
        link.setAttribute('download', FM.selection[i].name);
        link.click();
      }

      document.body.removeChild(link);
    };

    FM.delete = function () {
      for (var i in FM.selection) {
        var relPath = FM.selection[i].relPath;
        var url = 'api' + relPath;
        httpRequest('DELETE', url, null, null);
      }
    };

    FM.move = function (target) {
      var url = 'api' + encodeURI(target);
      var src = FM.selection.map(function (file) {
        return file.relPath;
      });
      httpRequest('PUT', url, {type: 'MOVE'}, {src: src});
    };

    FM.archive = function (archive) {
      if (!archive.match(/\.zip$/)) {
        archive += '.zip';
      }
      var url = 'api' + FM.curFolderPath + encodeURI(archive);
      var src = FM.selection.map(function (file) {
        return file.relPath;
      });
      httpRequest('POST', url, {type: 'CREATE_ARCHIVE'}, {src: src, embedDirs: FM.archiveEmbedDirs});
    };

    FM.rename = function (newName) {
      var url = 'api' + FM.selection[0].relPath;
      var target = FM.curFolderPath + encodeURI(newName);
      console.log('rename target', target);
      httpRequest('PUT', url, {type: 'RENAME'}, {target: target});
    };

    FM.createFolder = function (folderName) {
      var url = 'api' + FM.curFolderPath + encodeURI(folderName);
      httpRequest('POST', url, {type: 'CREATE_FOLDER'}, null);
    };

    FM.upload = function () {
      console.log('Upload File:', FM.uploadFile);
      var formData = new FormData();
      formData.append('upload', FM.uploadFile);
      var url = 'api' + FM.curFolderPath + encodeURI(FM.uploadFile.name);
      httpRequest('POST', url, {type: 'UPLOAD_FILE'}, formData, {
        transformRequest: angular.identity,
        headers: {'Content-Type': undefined}
      });
    };

    FM.btnDisabled = function (btnName) {
      switch (btnName) {
        case 'download':
          if (FM.selection.length === 0) return true;
          else {
            for (var i in FM.selection) {
              if (FM.selection[i].folder) return true;
            }
            return false;
          }
        case 'delete':
        case 'move':
        case 'archive':
          return FM.selection.length === 0;
        case 'rename':
          return FM.selection.length !== 1;
        case 'upload_file':
        case 'create_folder':
          return false;
        default:
          return true;
      }
    }
  }
]);


================================================
FILE: lib/routes.js
================================================
var fs = require('co-fs');
var path = require('path');
var views = require('co-views');
var origFs = require('fs');
var koaRouter = require('koa-router');
var bodyParser = require('koa-bodyparser');
var formParser = require('co-busboy');

var Tools = require('./tools');
var FilePath = require('./fileMap').filePath;
var FileManager = require('./fileManager');

var router = new koaRouter();
var render = views(path.join(__dirname, './views'), {map: {html: 'ejs'}});

router.get('/', function *() {
  this.redirect('files');
});

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);
  if (stats.isDirectory()) {
    this.body = yield * FileManager.list(p);
  }
  else {
    //this.body = yield fs.createReadStream(p);
    this.body = origFs.createReadStream(p);
  }
});

router.del('/api/(.*)', Tools.loadRealPath, Tools.checkPathExists, function *() {
  var p = this.request.fPath;
  yield * FileManager.remove(p);
  this.body = 'Delete Succeed!';
});

router.put('/api/(.*)', Tools.loadRealPath, Tools.checkPathExists, bodyParser(), function* () {
  var type = this.query.type;
  var p = this.request.fPath;
  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);
    });
    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!';
  }
  else {
    this.status = 400;
    this.body = 'Arg Type Error!';
  }
});

router.post('/api/(.*)', Tools.loadRealPath, Tools.checkPathNotExists, bodyParser(), function *() {
  var type = this.query.type;
  var p = this.request.fPath;
  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!';
    }
    else {
      this.status = 400;
      this.body = 'Lack Upload File!';
    }
  }
  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!';
  }
});

module.exports = router.middleware();


================================================
FILE: lib/tools.js
================================================
var fs = require('co-fs');
var FilePath = require('./fileMap').filePath;

module.exports = {
  realIp: function * (next) {
      this.req.ip = this.headers['x-forwarded-for'] || this.ip;
      yield *next;
  },

  handelError: function * (next) {
    try {
      yield * next;
    } catch (err) {
      this.status = err.status || 500;
      this.body = err.message;
      C.logger.error(err.stack);
      this.app.emit('error', err, this);
    }
  },

  loadRealPath: function *(next) {
    // router url format must be /api/(.*)
    this.request.fPath = FilePath(this.params[0]);
    C.logger.info(this.request.fPath);
    yield * next;
  },

  checkPathExists: function *(next) {
    // Must after loadRealPath
    if (!(yield fs.exists(this.request.fPath))) {
      this.status = 404;
      this.body = 'Path Not Exists!';
    }
    else {
      yield * next;
    }
  },

  checkPathNotExists: function *(next) {
    // Must after loadRealPath
    if (yield fs.exists(this.request.fPath)) {
      this.status = 400;
      this.body = 'Path Has Exists!';
    }
    else {
      yield * next;
    }
  }

};


================================================
FILE: lib/views/files.html
================================================
<!DOCTYPE html>
<html lang="en" ng-app="FMApp">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>File Manager</title>

  <link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
  <script src="//cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
  <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  <script src="//cdn.bootcss.com/angular.js/1.4.5/angular.min.js"></script>
  <script src="js/angular-file.js"></script>
  <script src="js/app.js"></script>

  <style type="text/css">
    .sortable {
      cursor: pointer;
    }
  </style>
</head>

<body ng-controller="FileManagerCtr as FM">
  <div class="modal fade" id="createFolderModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4>New Folder</h4>
        </div>
        <div class="modal-body">
          <p>Complete Path {{FM.curFolderPath + FM.newFolderName + '/'}}</p>
          <label class="control-label">Folder Name</label><input class="form-control" autofocus ng-model="FM.newFolderName" />
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="!FM.newFolderName" ng-click="FM.createFolder(FM.newFolderName)">OK</button>
        </div>
      </div>
    </div>
  </div>

  <div class="modal fade" id="uploadFileModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4>Upload File - Just for small file now</h4>
        </div>
        <div class="modal-body">
          <p>Upload to {{FM.curFolderPath + FM.uploadFile.name}}</p>
          <div class="form-inline">
              <input type="file" class="form-control" autofocus ng-model="FM.uploadFile">
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="!FM.uploadFile" ng-click="FM.upload()">OK</button>
        </div>
      </div>
    </div>
  </div>

  <div class="modal fade" id="renameModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4>Rename</h4>
        </div>
        <div class="modal-body">
          <label class="control-label">New Name</label><input class="form-control" autofocus ng-model="FM.newName" placeholder={{FM.selection[0].name}} />
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="!FM.newName" ng-click="FM.rename(FM.newName)">OK</button>
        </div>
      </div>
    </div>
  </div>

  <div class="modal fade" id="moveModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4>Move</h4>
        </div>
        <div class="modal-body">
          <label class="control-label">Target</label><input class="form-control" autofocus ng-model="FM.moveTarget" placeholder="{{FM.curFolderPath}}" />
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="!FM.moveTarget" ng-click="FM.move(FM.moveTarget)">OK</button>
        </div>
      </div>
    </div>
  </div>

  <div class="modal fade" id="archiveModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h4>Create Archive</h4>
        </div>
        <div class="modal-body">
          <label class="control-label">Name</label><input class="form-control" autofocus ng-model="FM.archiveTarget" placeholder="Enter name of archive" />
          <div class="checkbox">
            <label><input type="checkbox" ng-init="FM.archiveEmbedDirs = true" ng-model="FM.archiveEmbedDirs"> Embed directories in archive</label>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="!FM.archiveTarget" ng-click="FM.archive(FM.archiveTarget)">Create</button>
        </div>
      </div>
    </div>
  </div>

  <nav class="navbar navbar-default navbar-static-top">
    <div class="container">
      <div class="navbar-header">
        <a class="navbar-brand" href="#">File Manager</a>
      </div>
    </div>
  </nav>

  <div class="container">
    <div class="btn-toolbar">
      <button type="button" class="btn btn-default" ng-disabled="FM.btnDisabled('download')" ng-click="FM.download()"><span class="glyphicon glyphicon-download"></span> Download</button>
      <button type="button" class="btn btn-default" ng-disabled="FM.btnDisabled('delete')" ng-click="FM.delete()"><span class="glyphicon glyphicon-remove"></span> Delete</button>
      <button type="button" class="btn btn-default" ng-disabled="FM.btnDisabled('move')" data-toggle="modal" data-target="#moveModal"><span class="glyphicon glyphicon-random"></span> Move</button>
      <button type="button" class="btn btn-default" ng-disabled="FM.btnDisabled('rename')" data-toggle="modal" data-target="#renameModal"><span class="glyphicon glyphicon-font"></span> Rename</button>
      <button type="button" class="btn btn-default" ng-disabled="FM.btnDisabled('archive')" data-toggle="modal" data-target="#archiveModal" ng-click="FM.updateArchiveName()"><span class="glyphicon glyphicon-download-alt"></span> Archive</button>
      <button type="button" class="btn btn-default pull-right" ng-disabled="FM.btnDisabled('create_folder')" data-toggle="modal" data-target="#createFolderModal"><span class="glyphicon glyphicon-plus-sign"></span> New Folder</button>
      <button type="button" class="btn btn-default pull-right" ng-disabled="FM.btnDisabled('upload_file')" data-toggle="modal" data-target="#uploadFileModal"><span class="glyphicon glyphicon-upload"></span> Upload</button>
    </div>

    <ol class="breadcrumb">
      <span class="glyphicon glyphicon-home"></span>
      <li ng-repeat="p in FM.curBreadCrumbPaths"><a href={{p.path}}>{{p.name}}</a></li>
    </ol>

    <table class="table table-hover table-striped">
      <thead>
        <tr class="sortable">
          <th><input type="checkbox" value="" ng-model="FM.selectAll"></th>
          <th>Type</th>
          <th>Name</th>
          <th>Size</th>
          <th>Time</th>
        </tr>
      </thead>
      <tbody>
      <tr class="sortable" ng-repeat="file in FM.curFiles | orderBy:'folder'">
        <td><input type="checkbox" value="{{file.name}}" ng-model="file.selected"></td>
        <td ng-click="FM.clickFile(file)"><span class="{{file.folder ? 'glyphicon glyphicon-folder-open' : 'glyphicon glyphicon-file'}}"></span></td>
        <td ng-click="FM.clickFile(file)"><a>{{file.name}}</a></td>
        <td>{{file.humanSize}}</td>
        <td>{{file.humanTime}}</td>
      </tr>
      </tbody>
    </table>

    <div id="successAlert" class="alert alert-success" role="alert" style="display: none">
      <span>{{FM.successData}}</span>
    </div>

    <div id="errorAlert" class="alert alert-danger" role="alert" style="display: none">
      <button type="button" class="close" data-dismiss="alert" alert-label="Close">
        <span aria-hidden="true">&times</span>
      </button>
      <strong>Error!</strong>
      {{FM.errorData}}
    </div>

  </div>

</body>
</html>


================================================
FILE: package.json
================================================
{
  "name": "node-file-manager",
  "version": "0.4.6",
  "description": "File manager web based on Koa and Angular.js",
  "main": "index.js",
  "scripts": {
    "start": "node --harmony ./lib/index.js"
  },
  "bin": {
    "node-file-manager": "../node-file-manager/bin/node-file-manager.sh"
  },
  "dependencies": {
    "co": "^4.6.0",
    "co-busboy": "1.3.1",
    "co-from-stream": "0.0.0",
    "co-fs": "^1.2.0",
    "co-fs-extra": "^1.1.0",
    "co-views": "^2.1.0",
    "ejs": "^2.3.4",
    "jszip": "^3.1.3",
    "koa": "^1.0.0",
    "koa-bodyparser": "^2.0.1",
    "koa-morgan": "^0.3.0",
    "koa-mount": "^1.3.0",
    "koa-router": "^5.1.2",
    "koa-static": "^1.4.9",
    "optimist": "^0.6.1",
    "tracer": "^0.8.0"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/efeiefei/node-file-manager.git"
  },
  "keywords": [
    "files",
    "manager",
    "koa",
    "angular",
    "web",
    "server"
  ],
  "author": {
    "name": "efei",
    "email": "efeigm@gmail.com"
  },
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/efeiefei/node-file-manager/issues"
  },
  "homepage": "https://github.com/efeiefei/node-file-manager#readme"
}
Download .txt
gitextract_ghzjq0v0/

├── .gitignore
├── LICENSE
├── README.md
├── bin/
│   └── node-file-manager.sh
├── lib/
│   ├── fileManager.js
│   ├── fileMap.js
│   ├── index.js
│   ├── public/
│   │   └── js/
│   │       ├── angular-file.js
│   │       └── app.js
│   ├── routes.js
│   ├── tools.js
│   └── views/
│       └── files.html
└── package.json
Download .txt
SYMBOL INDEX (2 symbols across 1 files)

FILE: lib/public/js/angular-file.js
  function prepXHR (line 28) | function prepXHR(method, url, headers, callback, withCredentials, type, ...
  function stop (line 289) | function stop(e) {
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (39K chars).
[
  {
    "path": ".gitignore",
    "chars": 19,
    "preview": "node_modules/\n.idea"
  },
  {
    "path": "LICENSE",
    "chars": 1057,
    "preview": "MIT License\n\nCopyright (c) 2016 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "README.md",
    "chars": 577,
    "preview": "# Screen Shot\n![screenshot](https://raw.githubusercontent.com/efeiefei/node-file-manager/master/example/screenshot.png)\n"
  },
  {
    "path": "bin/node-file-manager.sh",
    "chars": 128,
    "preview": "#!/usr/bin/env sh\n\nSHELL_PATH=`dirname $0`\ncd $SHELL_PATH/../lib/node_modules/node-file-manager/lib\n\nnode --harmony inde"
  },
  {
    "path": "lib/fileManager.js",
    "chars": 2245,
    "preview": "var fs = require('co-fs');\nvar co = require('co');\nvar fse = require('co-fs-extra');\nvar path = require('path');\nvar JSZ"
  },
  {
    "path": "lib/fileMap.js",
    "chars": 352,
    "preview": "var path = require('path');\n\nvar DATA_ROOT = C.data.root;\n\nexports.filePath = function (relPath, decodeURI) {\n  if (deco"
  },
  {
    "path": "lib/index.js",
    "chars": 1543,
    "preview": "#!/usr/bin/env node\n\nvar koa =require('koa');\nvar path = require('path');\nvar tracer = require('tracer');\nvar mount = re"
  },
  {
    "path": "lib/public/js/angular-file.js",
    "chars": 9811,
    "preview": "/**\n * ur.file: Native HTML5-based file input bindings for AngularJS\n *\n * @version 0.9a\n * @copyright (c) 2013 Union of"
  },
  {
    "path": "lib/public/js/app.js",
    "chars": 7516,
    "preview": "var FMApp = angular.module('FMApp', ['ur.file']);\n\nFMApp.controller('FileManagerCtr', ['$scope', '$http', '$location',\n "
  },
  {
    "path": "lib/routes.js",
    "chars": 3113,
    "preview": "var fs = require('co-fs');\nvar path = require('path');\nvar views = require('co-views');\nvar origFs = require('fs');\nvar "
  },
  {
    "path": "lib/tools.js",
    "chars": 1109,
    "preview": "var fs = require('co-fs');\nvar FilePath = require('./fileMap').filePath;\n\nmodule.exports = {\n  realIp: function * (next)"
  },
  {
    "path": "lib/views/files.html",
    "chars": 7923,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"FMApp\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta http-equiv=\"X-UA-Compatible\" con"
  },
  {
    "path": "package.json",
    "chars": 1186,
    "preview": "{\n  \"name\": \"node-file-manager\",\n  \"version\": \"0.4.6\",\n  \"description\": \"File manager web based on Koa and Angular.js\",\n"
  }
]

About this extraction

This page contains the full source code of the efeiefei/node-file-manager GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (35.7 KB), approximately 9.8k tokens, and a symbol index with 2 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!