infoserver
v0.0.7
Published
static file server that spews a bunch of metadata with files
Maintainers
Readme
InfoServer
File manager server & middleware
Usage
npm install --save infoserverThen, in your js:
import infoServer from 'infoserver';
infoServer(rootDir,{/**options**/}).then(api=>
api.commands.getMeta('/some/file/path')
.then(result=>{console.log(results)})
.catch(err=>{throw err})
})
or as an express middleware:
var app = require('express').app
import infoServer from 'infoserver';
infoServer(rootDir,{/**options**/}).then(api=>
app.use('/meta',api.middleware);
})
(go to examples/express and run npm install && node index.js to check an example of a bare bones express app)
infoServer(rootDir,options) → Promise
rootDiris your public directoryoptionsis an object with the following switches:adapter: provide an adapter database for collections (see below for spec).persist: either a boolean, or a file path. Used by the default memory adapter to write/load the database to/from a json file. Only use it to prototype, provide your own adapter for productionfilters: an array of filters used bygetMetaandgetMetaRecursive.
The promise resolves to an api generated by apido.
commands
All commands from fs-meta, plus:
Selections
infoserver exposes a selections api to group files together. They work like virtual directories in the sense that they can contain files, or other selections.
All selections commands use an adapter for persistence. Only a memory adapter is provided by default, to create your own, see the spec below.
All arrays can be specified, in a GET array, either by repeating the key (?files=x&files=y), or by separating them with a comma (?files=x,y).
api.selections.commands.add(groupName[,files[,groups]])
Adds a group called groupName and adds the files and groups specified. If groupName does not exist, it is created. If it already has files or groups, new files and groups are appended.
Also accessible through api.runPath('/selections/add',{groupName,files,groups})
or on the http url /selections/add/:groupName?files=x&files=y&groups=x&groups=y
api.selections.commands.get([type,items])
if nothing is specified, returns all groups that are children of the root group.
items is an array of files paths if type is "file", or an array of groups names if type is "group"
Also accessible through api.runPath('/selections/get',{type,items})
or on the http url /selections/get/:type?items=a&items=b
api.selections.commands.remove(groupName[,files[,groups]])
If files or groups are not specified, deletes the group designed by groupName, as well as any files that are only in that specific group. if files and groups are specified, removes the specified files and sub-groups from the group. Orphan files are removed, but orphan groups aren't.
Also accessible through api.runPath('/selections/remove',{groupName,files,groups})
or on the http url /selections/remove/:groupName?files=x&files=y&groups=x&groups=y
Objects
An example response from selections/get/a:
{
response:"success",
//api meta data...
"result":{
"groups": {
"groupA": {
"name": "groupA",
"files": ["path/to/a","path/to/b","path/to/c"],
"groups": []
}
},
"files": {
"path/to/a": {
"path": "path/to/a",
"parents": ["groupA"]
},
"path/to/b": {
"path": "path/to/b",
"parents": ["groupA"]
},
"path/to/c": {
"path": "path/to/c",
"parents": ["groupA","groupB"]
}
}
}
} Here's an easy function to re-nest everything in a cyclic structure if you wanted to:
function nest({result},cache){
cache = cache || {
groups:{}
, files:{}
}
function add(collection,value){
const key = collection == 'files' ? 'path' : 'name';
const obj = {[key]:value};
cache[collection][value] = obj;
return obj;
}
for(group of result.groups){
let {name} = group;
let files = group.files.map(path=>
result.files[path] || cache.files[path] || add('files',path)
)
let groups = group.groups.map(name=>
result.groups[name] || cache.groups[name]|| add('groups',name)
)
if(cache.groups[name]){
Object.assign(cache.groups[name],{files,groups});
}else{
cache.groups[name] = {name,files,groups}
}
}
for(file of result.paths){
let {path} = file;
file.parents = file.parents.map(name=>
result.groups[name] || cache.groups[name] || add('groups',name)
)
if(cache.files[path]){
cache.files[path].parents = parents;
}else{
cache.files[path] = {path,parents}
}
}
return cache;
}Adapter
An adapter has the following signature:
export default Promise.promisify(function customAdapter(fs,opts,cb){
const commands = {
add(groupName,files,groups,cb){}
, getFiles(files,cb){}
, getGroups(groups,cb){}
, getRoot(cb){}
, remove(groupName,files,groups,cb){}
}
cb(null,commands);
})addis guaranteed to receive a group name, butfilesandgroupsare optional. If a group doesn't exist, it is expected thataddcreates it on the fly. If it already exists, thengroupsandfilesshould be added to the existing groups and files.getFilesreceives an array of files pathsgetGroupsreceives an array of group namesremoveis guaranteed to receive a group name, butfilesandgroupsare optional. Ifgroupsandfilesare empty, then the function should remove the group specified bygroupName(as well as cascade all relations). If eitherfilesorgroupsare specified, then the group should remove all
Tests & Compile
First:
npm install --devCompile:
npm run compiletests:
Not implemented yetrun example:
cd examples/express
npm install
cd ../..
npm startThen open http://localhost:3000 in your browser
To get debug messages, you can set:
DEBUG=infoserver:* node yourApp.jsMIT License
Copyright © Jad Sarout
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.
