UNPKG

js-databox

Version:

databox & metabox

483 lines (482 loc) 18.7 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { idlFactory } from "./did/databox"; import { Actor } from "@dfinity/agent"; import { nanoid } from "nanoid"; import random from "string-random"; import { AESEncryptApi, EncryptApi, RSAEncryptApi } from "../utils"; import { MetaBox } from "../metabox"; const chunkSize = 1992288; const ONE_BYTE_UPLOAD_USE_CYCLES = 2260; export class DataBox { constructor(canisterId, agent) { this.agent = agent; this.DataBoxActor = Actor.createActor(idlFactory, { agent, canisterId }); } boxState() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.canisterState(); } catch (e) { throw e; } }); } cycleBalance() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.cycleBalance(); } catch (e) { throw e; } }); } put_plain_files(files, is_private, key_arr, fileTypes) { return __awaiter(this, void 0, void 0, function* () { 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] : 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 = yield 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)); } } yield Promise.all(allPromise); return keyArr; } catch (e) { throw e; } }); } static FileRead(file) { return __awaiter(this, void 0, void 0, function* () { 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 = function (e) { return __awaiter(this, void 0, void 0, function* () { 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 encryptFileData(data, publicKey) { return __awaiter(this, void 0, void 0, function* () { try { const AESKEY = yield EncryptApi.aesKeyGen(); const AESIv = random(128); const encData = AESEncryptApi.AESEncData(data, AESKEY, AESIv); const encryptedAesKey = yield RSAEncryptApi.encryptMessage(publicKey, `${AESKEY}${AESIv}`); return { encData, encryptedAesKey }; } catch (e) { throw e; } }); } put_encrypt_files(files, is_private, publicKey, key_arr) { return __awaiter(this, void 0, void 0, function* () { 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] : nanoid(); keyArr.push(key); const total_size = file.size; const allData = yield 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 } = yield DataBox.encryptFileData(data, publicKey); const NewBlob = new Blob([encData]); const encryptedData = yield 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)); } } yield Promise.all(allPromise); return keyArr; } catch (e) { throw e; } }); } static getFile(decodeArr, length) { return __awaiter(this, void 0, void 0, function* () { 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; }); } getData(file_info, isEncrypt) { return __awaiter(this, void 0, void 0, function* () { 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 yield 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; } }); } get_plain_file(file_key) { return __awaiter(this, void 0, void 0, function* () { try { const dataArr = []; let fileSize = 0; const file_info = yield this.get_file_info(file_key); const fileType = file_info.ok.PlainFileExt.file_extension; const res = yield this.getData(file_info, false); if (res[0] && res[0].ok) { res.forEach(e => { dataArr.push(e.ok); fileSize += e.ok.length; }); const metadata = yield 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; } }); } get_encrypt_file(file_key, privatekey) { var _a; return __awaiter(this, void 0, void 0, function* () { try { const dataArr = []; let fileSize = 0; const file_info = yield this.get_file_info(file_key); const fileType = (_a = file_info === null || file_info === void 0 ? void 0 : file_info.ok) === null || _a === void 0 ? void 0 : _a.EncryptFileExt.file_extension; const res = yield 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 = yield DataBox.getFile(dataArr, fileSize); const privateKey = yield RSAEncryptApi.importPrivateKey(privatekey); const preFileAesKey = yield RSAEncryptApi.decryptMessage(privateKey, file_info.ok.EncryptFileExt.aes_pub_key[0]); const AesKey = preFileAesKey.slice(0, 256); const AesIv = preFileAesKey.slice(256); const plainText = 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; } }); } delete_box_plain_file(file_key) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.deleteFileFromKey(file_key, { 'Plain': null }); } catch (e) { throw e; } }); } delete_box_encrypted_file(file_key) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.deleteFileFromKey(file_key, { 'EnCrypt': null }); } catch (e) { throw e; } }); } clear_box() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.clearall(); } catch (e) { throw e; } }); } get_file_info(file_key) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.getAssetextkey(file_key); } catch (e) { throw e; } }); } getVersion() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.getVersion(); } catch (e) { throw e; } }); } get_all_files_info() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.getAssetexts(); } catch (e) { throw e; } }); } transferOwner(to) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.transferOwner(to); } catch (e) { throw e; } }); } get_owner() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.getOwner(); } catch (e) { throw e; } }); } setPlainFilePubOrPri(changePlainFilePermissionArg) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.setPlainFilePubOrPri(changePlainFilePermissionArg.file_key, changePlainFilePermissionArg.is_private); } catch (e) { throw e; } }); } addPrivatePlainShare(shareFileArg) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.addPrivatePlainShare(shareFileArg.file_key, shareFileArg.to); } catch (e) { throw e; } }); } removePrivatePlainShare(shareFileArg) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.removePrivatePlainShare(shareFileArg.file_key, shareFileArg.to); } catch (e) { throw e; } }); } getShareFiles() { return __awaiter(this, void 0, void 0, function* () { try { return yield this.DataBoxActor.getShareFiles(); } catch (e) { throw e; } }); } is_need_upgrade() { return __awaiter(this, void 0, void 0, function* () { try { const MBapi = new MetaBox(this.agent); const version = Number(yield this.getVersion()); const new_version = Number(yield MBapi.getDataBoxVersion()); return version < new_version; } catch (e) { throw e; } }); } is_enough_to_upload(total_size) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { try { const res = yield 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} 数据个数 */ getFileNums(fileLocation) { return __awaiter(this, void 0, void 0, function* () { try { return yield 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((resolve, reject) => __awaiter(this, void 0, void 0, function* () { try { if (onePageFileNums > 5000) return reject("A page of data cannot exceed 5000"); const res = yield 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; } })); } }