UNPKG

js-databox

Version:

databox & metabox

428 lines (427 loc) 15.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataBox = void 0; const databox_1 = require("./did/databox"); const agent_1 = require("@dfinity/agent"); const nanoid_1 = require("nanoid"); const string_random_1 = __importDefault(require("string-random")); const utils_1 = require("../utils"); const metabox_1 = require("../metabox"); const chunkSize = 1992288; const ONE_BYTE_UPLOAD_USE_CYCLES = 2260; class DataBox { constructor(canisterId, agent) { this.agent = agent; this.DataBoxActor = agent_1.Actor.createActor(databox_1.idlFactory, { agent, canisterId }); } async boxState() { try { return await this.DataBoxActor.canisterState(); } catch (e) { throw e; } } async cycleBalance() { try { return await this.DataBoxActor.cycleBalance(); } catch (e) { throw e; } } async put_plain_files(files, is_private, key_arr, fileTypes) { if (key_arr && key_arr.length !== files.length) throw new Error("文件数量与key数量不匹配"); if (fileTypes && fileTypes.length !== files.length) throw new Error("文件数量与类型数量不匹配"); try { const Actor = this.DataBoxActor; const allPromise = []; const keyArr = []; for (let i = 0; i < files.length; i++) { const file = files[i]; const key = key_arr ? key_arr[i] : (0, nanoid_1.nanoid)(); keyArr.push(key); const file_extension = fileTypes ? fileTypes[i] : file.type; const total_size = file.size; const total_index = Math.ceil(total_size / chunkSize); const allData = await DataBox.FileRead(file); for (let i = 0; i < allData.length; i++) { const arg = { PlainFilePut: { IC: { file_extension, order: BigInt(i), chunk_number: BigInt(total_index), chunk: { data: allData[i] }, aes_pub_key: [], file_name: file.name, file_key: key, total_size: BigInt(file.size), is_private: is_private } } }; allPromise.push(Actor.put(arg)); } } await Promise.all(allPromise); return keyArr; } catch (e) { throw e; } } static async FileRead(file) { try { return new Promise((resolve, reject) => { let start = 0; let currentChunk = 0; const total_index = Math.ceil(file.size / chunkSize); const allData = []; let reader = new FileReader(); reader.onload = async function (e) { allData.push(new Uint8Array(e.target.result)); if (currentChunk === total_index) return resolve(allData); else loadChunk(); }; reader.onerror = (error) => { reject(error); }; const loadChunk = () => { const end = start + chunkSize; currentChunk++; reader.readAsArrayBuffer(file.slice(start, end)); start = end; }; loadChunk(); }); } catch (e) { throw e; } } static async encryptFileData(data, publicKey) { try { const AESKEY = await utils_1.EncryptApi.aesKeyGen(); const AESIv = (0, string_random_1.default)(128); const encData = utils_1.AESEncryptApi.AESEncData(data, AESKEY, AESIv); const encryptedAesKey = await utils_1.RSAEncryptApi.encryptMessage(publicKey, `${AESKEY}${AESIv}`); return { encData, encryptedAesKey }; } catch (e) { throw e; } } async put_encrypt_files(files, is_private, publicKey, key_arr) { try { if (key_arr && key_arr.length !== files.length) throw new Error("文件数量与key数量不匹配"); const Actor = this.DataBoxActor; const keyArr = []; const allPromise = []; for (let i = 0; i < files.length; i++) { const file = files[i]; const key = key_arr ? key_arr[i] : (0, nanoid_1.nanoid)(); keyArr.push(key); const total_size = file.size; const allData = await DataBox.FileRead(file); const data = new Uint8Array(total_size); for (let i = 0; i < allData.length; i++) { data.set(allData[i], i * chunkSize); } const { encData, encryptedAesKey } = await DataBox.encryptFileData(data, publicKey); const NewBlob = new Blob([encData]); const encryptedData = await DataBox.FileRead(NewBlob); for (let i = 0; i < encryptedData.length; i++) { const arg = { EncryptFilePut: { IC: { file_extension: file.type, order: BigInt(i), chunk_number: BigInt(Math.ceil(NewBlob.size / chunkSize)), chunk: { data: encryptedData[i] }, aes_pub_key: [encryptedAesKey], file_name: file.name, file_key: key, total_size: BigInt(NewBlob.size), is_private: is_private } } }; allPromise.push(Actor.put(arg)); } } await Promise.all(allPromise); return keyArr; } catch (e) { throw e; } } static async getFile(decodeArr, length) { const File = new Uint8Array(length); for (let i = 0; i < decodeArr.length; i++) { let slice = decodeArr[i]; let start = 0; for (let j = 0; j < i; j++) { start += decodeArr[j].length; } File.set(slice, start); } return File; } async getData(file_info, isEncrypt) { try { const queryPromiseArr = []; if (file_info.ok) { const AssetExt = file_info.ok[isEncrypt ? "EncryptFileExt" : "PlainFileExt"]; if (AssetExt) { const need_query_times = Number(AssetExt.need_query_times); for (let i = 0; i < need_query_times; i++) { queryPromiseArr.push(this.DataBoxActor[isEncrypt ? "getCipher" : "getPlain"]({ file_key: AssetExt.file_key, flag: BigInt(i) })); } return await Promise.all(queryPromiseArr); } else throw new Error(`this is not a ${isEncrypt ? "encrypt" : "plain"} file`); } else throw new Error(Object.keys(file_info.err)[0]); } catch (e) { throw e; } } async get_plain_file(file_key) { try { const dataArr = []; let fileSize = 0; const file_info = await this.get_file_info(file_key); const fileType = file_info.ok.PlainFileExt.file_extension; const res = await this.getData(file_info, false); if (res[0] && res[0].ok) { res.forEach(e => { dataArr.push(e.ok); fileSize += e.ok.length; }); const metadata = await DataBox.getFile(dataArr, fileSize); return new Blob([metadata.buffer], { type: fileType, }); } else throw new Error(Object.keys(res[0].err)[0]); } catch (e) { throw e; } } async get_encrypt_file(file_key, privatekey) { try { const dataArr = []; let fileSize = 0; const file_info = await this.get_file_info(file_key); const fileType = file_info?.ok?.EncryptFileExt.file_extension; const res = await this.getData(file_info, true); if (res[0] && res[0].ok) { res.forEach(e => { e.ok.forEach(value => { dataArr.push(value); fileSize += value.length; }); }); const metadata = await DataBox.getFile(dataArr, fileSize); const privateKey = await utils_1.RSAEncryptApi.importPrivateKey(privatekey); const preFileAesKey = await utils_1.RSAEncryptApi.decryptMessage(privateKey, file_info.ok.EncryptFileExt.aes_pub_key[0]); const AesKey = preFileAesKey.slice(0, 256); const AesIv = preFileAesKey.slice(256); const plainText = utils_1.AESEncryptApi.AESDecData(metadata, AesKey, AesIv); return new Blob([plainText.buffer], { type: fileType, }); } else throw new Error(Object.keys(res[0].err)[0]); } catch (e) { throw e; } } async delete_box_plain_file(file_key) { try { return await this.DataBoxActor.deleteFileFromKey(file_key, { 'Plain': null }); } catch (e) { throw e; } } async delete_box_encrypted_file(file_key) { try { return await this.DataBoxActor.deleteFileFromKey(file_key, { 'EnCrypt': null }); } catch (e) { throw e; } } async clear_box() { try { return await this.DataBoxActor.clearall(); } catch (e) { throw e; } } async get_file_info(file_key) { try { return await this.DataBoxActor.getAssetextkey(file_key); } catch (e) { throw e; } } async getVersion() { try { return await this.DataBoxActor.getVersion(); } catch (e) { throw e; } } async get_all_files_info() { try { return await this.DataBoxActor.getAssetexts(); } catch (e) { throw e; } } async transferOwner(to) { try { return await this.DataBoxActor.transferOwner(to); } catch (e) { throw e; } } async get_owner() { try { return await this.DataBoxActor.getOwner(); } catch (e) { throw e; } } async setPlainFilePubOrPri(changePlainFilePermissionArg) { try { return await this.DataBoxActor.setPlainFilePubOrPri(changePlainFilePermissionArg.file_key, changePlainFilePermissionArg.is_private); } catch (e) { throw e; } } async addPrivatePlainShare(shareFileArg) { try { return await this.DataBoxActor.addPrivatePlainShare(shareFileArg.file_key, shareFileArg.to); } catch (e) { throw e; } } async removePrivatePlainShare(shareFileArg) { try { return await this.DataBoxActor.removePrivatePlainShare(shareFileArg.file_key, shareFileArg.to); } catch (e) { throw e; } } async getShareFiles() { try { return await this.DataBoxActor.getShareFiles(); } catch (e) { throw e; } } async is_need_upgrade() { try { const MBapi = new metabox_1.MetaBox(this.agent); const version = Number(await this.getVersion()); const new_version = Number(await MBapi.getDataBoxVersion()); return version < new_version; } catch (e) { throw e; } } async is_enough_to_upload(total_size) { return new Promise(async (resolve, reject) => { try { const res = await this.cycleBalance(); if (Object.keys(res)[0] === "ok") { //@ts-ignore const balance = Number(res.ok); if (total_size * ONE_BYTE_UPLOAD_USE_CYCLES < balance) { return resolve(true); } else return reject(Number(total_size * ONE_BYTE_UPLOAD_USE_CYCLES - balance)); //@ts-ignore } else return reject(String(Object.keys(res.err)[0])); } catch (e) { return reject(e); } }); } /** * * @param {FileLocation} fileLocation 文件位置 * @return {Result_7} 数据个数 */ async getFileNums(fileLocation) { try { return await this.DataBoxActor.getFileNums(fileLocation); } catch (e) { throw e; } } /** * 分页get数据 * * @param {FileLocation} fileLocation 文件位置 * @param {number} onePageFileNums 每一页的数据大小 不能超过5000 * @param {number} pageIndex 取哪一页 * @example * getPageFiles({Plain:null},2,0) 取明文数据,每一页有两个数据,取第一页 */ getPageFiles(fileLocation, onePageFileNums, pageIndex) { return new Promise(async (resolve, reject) => { try { if (onePageFileNums > 5000) return reject("A page of data cannot exceed 5000"); const res = await this.DataBoxActor.getPageFiles(fileLocation, BigInt(onePageFileNums), BigInt(pageIndex)); if (Object.keys(res)[0] === "ok") return resolve(res.ok); else return reject(Object.keys(res.err)[0]); } catch (e) { throw e; } }); } } exports.DataBox = DataBox;