DocSpace-buildtools/common/ASC.WebDav/resource/customVirtualResource.js

582 lines
21 KiB
JavaScript
Raw Normal View History

2022-06-09 17:33:08 +00:00
/*
*
* (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;