/*! meta-admin/modules/files */
/*jslint
browser, long
*/
/*global
Blob, confirm
*/
/**
* files component.
*
* @module meta-admin/modules/component/files
*/
import _ from "underscore";
import {saveAs} from "file-saver";
import buffers from "util-web/modules/buffers";
import crypto from "util-web/modules/crypto";
import dates from "util-web/modules/dates";
import ext from "util-web/modules/ext";
import i18next from "util-web/modules/i18next";
import ko from "knockout";
import Logger from "js-logger";
import model from "meta-core/modules/model";
import nav from "util-web/modules/nav";
import objects from "meta-client/modules/objects";
import protocol from "meta-core/modules/protocol";
import template from "./files.html";
import ui from "util-web/modules/ui";
const LOG = Logger.get("meta-admin/files");
const BACKUP_QUERY = `tags:${model.BACKUP_TAG}`;
const BASE_64_SPLIT = "base64,";
const BASE_64_SPLIT_SIZE = BASE_64_SPLIT.length;
export default Object.freeze({
template,
viewModel: {
createViewModel: function ({viewmodel}) {
const vm = {};
vm.FILE_CONTENT_TYPES = ext.FILE_CONTENT_TYPES;
vm.viewmodel = viewmodel;
vm.fileQuery = ko.observable();
vm.fileName = ko.observable();
vm.fileTags = ko.observable();
vm.fileChannel = ko.observable();
vm.fileExpiration = ko.observable();
vm.fileExpirationSub = dates.insertDateTimes({fromDateObservable: vm.fileExpiration});
vm.fileType = ko.observable(ext.FILE_CONTENT_TYPES.BUFFER);
vm.fileSearcher = objects({
searchReplyObjectsPath: "files",
toVm: (fileInfo) => vm.viewmodel.fileInfoVm(protocol.fileInfo(fileInfo))
});
vm.fileProgress = ko.observable();
vm.backupServiceOptions = ko.observableArray();
vm.backupService = ko.observable();
vm.isBackupInProgres = ko.observable(false);
vm.errorReply = ko.observable();
vm.copyKey = function (fileInfo) {
return navigator.clipboard.writeText(fileInfo.key());
};
vm.copyHash = function (fileInfo) {
return navigator.clipboard.writeText(fileInfo.hash());
};
vm.searchFilesInternal = function (limit, clearSearch) {
const query = vm.fileQuery();
LOG.debug(`searchFilesInternal, query=${query}`);
if (clearSearch) {
vm.fileSearcher.clear();
}
return vm.fileSearcher.processSearch(vm.viewmodel.client.fileSearch(protocol.fileSearchRequest({
cursor: vm.fileSearcher.searchCursor(),
limit,
query,
sort: model.lucene.FILE_FIELD_UPLOAD_TIMESTAMP,
sortReverse: true
}))).then(function () {
vm.errorReply(undefined);
}, function (exc) {
LOG.warn("searchFilesInternal failed", exc);
vm.errorReply(JSON.stringify(exc, undefined, 4));
});
};
vm.searchFiles = function (form) {
if (ui.validateForm(form) === false) {
return;
}
LOG.debug("searchFiles");
return vm.searchFilesInternal(100, true).then(function () {
ui.resetForm(form);
});
};
vm.loadMore = function () {
LOG.debug("loadMore");
return vm.searchFilesInternal(100, false);
};
vm.loadAll = function () {
LOG.debug("loadAll");
return vm.searchFilesInternal(0, false);
};
vm.uploadFile = function (ignore, event) {
let uploadBuffer;
let contentSize;
LOG.debug("uploadFile");
return ext.loadFileContent({event, type: vm.fileType()}).then(function (file) {
if (ext.FILE_CONTENT_TYPES.BUFFER === vm.fileType()) {
uploadBuffer = file.content;
} else {
uploadBuffer = buffers.stringToBuffer(file.content);
}
contentSize = uploadBuffer.byteLength;
LOG.debug(`uploadFile ready, contentSize=${contentSize}`);
return crypto.hashBuffer(uploadBuffer);
}).then(function (hash) {
const tags = vm.fileTags();
return vm.viewmodel.client.upload(protocol.uploadRequest({
channel: vm.fileChannel(),
expiration: vm.fileExpiration(),
hash,
name: vm.fileName(),
size: contentSize,
tags: (
_.isEmpty(tags) === false
? tags.split(",")
: undefined
)
}));
}).then(function (reply) {
if (reply.duplicate) {
vm.errorReply(i18next.t("file_duplicate"));
return Promise.resolve(undefined);
}
vm.errorReply(undefined);
vm.fileProgress(reply.progress);
return Promise.all([
reply.sendFile(uploadBuffer),
reply.donePromise
]);
}).then(function () {
vm.fileChannel(undefined);
vm.fileExpiration(undefined);
vm.fileName(undefined);
}, function (exc) {
vm.errorReply(JSON.stringify(exc, undefined, 4));
});
};
vm.downloadFile = function (fileInfo) {
const key = fileInfo.key();
let expectedHash;
let fileName;
let downloadBlob;
LOG.debug(`downloadFile, key=${key}`);
return vm.viewmodel.client.download(protocol.downloadRequest({key})).then(function (downloadReply) {
expectedHash = downloadReply.file.hash;
fileName = downloadReply.file.name;
vm.fileProgress(downloadReply.progress);
return downloadReply.donePromise;
}).then(function (blob) {
downloadBlob = blob;
return blob.arrayBuffer();
}).then(function (buffer) {
return Promise.all([crypto.hashBuffer(buffer), Promise.resolve(buffer)]);
}).then(function (result) {
const hash = result[0];
if (hash !== expectedHash) {
throw new Error(`hash invalid, key=${key}, expectedHash=${expectedHash}, hash=${hash}`);
}
vm.errorReply(undefined);
const buffer = result[1];
let fileBlob;
let dataUrl;
let dataUrlBase64;
switch (vm.fileType()) {
case ext.FILE_CONTENT_TYPES.BUFFER:
fileBlob = downloadBlob;
break;
case ext.FILE_CONTENT_TYPES.DATAURL:
dataUrl = buffers.bufferToString(buffer);
dataUrlBase64 = dataUrl.substring(dataUrl.indexOf(BASE_64_SPLIT) + BASE_64_SPLIT_SIZE);
fileBlob = new Blob([buffers.base64ToBuffer(dataUrlBase64)]);
break;
case ext.FILE_CONTENT_TYPES.TEXT:
fileBlob = new Blob([buffers.bufferToString(buffer)]);
break;
}
saveAs(fileBlob, key + (
_.isEmpty(fileName)
? ".file"
: "_" + fileName
));
}, function (exc) {
LOG.warn("downloadFile failed", exc);
vm.errorReply(JSON.stringify(exc, undefined, 4));
});
};
vm.deleteFile = function (fileInfo) {
const key = fileInfo.key();
LOG.debug(`deleteFile, key=${key}`);
if (confirm(i18next.t("file_delete_confirm")) === false) {
return;
}
return vm.viewmodel.client.fileDelete(protocol.fileDeleteRequest({key})).then(function () {
vm.errorReply(undefined);
vm.fileSearcher.objects.remove((fileVm) => fileVm.key() === key);
}, function (exc) {
vm.errorReply(JSON.stringify(exc, undefined, 4));
});
};
vm.searchBackups = function () {
const service = vm.backupService();
const query = BACKUP_QUERY + (
_.isEmpty(service)
? ""
: ` AND tags:"${service}"`
);
vm.fileQuery(query);
return vm.searchFiles();
};
vm.createBackup = function (form) {
if (ui.validateForm(form) === false) {
return;
}
const service = vm.backupService();
LOG.debug(`createBackup, service=${service}`);
vm.isBackupInProgres(true);
return vm.viewmodel.client.backup(protocol.backupRequest({service})).then(function () {
vm.errorReply(undefined);
ui.resetForm(form);
}, function (exc) {
vm.errorReply(JSON.stringify(exc, undefined, 4));
}).then(function () {
vm.isBackupInProgres(false);
});
};
vm.restoreBackup = function (fileInfo) {
const key = fileInfo.key();
LOG.debug(`restoreBackup, key=${key}`);
if (confirm(i18next.t("restore_backup_confirm")) === false) {
return;
}
vm.isBackupInProgres(true);
return vm.viewmodel.client.backupRestore(protocol.backupRestoreRequest({key})).then(function () {
vm.errorReply(undefined);
}, function (exc) {
vm.errorReply(JSON.stringify(exc, undefined, 4));
}).then(function () {
vm.isBackupInProgres(false);
});
};
vm.isExporting = ko.observable(false);
vm.doExport = function () {
const fileExport = {
files: vm.fileSearcher.objects().map(vm.viewmodel.vmToFileInfo)
};
LOG.debug("doExport");
vm.isExporting(true);
saveAs(
new Blob([JSON.stringify(fileExport, undefined, 4)], {type: "application/json;charset=utf-8"}),
`file_export-${dates.now().toMillis()}.json`
);
vm.isExporting(false);
};
vm.onClientNotification = function (clientNotification) {
const data = _.get(clientNotification, "data");
if (protocol.UPLOAD_DONE_NOTIFICATION_ACTION !== clientNotification.action || _.isObject(data) === false) {
return;
}
LOG.debug("onClientNotification", data);
vm.fileSearcher.objects.unshift(vm.viewmodel.fileInfoVm(data));
};
vm[protocol.CLIENT_NOTIFICATION] = vm.onClientNotification;
const eventHandle = vm.viewmodel.client.registerOnEvent(vm);
nav.register({
id: vm.viewmodel.COMPONENTS.FILES,
onUpdate: vm.searchFiles
});
vm.dispose = function () {
LOG.debug("dispose");
vm.viewmodel.client.unregisterOnEvent(eventHandle);
vm.fileExpirationSub.dispose();
};
ko.when(function () {
return vm.viewmodel.client.isAuth();
}).then(function () {
return vm.viewmodel.client.getServiceInfo(protocol.serviceInfoRequest());
}).then(function (reply) {
vm.backupServiceOptions(reply.info["plat-FileService"].backupDelegateServiceNames);
});
return Object.freeze(vm);
}
}
});