diff --git a/examples/gatewayPhysical/README.md b/examples/gatewayPhysical/README.md new file mode 100644 index 00000000..c91bd4fa --- /dev/null +++ b/examples/gatewayPhysical/README.md @@ -0,0 +1,13 @@ +# Custom Physical gateway + +This is an example to show how to use a gateway to manage the content of a physical folder. + +The folder `ts` and the folder `js` are the same thing. The `js` folder display the example in JavaScript while the `ts` display the example in TypeScript. + +## Usage + +### Execute + +```bash +node index.js +``` diff --git a/examples/gatewayPhysical/data.json b/examples/gatewayPhysical/data.json new file mode 100644 index 00000000..0964c573 Binary files /dev/null and b/examples/gatewayPhysical/data.json differ diff --git a/examples/gatewayPhysical/index.js b/examples/gatewayPhysical/index.js new file mode 100644 index 00000000..f82af395 --- /dev/null +++ b/examples/gatewayPhysical/index.js @@ -0,0 +1,39 @@ +const webdav = require('../../lib/index.js'), + phyFsManager = require('./js/PhysicalGFSManager.js'), + gateway = require('./js/PhysicalGateway.js'); + +const server = new webdav.WebDAVServer({ + port: 1900, + autoSave: { + treeFilePath: './data.json', + tempTreeFilePath: './data.tmp.json' + }, + autoLoad: { + treeFilePath: './data.json', + fsManagers: [ + new webdav.RootFSManager(), + new phyFsManager.PhysicalGFSManager(), + new webdav.VirtualFSManager() + ] + } +}); + +server.autoLoad((e) => { + if(e) + { + server.addResourceTree(new gateway.PhysicalGateway('./testData', 'phyGateway'), (e) => { + if(e) throw e; + + run(); + }); + } + else + run(); +}) + +function run() +{ + server.start((s) => { + console.log('Server started on port ' + s.address().port + '.'); + }); +} \ No newline at end of file diff --git a/examples/gatewayPhysical/js/PhysicalGFSManager.js b/examples/gatewayPhysical/js/PhysicalGFSManager.js new file mode 100644 index 00000000..989a9d9d --- /dev/null +++ b/examples/gatewayPhysical/js/PhysicalGFSManager.js @@ -0,0 +1,39 @@ +"use strict"; +const webdav = require('../../../lib/index.js'), + FTPClient = require('ftp'), + PhysicalGateway = require('./PhysicalGateway.js').PhysicalGateway; + +module.exports.PhysicalGFSManager = function(config) +{ + const fsManager = new webdav.PhysicalFSManager(); + fsManager.constructor = module.exports.PhysicalGFSManager; + + fsManager.uid = "PhysicalGFSManager_1.0.0"; + + fsManager.serialize = function(resource, obj) + { + if(resource.constructor !== PhysicalGateway) + return null; + + return { + realPath: resource.realPath, + dateCreation: resource.dateCreation, + dateLastModified: resource.dateLastModified, + properties: resource.properties, + customName: resource.customName + }; + } + + fsManager.unserialize = function(data, obj) + { + const rs = new PhysicalGateway(data.realPath, data.customName, null, this); + + rs.dateCreation = data.dateCreation; + rs.dateLastModified = data.dateLastModified; + rs.properties = data.properties; + + return rs; + } + + return fsManager; +} diff --git a/examples/gatewayPhysical/js/PhysicalGateway.js b/examples/gatewayPhysical/js/PhysicalGateway.js new file mode 100644 index 00000000..adde59e5 --- /dev/null +++ b/examples/gatewayPhysical/js/PhysicalGateway.js @@ -0,0 +1,183 @@ +"use strict"; +const webdav = require('../../../lib/index.js'), + PhysicalGFSManager = require('./PhysicalGFSManager.js').PhysicalGFSManager, + path = require('path'), + fs = require('fs'); + +module.exports.PhysicalGateway = function(rootPath, customName, parent, fsManager) +{ + const gateway = new webdav.PhysicalFolder(rootPath, parent, fsManager ? fsManager : new PhysicalGFSManager()); + gateway.constructor = module.exports.PhysicalGateway; + + gateway.customName = customName; + gateway.rootPath = rootPath ? rootPath : '/'; + gateway.cache = { + '/': gateway + }; + + const super_webName = gateway.webName; + gateway.webName = function(callback) + { + if(this.customName) + callback(null, this.customName); + else + super_webName(callback); + } + + gateway.listChildren = function(parent, rpath, callback) + { + if(rpath.lastIndexOf('/') !== rpath.length - 1) + rpath += '/'; + + fs.readdir(parent.realPath, (e, list) => { + if(e) + { + callback(e); + return; + } + + new webdav.Workflow() + .each(list, (file, cb) => { + const resourcePath = rpath + file; + let resource = this.cache[resourcePath]; + const realPath = path.join(parent.realPath, file); + + if(resource) + { + cb(null, resource); + return; + } + + fs.stat(realPath, (e, stat) => { + if(e) + { + cb(e); + return; + } + + if(stat.isFile()) + resource = new webdav.PhysicalFile(realPath, parent, this.fsManager); + else + resource = new webdav.PhysicalFolder(realPath, parent, this.fsManager); + resource.deleteOnMoved = true; + this.cache[resourcePath] = resource; + cb(null, resource); + }) + }) + .error(callback) + .done((resources) => callback(null, resources)); + }) + } + + gateway.find = function(path, callback, forceRefresh = false) + { + const resource = this.cache[path.toString()]; + if(forceRefresh || !resource) + { + const parentPath = path.getParent(); + this.find(parentPath, (e, parent) => { + if(e) + { + callback(e); + return; + } + + parent.getChildren((e, actualChildren) => { + if(e) + { + callback(e); + return; + } + + this.listChildren(parent, parentPath.toString(), (e, children) => { + if(e) + { + callback(e); + return; + } + + actualChildren + .filter((c) => c.constructor !== webdav.PhysicalResource && c.constructor !== webdav.PhysicalFile && c.constructor !== webdav.PhysicalFolder) + .forEach((c) => children.push(c)); + + parent.children.children = children; + + new webdav.Workflow() + .each(children, (child, cb) => { + child.webName((e, name) => { + cb(e, !e && name === path.fileName() ? child : null); + }) + }) + .error(callback) + .done((matchingChildren) => { + for(const child of matchingChildren) + if(child) + { + callback(null, child); + return; + } + + callback(webdav.Errors.ResourceNotFound); + }) + }) + }) + }) + } + else + callback(null, resource); + } + + gateway.gateway = function(arg, path, callback) + { + const updateChildren = (r, cb) => + { + this.listChildren(r, path.toString(), (e, children) => { + if(!e) + { + r.children.children + .filter((c) => c.constructor !== webdav.PhysicalResource && c.constructor !== webdav.PhysicalFile && c.constructor !== webdav.PhysicalFolder) + .forEach((c) => children.push(c)); + + r.children.children = children; + } + cb(e); + }) + } + + if(path.isRoot()) + { + updateChildren(this, (e) => { + callback(e, this); + }) + return; + } + + this.find(path, (e, r) => { + if(e) + { + callback(e); + return; + } + + r.type((e, type) => { + if(e) + { + callback(e); + return; + } + + if(type.isFile) + { + callback(e, r); + return; + } + + updateChildren(r, (e) => { + callback(e, r); + }) + }) + }); + } + + return gateway; +} diff --git a/examples/gatewayPhysical/package.json b/examples/gatewayPhysical/package.json new file mode 100644 index 00000000..97051ca1 --- /dev/null +++ b/examples/gatewayPhysical/package.json @@ -0,0 +1,11 @@ +{ + "name": "customphysicalgateway", + "version": "1.0.0", + "description": "Example to show how to use a gateway to manage the content of a physical folder.", + "main": "index.js", + "author": "Adrien Castex ", + "license": "Unlicense", + "dependencies": { + "webdav-server": "^1.9.0" + } +} diff --git a/examples/gatewayPhysical/testData/a.txt b/examples/gatewayPhysical/testData/a.txt new file mode 100644 index 00000000..b5f515de --- /dev/null +++ b/examples/gatewayPhysical/testData/a.txt @@ -0,0 +1 @@ +This is some data 2. \ No newline at end of file diff --git a/examples/gatewayPhysical/testData/fold/j.txt b/examples/gatewayPhysical/testData/fold/j.txt new file mode 100644 index 00000000..57e6d403 --- /dev/null +++ b/examples/gatewayPhysical/testData/fold/j.txt @@ -0,0 +1 @@ +This is some data 3. \ No newline at end of file diff --git a/examples/gatewayPhysical/testData/x.txt b/examples/gatewayPhysical/testData/x.txt new file mode 100644 index 00000000..358ed19b --- /dev/null +++ b/examples/gatewayPhysical/testData/x.txt @@ -0,0 +1 @@ +This is some data. \ No newline at end of file diff --git a/examples/gatewayPhysical/ts/PhysicalGFSManager.ts b/examples/gatewayPhysical/ts/PhysicalGFSManager.ts new file mode 100644 index 00000000..d6980105 --- /dev/null +++ b/examples/gatewayPhysical/ts/PhysicalGFSManager.ts @@ -0,0 +1,31 @@ +import { SerializedObject, FSManager, Errors, IResource, ResourceType, PhysicalFSManager, PhysicalFile, PhysicalFolder, PhysicalResource } from '../../../lib/index.js' +import { PhysicalGateway } from './PhysicalGateway' + +export class PhysicalGFSManager extends PhysicalFSManager +{ + uid : string = "PhysicalGFSManager_1.0.0"; + + serialize(resource : any, obj : SerializedObject) : object + { + if(resource.constructor !== PhysicalGateway) + return null; + + return { + realPath: resource.realPath, + dateCreation: resource.dateCreation, + dateLastModified: resource.dateLastModified, + properties: resource.properties, + customName: resource.customName + }; + } + unserialize(data : any, obj : SerializedObject) : IResource + { + const rs = new PhysicalGateway(data.realPath, data.customName, null, this); + + rs.dateCreation = data.dateCreation; + rs.dateLastModified = data.dateLastModified; + rs.properties = data.properties; + + return rs; + } +} diff --git a/examples/gatewayPhysical/ts/PhysicalGateway.ts b/examples/gatewayPhysical/ts/PhysicalGateway.ts new file mode 100644 index 00000000..ee5ec7a0 --- /dev/null +++ b/examples/gatewayPhysical/ts/PhysicalGateway.ts @@ -0,0 +1,183 @@ +import { PhysicalGFSManager } from './PhysicalGFSManager' +import * as webdav from '../../../lib/index.js' +import * as path from 'path' +import * as fs from 'fs' + +export class PhysicalGateway extends webdav.PhysicalFolder +{ + cache : { + [path : string] : webdav.PhysicalResource + } + + constructor(rootPath : string, protected customName ?: string, parent ?: webdav.IResource, fsManager ?: webdav.FSManager) + { + super(rootPath, parent, fsManager ? fsManager : new PhysicalGFSManager()); + + this.cache = { + '/': this + }; + } + + webName(callback : webdav.ReturnCallback) + { + if(this.customName) + callback(null, this.customName); + else + super.webName(callback); + } + + protected listChildren(parent : webdav.PhysicalResource, rpath : string, callback : (error : Error, children ?: webdav.IResource[]) => void) + { + if(rpath.lastIndexOf('/') !== rpath.length - 1) + rpath += '/'; + + fs.readdir(parent.realPath, (e, list) => { + if(e) + { + callback(e); + return; + } + + new webdav.Workflow() + .each(list, (file, cb) => { + const resourcePath = rpath + file; + let resource = this.cache[resourcePath]; + const realPath = path.join(parent.realPath, file); + + if(resource) + { + cb(null, resource); + return; + } + + fs.stat(realPath, (e, stat) => { + if(e) + { + cb(e); + return; + } + + if(stat.isFile()) + resource = new webdav.PhysicalFile(realPath, parent, this.fsManager); + else + resource = new webdav.PhysicalFolder(realPath, parent, this.fsManager); + (resource as webdav.PhysicalResource).deleteOnMoved = true; + this.cache[resourcePath] = resource; + cb(null, resource); + }) + }) + .error(callback) + .done((resources) => callback(null, resources)); + }) + } + + protected find(path : webdav.FSPath, callback : (error : Error, resource ?: webdav.PhysicalResource) => void, forceRefresh : boolean = false) + { + const resource = this.cache[path.toString()]; + if(forceRefresh || !resource) + { + const parentPath = path.getParent(); + this.find(parentPath, (e, parent) => { + if(e) + { + callback(e); + return; + } + + parent.getChildren((e, actualChildren) => { + if(e) + { + callback(e); + return; + } + + this.listChildren(parent, parentPath.toString(), (e, children) => { + if(e) + { + callback(e); + return; + } + + actualChildren + .filter((c) => c.constructor !== webdav.PhysicalResource && c.constructor !== webdav.PhysicalFile && c.constructor !== webdav.PhysicalFolder) + .forEach((c) => children.push(c)); + + (parent as webdav.PhysicalFolder).children.children = children; + + new webdav.Workflow() + .each(children, (child, cb) => { + child.webName((e, name) => { + cb(e, !e && name === path.fileName() ? child : null); + }) + }) + .error(callback) + .done((matchingChildren) => { + for(const child of matchingChildren) + if(child) + { + callback(null, child); + return; + } + + callback(webdav.Errors.ResourceNotFound); + }) + }) + }) + }) + } + else + callback(null, resource); + } + + gateway(arg : webdav.MethodCallArgs, path : webdav.FSPath, callback : (error : Error, resource ?: webdav.IResource) => void) + { + const updateChildren = (r, cb) => + { + this.listChildren(r, path.toString(), (e, children) => { + if(!e) + { + (r as webdav.PhysicalFolder).children.children + .filter((c) => c.constructor !== webdav.PhysicalResource && c.constructor !== webdav.PhysicalFile && c.constructor !== webdav.PhysicalFolder) + .forEach((c) => (children as webdav.IResource[]).push(c)); + + (r as webdav.PhysicalFolder).children.children = children; + } + cb(e); + }) + } + + if(path.isRoot()) + { + updateChildren(this, (e) => { + callback(e, this); + }) + return; + } + + this.find(path, (e, r) => { + if(e) + { + callback(e); + return; + } + + r.type((e, type) => { + if(e) + { + callback(e); + return; + } + + if(type.isFile) + { + callback(e, r); + return; + } + + updateChildren(r, (e) => { + callback(e, r); + }) + }) + }); + } +}