newpay-wallet-js
Version:
260 lines (232 loc) • 9.83 kB
JavaScript
import {Apis} from "bitsharesjs-ws";
import idb_helper from "./idb-helper.js";
import iDBRoot from "./idb-root.js";
const DB_VERSION = 2; // Initial value was 1
const DB_PREFIX = "graphene_v2";
const WALLET_BACKUP_STORES = [
"wallet", "private_keys", "linked_accounts"
];
var current_wallet_name = "default";
var upgrade = function(db, oldVersion) {
// DEBUG console.log('... upgrade oldVersion',oldVersion)
if (oldVersion === 0) {
db.createObjectStore("wallet", { keyPath: "public_name" })
idb_helper.autoIncrement_unique(db, "private_keys", "pubkey")
db.createObjectStore("linked_accounts", { keyPath: "name" })
}
if (oldVersion < 2) {
// Cache only, do not backup...
db.createObjectStore("cached_properties", { keyPath: "name" })
}
}
/**
Everything in this class is scopped by the database name. This separates
data per-wallet and per-chain.
*/
var getDatabaseName = function(
current_wallet = current_wallet_name,
chain_id = Apis.instance().chain_id) {
return [
DB_PREFIX,
chain_id ? chain_id.substring(0, 6) : "",
current_wallet
].join("_")
}
var openDatabase = function(database_name = this.getDatabaseName()) {
return new Promise((resolve, reject) => {
var openRequest = iDB.impl.open(database_name, DB_VERSION);
openRequest.onupgradeneeded = function (e) {
// DEBUG console.log('... openRequest.onupgradeneeded ' + database_name)
// Don't resolve here, indexedDb will call onsuccess or onerror next
upgrade(e.target.result, e.oldVersion)
};
openRequest.onsuccess = function (e) {
// DEBUG console.log('... openRequest.onsuccess ' + database_name, e.target.result)
var db = e.target.result //db的version是1
iDB.database_name = database_name
idb_helper.set_graphene_db(db)
resolve(db);
};
openRequest.onerror = function (e) {
// DEBUG console.log("... openRequest.onerror " + database_name,e.target.error, e)
reject(e.target.error);
};
})
}
var iDB = (function () {
var _instance;
var idb;
/** Be carefull not to call twice especially for a new database
needing an upgrade...
*/
function openIndexedDB(chain_id) {
return iDB.root.getProperty("current_wallet", "default").then(current_wallet => {
current_wallet_name = current_wallet;
var database_name = getDatabaseName(current_wallet, chain_id);
return openDatabase(database_name);
});
}
function init(chain_id) {
let promise = openIndexedDB(chain_id);
promise.then(db => {
idb = db;
});
return {
init_promise: promise,
db: () => idb
};
}
return {
WALLET_BACKUP_STORES,
getDatabaseName: getDatabaseName,
getCurrentWalletName: ()=> current_wallet_name,
deleteDatabase: function(are_you_sure = false) {
if( ! are_you_sure) return "Are you sure?"
var req = iDB.impl.deleteDatabase(this.database_name)
return req.result
},
set_impl: function(impl) {
this.impl = impl
this.root = new iDBRoot(this.impl)
},
set_chain_id: function(chain_id) {
this.chain_id = chain_id
var chain_substring = chain_id ? chain_id.substring(0, 6) : ""
this.root.setDbSuffix("_" + chain_substring)
},
getchainid: function(){
console.log(Apis.instance().chain_id);
},
init_instance: function (
indexedDBimpl,
chain_id = Apis.instance().chain_id //chain_id是通过配置里面,访问地址列表第一个地址获取(访问方式:ReconnectingWebSocket),参数:{"method":"call","params":[2,"get_chain_id",[]],"id":398},但测试返回失败
) {
if (!_instance) {
if(indexedDBimpl) {
this.set_impl( indexedDBimpl )
if("__useShim" in indexedDBimpl) {
this.impl.__useShim() //always use shim
}
}
this.set_chain_id(chain_id)
_instance = init(chain_id)
}
return _instance;
},
instance: function () {
if (!_instance) {
throw new Error("Internal Database instance is not initialized");
}
return _instance;
},
close: function () {
if (_instance && _instance.db()) _instance.db().close();
idb_helper.set_graphene_db(null);
_instance = undefined;
},
add_to_store: function (store_name, value) {
return new Promise((resolve, reject) => {
let transaction = this.instance().db().transaction([store_name], "readwrite");
let store = transaction.objectStore(store_name);
let request = store.add(value);
request.onsuccess = () => { resolve(value); };
request.onerror = (e) => {
console.log("ERROR!!! add_to_store - can't store value in db. ", e.target.error.message, value);
reject(e.target.error.message);
};
});
},
remove_from_store: function (store_name, value) {
return new Promise((resolve, reject) => {
let transaction = this.instance().db().transaction([store_name], "readwrite");
let store = transaction.objectStore(store_name);
let request = store.delete(value);
request.onsuccess = () => { resolve(); };
request.onerror = (e) => {
console.log("ERROR!!! remove_from_store - can't remove value from db. ", e.target.error.message, value);
reject(e.target.error.message);
};
});
},
load_data: function (store_name) {
return new Promise((resolve, reject) => {
let data = [];
let transaction = this.instance().db().transaction([store_name], "readonly");
let store = transaction.objectStore(store_name);
let request = store.openCursor();
//request.oncomplete = () => { resolve(data); };
request.onsuccess = e => {
let cursor = e.target.result;
if (cursor) {
data.push(cursor.value);
cursor.continue();
} else {
resolve(data);
}
};
request.onerror = (e) => {
console.log("ERROR!!! open_store - can't get '`${store_name}`' cursor. ", e.target.error.message);
reject(e.target.error.message);
};
});
},
/** Persisted to disk but not backed up.
@return promise
*/
getCachedProperty: function(name, default_value) {
var db = this.instance().db()
var transaction = db.transaction(["cached_properties"], "readonly")
var store = transaction.objectStore("cached_properties")
return idb_helper.on_request_end( store.get(name) ).then( event => {
var result = event.target.result
return result ? result.value : default_value
}).catch( error => { console.error(error); throw error })
},
/** Persisted to disk but not backed up. */
setCachedProperty: function(name, value) {
var db = this.instance().db()
var transaction = db.transaction(["cached_properties"], "readwrite")
var store = transaction.objectStore("cached_properties")
if(value && value["toJS"]) value = value.toJS() //Immutable-js
return idb_helper.on_request_end( store.put({name, value}) )
.catch( error => { console.error(error); throw error })
},
backup: function (store_names = WALLET_BACKUP_STORES) {
var promises = []
for (var store_name of store_names) {
promises.push(this.load_data(store_name))
}
//Add each store name
return Promise.all(promises).then( results => {
var obj = {}
for (let i = 0; i < store_names.length; i++) {
var store_name = store_names[i]
if( store_name === "wallet" ) {
var wallet_array = results[i]
// their should be only 1 wallet per database
for(let wallet of wallet_array)
wallet.backup_date = new Date().toISOString()
}
obj[store_name] = results[i]
}
return obj
})
},
restore: function(wallet_name, object) {
var database_name = getDatabaseName(wallet_name)
return openDatabase(database_name).then( db => {
var store_names = Object.keys(object)
var trx = db.transaction(store_names, "readwrite")
for(let store_name of store_names) {
var store = trx.objectStore(store_name)
var records = object[store_name]
for(let record of records) {
store.put(record)
}
}
return idb_helper.on_transaction_end(trx)
})
}
};
})();
export default iDB;