582 lines
21 KiB
JavaScript
582 lines
21 KiB
JavaScript
/*
|
||
*
|
||
* (c) Copyright Ascensio System Limited 2010-2021
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*/
|
||
|
||
|
||
const webdav = require('webdav-server').v2;
|
||
const {
|
||
getStructDirectory,
|
||
statusOperation,
|
||
getPresignedUri,
|
||
getReadStream,
|
||
readFile,
|
||
createFile,
|
||
createFolder,
|
||
deleteFile,
|
||
deleteFolder,
|
||
copyFile,
|
||
copyFolder,
|
||
moveFile,
|
||
moveFolder,
|
||
renameFile,
|
||
renameFolder,
|
||
createFileTxt,
|
||
createFileHtml,
|
||
createEditSession
|
||
} = require('../server/requestAPI.js');
|
||
const {
|
||
method,
|
||
maxChunkSize,
|
||
virtualPath
|
||
} = require('../server/config.js');
|
||
const streamWrite = require('../helper/writable.js');
|
||
const SimpleStruct = require('./simpleStruct.js');
|
||
const parse = require('../helper/propertyParser.js');
|
||
const {
|
||
maxExecutionTime
|
||
} = require('../server/config');
|
||
const { logMessage } = require('../helper/helper.js');
|
||
|
||
class CustomVirtualResources {
|
||
constructor() {
|
||
this.structСache = new SimpleStruct();
|
||
this.createdNow = [];
|
||
}
|
||
|
||
async getRootFolder(ctx, user) {
|
||
const structRoot = {
|
||
files: [],
|
||
folders: [],
|
||
current: {}
|
||
};
|
||
try {
|
||
logMessage('CustomVirtualResources.getRootFolder', user.username);
|
||
const structDir = await getStructDirectory(ctx, method.pathRootDirectory, user.token);
|
||
for (let i = 0; i < structDir.length; i++) {
|
||
structRoot.folders.push(structDir[i].current);
|
||
}
|
||
return structRoot;
|
||
} catch (error) {
|
||
return webdav.Errors.ResourceNotFound;
|
||
}
|
||
}
|
||
|
||
findFolder(struct, element) {
|
||
try {
|
||
return struct.folders.find(folder => folder.title == element);
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
findFile(struct, element) {
|
||
try {
|
||
return struct.files.find(file => file.title == element);
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async isOperationOver(ctx, token, response) {
|
||
let isFinished = false;
|
||
let status = {};
|
||
const startTime = new Date();
|
||
while (!isFinished) {
|
||
status = await statusOperation(ctx, response.id, token);
|
||
if (!status) {
|
||
isFinished = true;
|
||
status = response;
|
||
} else {
|
||
isFinished = status.finished;
|
||
}
|
||
if ((new Date() - startTime) >= maxExecutionTime) {
|
||
throw new Error('The maximum waiting time for the operation has been exceeded');
|
||
}
|
||
if (status.error != null) {
|
||
throw new Error(`Operation failed. Error: ${status.error}`);
|
||
}
|
||
if (!isFinished) {
|
||
await new Promise(resolve => setTimeout(resolve, 200));
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
fastExistCheck(path, ctx) {
|
||
//logMessage('CustomVirtualResources.fastExistCheck', ctx.user.username, path);
|
||
const substituteVP = virtualPath ? virtualPath : '';
|
||
if (path == '/' || path == '/' + substituteVP) {
|
||
return true;
|
||
}
|
||
const user = ctx.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
let struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
if (struct) {
|
||
if (this.findFile(struct, element) || this.findFolder(struct, element)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
async create(ctx, path) {
|
||
logMessage('CustomVirtualResources.create', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
let {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
let parentId = this.structСache.getStruct(parentFolder, user.uid).current.id;
|
||
|
||
|
||
if (ctx.type.isDirectory) {
|
||
let createdObj = await createFolder(ctx, parentId, element, user.token);
|
||
this.structСache.setFolderObject(parentFolder, user.uid, createdObj);
|
||
await this.readDir(ctx, path);
|
||
}
|
||
if (ctx.type.isFile) {
|
||
let createdObj = null;
|
||
switch (parse.parseFileExt(element)) {
|
||
case 'docx':
|
||
case 'pptx':
|
||
case 'xlsx':
|
||
createdObj = await createFile(ctx, parentId, element, user.token, false);
|
||
this.createdNow.push(createdObj.id);
|
||
this.structСache.setFileObject(parentFolder, user.uid, createdObj);
|
||
break;
|
||
case 'txt':
|
||
createdObj = await createFileTxt(ctx, parentId, element, user.token);
|
||
this.createdNow.push(createdObj.id);
|
||
this.structСache.setFileObject(parentFolder, user.uid, createdObj);
|
||
break;
|
||
case 'html':
|
||
createdObj = await createFileHtml(ctx, parentId, element, user.token);
|
||
this.createdNow.push(createdObj.id);
|
||
this.structСache.setFileObject(parentFolder, user.uid, createdObj);
|
||
break;
|
||
default:
|
||
//TODO: think about .tmp when create .xlsx in Windows Exploder
|
||
createdObj = await createFile(ctx, parentId, element, user.token, true);
|
||
this.createdNow.push(createdObj.id);
|
||
this.structСache.setFileObject(parentFolder, user.uid, createdObj);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
async delete(ctx, path) {
|
||
logMessage('CustomVirtualResources.delete', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
try {
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
const deleteResponse = await deleteFolder(ctx, folder.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, deleteResponse);
|
||
this.structСache.dropFolderObject(parentFolder, user.uid, folder);
|
||
this.structСache.dropPath(path, user.uid);
|
||
}
|
||
const file = this.findFile(struct, element);
|
||
if (file) {
|
||
const deleteResponse = await deleteFile(ctx, file.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, deleteResponse);
|
||
this.structСache.dropFileObject(parentFolder, user.uid, file);
|
||
}
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
|
||
|
||
addPrivilege(ctx, sPath, title, parentId, access, rootFolderType) {
|
||
let path = "";
|
||
let privilageList = "";
|
||
|
||
if (parentId == -1) {
|
||
path = sPath + title;
|
||
privilageList = ['all'];
|
||
} else if (parentId == 0) {
|
||
if (rootFolderType == 1 || rootFolderType == 5) {
|
||
path = sPath + '/' + title;
|
||
privilageList = ['all'];
|
||
} else {
|
||
path = sPath + '/' + title;
|
||
privilageList = ['canRead'];
|
||
}
|
||
} else switch (access) {
|
||
case 0:
|
||
path = sPath + '/' + title;
|
||
privilageList = ['all'];
|
||
break;
|
||
case 1:
|
||
path = sPath + '/' + title;
|
||
privilageList = ['canRead', 'canWriteContent', 'canWriteProperties', 'canWriteLocks'];
|
||
break;
|
||
default:
|
||
path = sPath + '/' + title;
|
||
privilageList = ['canRead'];
|
||
break;
|
||
}
|
||
|
||
ctx.context.server.privilegeManager.setRights(ctx.context.user, path, privilageList);
|
||
}
|
||
|
||
addStructPrivilege(ctx, path, struct) {
|
||
struct.folders.forEach(el => {
|
||
this.addPrivilege(ctx, path, el.title, el.parentId, el.access, el.rootFolderType);
|
||
});
|
||
|
||
struct.files.forEach(el => {
|
||
this.addPrivilege(ctx, path, el.title, el.parentId, el.access);
|
||
});
|
||
}
|
||
|
||
async readDir(ctx, path) {
|
||
logMessage('CustomVirtualResources.readDir', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
const substituteVP = virtualPath ? virtualPath : '';
|
||
|
||
if (virtualPath && path == '/') {
|
||
let virtualRootFolder = this.structСache.getStruct(path, user.uid);
|
||
if (virtualRootFolder) {
|
||
return virtualRootFolder;
|
||
}
|
||
virtualRootFolder = {
|
||
files: [],
|
||
current: {
|
||
id: -1
|
||
},
|
||
folders: [{
|
||
id: 0,
|
||
title: virtualPath,
|
||
parentId: -1
|
||
}]
|
||
};
|
||
this.structСache.setStruct(path, user.uid, virtualRootFolder);
|
||
this.addStructPrivilege(ctx, path, virtualRootFolder);
|
||
return this.structСache.getStruct(path, user.uid);
|
||
}
|
||
|
||
if (path == '/' + substituteVP) {
|
||
try {
|
||
let rootFolder = this.structСache.getStruct(path, user.uid);
|
||
if (rootFolder) {
|
||
return rootFolder;
|
||
}
|
||
rootFolder = await this.getRootFolder(ctx, user);
|
||
this.structСache.setStruct(path, user.uid, rootFolder);
|
||
this.addStructPrivilege(ctx, path, rootFolder);
|
||
return this.structСache.getStruct(path, user.uid);
|
||
} catch (error) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
}
|
||
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
|
||
let struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
if (!struct) {
|
||
struct = await this.readDir(ctx, parentFolder);
|
||
if (!struct || struct instanceof Error) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
} else {
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
try {
|
||
const structDirectory = await getStructDirectory(ctx, folder.id, user.token);
|
||
this.structСache.setStruct(path, user.uid, structDirectory);
|
||
this.addStructPrivilege(ctx, path, structDirectory);
|
||
return this.structСache.getStruct(path, user.uid);
|
||
} catch (error) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
if (this.structСache.structIsNotExpire(path, user.uid)) {
|
||
return this.structСache.getStruct(path, user.uid);
|
||
}
|
||
try {
|
||
const structDirectory = await getStructDirectory(ctx, folder.id, user.token);
|
||
this.structСache.setStruct(path, user.uid, structDirectory);
|
||
this.addStructPrivilege(ctx, path, structDirectory);
|
||
return this.structСache.getStruct(path, user.uid);
|
||
} catch (error) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
}
|
||
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
|
||
async getReadStream(ctx, path) {
|
||
logMessage('CustomVirtualResources.getReadStream', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
const { element, parentFolder } = parse.parsePath(path);
|
||
const file = this.findFile(this.structСache.getStruct(parentFolder, user.uid), element);
|
||
if (file) {
|
||
const uri = await getPresignedUri(ctx, file.id, user.token);
|
||
const readStream = await getReadStream(ctx, uri, user.token);
|
||
return readStream;
|
||
} else {
|
||
throw new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
}
|
||
|
||
readFile(ctx, path, callback) {
|
||
logMessage('CustomVirtualResources.readFile', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
const { element, parentFolder } = parse.parsePath(path);
|
||
const file = this.findFile(this.structСache.getStruct(parentFolder, user.uid), element);
|
||
if (file) {
|
||
readFile(ctx, file.id, user.token, callback);
|
||
} else {
|
||
callback(new Error(webdav.Errors.ResourceNotFound));
|
||
}
|
||
}
|
||
|
||
async writeFile(path, ctx) {
|
||
logMessage('CustomVirtualResources.writeFile', ctx.context.user.username, path);
|
||
const user = ctx.context.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
|
||
if (ctx.estimatedSize > 0) {
|
||
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
const file = this.findFile(struct, element);
|
||
const content = [];
|
||
const positionId = this.createdNow.indexOf(file.id);
|
||
if (positionId != -1) {
|
||
var createdNow = true;
|
||
this.createdNow = this.createdNow.splice(positionId + 1, 1);
|
||
|
||
var location = await createEditSession(ctx, file.id, ctx.estimatedSize, user.token);
|
||
}
|
||
let arrayBufLength = [];
|
||
if (ctx.estimatedSize > maxChunkSize) {
|
||
arrayBufLength = new Array(maxChunkSize);
|
||
}
|
||
const stream = new streamWrite(content, arrayBufLength, ctx, location, user, file, createdNow);
|
||
return stream;
|
||
} else {
|
||
const content = [];
|
||
const stream = new streamWrite(content, undefined, undefined, undefined, undefined);
|
||
return stream;
|
||
}
|
||
}
|
||
|
||
async copy(ctx, pathFrom, pathTo) {
|
||
logMessage('CustomVirtualResources.copy', ctx.context.user.username, pathFrom, pathTo);
|
||
const user = ctx.context.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(pathFrom);
|
||
pathTo = parse.parsePathTo(pathTo);
|
||
const structTo = this.structСache.getStruct(pathTo, user.uid);
|
||
if (!structTo) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
const folderId = structTo.current.id;
|
||
const structFrom = this.structСache.getStruct(parentFolder, user.uid);
|
||
const folder = this.findFolder(structFrom, element);
|
||
if (folder) {
|
||
try {
|
||
const copyResponse = await copyFolder(ctx, folderId, folder.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, copyResponse);
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
const file = this.findFile(structFrom, element);
|
||
if (file) {
|
||
try {
|
||
const copyResponse = await copyFile(ctx, folderId, file.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, copyResponse);
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
async rename(ctx, path, newName) {
|
||
logMessage('CustomVirtualResources.rename', ctx.context.user.username, path, newName);
|
||
const user = ctx.context.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
try {
|
||
await renameFolder(ctx, folder.id, newName, user.token);
|
||
this.structСache.renameFolderObject(element, newName, parentFolder, user.uid);
|
||
folder.realTitle = newName;
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
const file = this.findFile(struct, element);
|
||
if (file) {
|
||
try {
|
||
await renameFile(ctx, file.id, newName, user.token);
|
||
this.structСache.renameFileObject(element, newName, parentFolder, user.uid);
|
||
file.realTitle = newName;
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
async move(ctx, pathFrom, pathTo) {
|
||
logMessage('CustomVirtualResources.move', ctx.context.user.username, pathFrom, pathTo);
|
||
pathTo = parse.parsePathTo(pathTo);
|
||
const {
|
||
element: elementFrom,
|
||
parentFolder: parentFolderFrom
|
||
} = parse.parsePath(pathFrom);
|
||
const {
|
||
element: elementTo,
|
||
parentFolder: parentFolderTo
|
||
} = parse.parsePath(pathTo);
|
||
const user = ctx.context.user;
|
||
let isRename = false;
|
||
if (parentFolderFrom == parentFolderTo) {
|
||
isRename = this.structСache.checkRename(elementFrom, elementTo, parentFolderFrom, parentFolderTo, user);
|
||
}
|
||
if (isRename) {
|
||
try {
|
||
const rename = this.rename(ctx, pathFrom, elementTo);
|
||
return rename;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
if (!this.structСache.getStruct(parentFolderTo, user.uid)) {
|
||
return new Error(webdav.Errors.ResourceNotFound);
|
||
}
|
||
const folderId = this.structСache.getStruct(parentFolderTo, user.uid).current.id;
|
||
const structFrom = this.structСache.getStruct(parentFolderFrom, user.uid);
|
||
const folder = this.findFolder(structFrom, elementFrom);
|
||
if (folder) {
|
||
try {
|
||
const moveResponse = await moveFolder(ctx, folderId, folder.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, moveResponse);
|
||
this.structСache.dropFolderObject(parentFolderFrom, user.uid, folder);
|
||
this.structСache.dropPath(pathFrom, user.uid);
|
||
const folderWithNewLocation = status.folders.find(newLocation => newLocation.id == folder.id);
|
||
this.structСache.setFolderObject(parentFolderTo, user.uid, folderWithNewLocation);
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
const file = this.findFile(structFrom, elementFrom);
|
||
if (file) {
|
||
try {
|
||
const moveResponse = await moveFile(ctx, folderId, file.id, user.token);
|
||
const status = await this.isOperationOver(ctx, user.token, moveResponse);
|
||
this.structСache.dropFileObject(parentFolderFrom, user.uid, file);
|
||
const fileWithNewLocation = status.files.find(newLocation => newLocation.id == file.id);
|
||
this.structСache.setFileObject(parentFolderTo, user.uid, fileWithNewLocation);
|
||
return true;
|
||
} catch (error) {
|
||
return new Error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
getType(path, ctx) {
|
||
const user = ctx.context.user;
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
const substituteVP = virtualPath ? virtualPath : '';
|
||
if (parentFolder == '/' || parentFolder == '/' + substituteVP) {
|
||
return webdav.ResourceType.Directory;
|
||
}
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
return webdav.ResourceType.Directory;
|
||
}
|
||
const file = this.findFile(struct, element);
|
||
if (file) {
|
||
return webdav.ResourceType.File;
|
||
}
|
||
}
|
||
|
||
getSize(path, ctx) {
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
const user = ctx.context.user;
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
return null;
|
||
}
|
||
const file = this.findFile(struct, element);
|
||
if (file) {
|
||
return file.pureContentLength;
|
||
}
|
||
}
|
||
|
||
getlastModifiedDate(path, ctx) {
|
||
const substituteVP = virtualPath ? virtualPath : '';
|
||
if (path == '/' || path == '/' + substituteVP) {
|
||
return new Date(0, 0, 0, 0, 0, 0);
|
||
}
|
||
const {
|
||
element,
|
||
parentFolder
|
||
} = parse.parsePath(path);
|
||
const user = ctx.context.user;
|
||
const struct = this.structСache.getStruct(parentFolder, user.uid);
|
||
const folder = this.findFolder(struct, element);
|
||
if (folder) {
|
||
return parse.parseDate(folder.updated);
|
||
}
|
||
const file = this.findFile(struct, element);
|
||
if (file) {
|
||
return parse.parseDate(file.updated);
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = CustomVirtualResources; |