js-databox
Version:
databox & metabox
483 lines (482 loc) • 18.7 kB
JavaScript
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;
}
}));
}
}