infoserver
v0.0.7
Published
static file server that spews a bunch of metadata with files
Downloads
2
Maintainers
Readme
InfoServer
File manager server & middleware
Usage
npm install --save infoserver
Then, 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
rootDir
is your public directoryoptions
is 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 bygetMeta
andgetMetaRecursive
.
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);
})
add
is guaranteed to receive a group name, butfiles
andgroups
are optional. If a group doesn't exist, it is expected thatadd
creates it on the fly. If it already exists, thengroups
andfiles
should be added to the existing groups and files.getFiles
receives an array of files pathsgetGroups
receives an array of group namesremove
is guaranteed to receive a group name, butfiles
andgroups
are optional. Ifgroups
andfiles
are empty, then the function should remove the group specified bygroupName
(as well as cascade all relations). If eitherfiles
orgroups
are specified, then the group should remove all
Tests & Compile
First:
npm install --dev
Compile:
npm run compile
tests:
Not implemented yet
run example:
cd examples/express
npm install
cd ../..
npm start
Then open http://localhost:3000 in your browser
To get debug messages, you can set:
DEBUG=infoserver:* node yourApp.js
MIT 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.