@sentclose/sentc-nodejs
Version:
End-to-end encryption sdk
185 lines (184 loc) • 7.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Downloader = exports.findAvailableFileName = void 0;
/**
* @author Jörn Heinemann <joernheinemann@gmx.de>
* @since 2022/08/27
*/
const Mutex_1 = require("./Mutex");
const Sentc_1 = require("../Sentc");
const sentc_node_js_1 = require("@sentclose/sentc_node_js");
const promises_1 = require("node:fs/promises");
const path = require("path");
async function findAvailableFileName(file_path) {
const { name, dir, ext } = path.parse(file_path);
let file_name = path.join(dir, name, ext);
for (let i = 1; i < 20; i++) {
try {
// eslint-disable-next-line no-await-in-loop
await (0, promises_1.access)(file_name, promises_1.constants.W_OK);
file_name = path.join(dir, `${name}(${i})`, ext);
}
catch (e) {
return file_name;
}
}
}
exports.findAvailableFileName = findAvailableFileName;
class Downloader {
static init() {
if (this.is_init) {
return;
}
this.is_init = true;
this.mutex = new Mutex_1.Mutex();
}
constructor(base_url, app_token, user, group_id, group_as_member) {
//the base url can be different when serving the files from a different storage
this.base_url = base_url;
this.app_token = app_token;
this.user = user;
this.group_id = group_id;
this.group_as_member = group_as_member;
Downloader.init();
}
/**
* Get the file info and the first page of the file part list
*
* @param file_id
*/
async downloadFileMetaInformation(file_id) {
const jwt = await this.user.getJwt();
const out = await (0, sentc_node_js_1.fileDownloadFileMeta)(this.base_url, this.app_token, jwt, file_id, this.group_id, this.group_as_member);
const part_list = [];
for (let i = 0; i < out.partList.length; i++) {
const part = out.partList[i];
part_list.push({
part_id: part.partId,
extern_storage: part.externStorage,
sequence: part.sequence
});
}
const file_meta = {
file_id: out.fileId,
belongs_to: out.belongsTo,
belongs_to_type: out.belongsToType,
encrypted_file_name: out.encryptedFileName,
encrypted_key: out.encryptedKey,
encrypted_key_alg: out.encryptedKeyAlg,
key_id: out.masterKeyId,
master_key_id: out.masterKeyId,
part_list
};
if (part_list.length >= 500) {
//download parts via pagination
let last_item = part_list[part_list.length - 1];
let next_fetch = true;
while (next_fetch) {
// eslint-disable-next-line no-await-in-loop
const fetched_parts = await this.downloadFilePartList(file_id, last_item);
part_list.push(...fetched_parts);
next_fetch = fetched_parts.length >= 500;
last_item = fetched_parts[fetched_parts.length - 1];
}
}
return {
belongs_to: file_meta.belongs_to,
belongs_to_type: file_meta.belongs_to_type,
file_id: file_meta.file_id,
master_key_id: file_meta.master_key_id,
encrypted_key: file_meta.encrypted_key,
encrypted_key_alg: file_meta.encrypted_key_alg,
part_list,
encrypted_file_name: file_meta.encrypted_file_name
};
}
/**
* Download the rest of the part list via pagination
*
* @param file_id
* @param last_item
*/
async downloadFilePartList(file_id, last_item = null) {
var _a;
const last_seq = (_a = (last_item === null || last_item === void 0 ? void 0 : last_item.sequence) + "") !== null && _a !== void 0 ? _a : "";
const out = await (0, sentc_node_js_1.fileDownloadPartList)(this.base_url, this.app_token, file_id, last_seq);
const part_list = [];
for (let i = 0; i < out.length; i++) {
const part = out[i];
part_list.push({
part_id: part.partId,
extern_storage: part.externStorage,
sequence: part.sequence
});
}
return part_list;
}
async downloadFileParts(fileHandle, part_list, content_key, updateProgressCb, verify_key) {
var _a, _b;
const unlock = await Downloader.mutex.lock();
Downloader.cancel_download = false;
const url_prefix = (_b = (_a = Sentc_1.Sentc.options) === null || _a === void 0 ? void 0 : _a.file_part_url) !== null && _b !== void 0 ? _b : undefined;
let next_file_key = content_key;
let offset = 0;
for (let i = 0; i < part_list.length; i++) {
const external = part_list[i].extern_storage === true;
const part_url_base = (external) ? url_prefix : undefined;
let part;
try {
if (i === 0) {
//first part
// eslint-disable-next-line no-await-in-loop
const res = await (0, sentc_node_js_1.fileDownloadAndDecryptFilePartStart)(this.base_url, part_url_base, this.app_token, part_list[i].part_id, content_key, verify_key);
next_file_key = res.nextFileKey;
part = res.file;
}
else {
// eslint-disable-next-line no-await-in-loop
const res = await (0, sentc_node_js_1.fileDownloadAndDecryptFilePart)(this.base_url, part_url_base, this.app_token, part_list[i].part_id, next_file_key, verify_key);
next_file_key = res.nextFileKey;
part = res.file;
}
}
catch (e) {
// eslint-disable-next-line no-await-in-loop
unlock();
throw e;
}
if (!part) {
// eslint-disable-next-line no-await-in-loop
unlock();
throw Error("Part not found");
}
// eslint-disable-next-line no-await-in-loop
await fileHandle.write(part, 0, part.length, offset);
offset += part.length;
if (updateProgressCb) {
updateProgressCb((i + 1) / part_list.length);
}
if (Downloader.cancel_download) {
Downloader.cancel_download = false;
// eslint-disable-next-line no-await-in-loop
unlock();
return;
}
}
unlock();
}
async downloadFilePartsWithPath(path, part_list, content_key, updateProgressCb, verify_key) {
const file_handle = await (0, promises_1.open)(path, "a");
try {
await this.downloadFileParts(file_handle, part_list, content_key, updateProgressCb, verify_key);
}
catch (e) {
await (0, promises_1.unlink)(path);
throw e;
}
finally {
await file_handle.close();
}
}
}
exports.Downloader = Downloader;
Downloader.is_init = false;
Downloader.cancel_download = false;