classic-ui-dev/plugins/idb.js
2022-10-30 23:58:12 +00:00

308 lines
7.1 KiB
JavaScript

import { openDB, deleteDB } from 'idb'
import networkConfig from '@/networkConfig'
import { INDEX_DB_ERROR } from '@/constants'
// TODO method for migration, remove indexed
class IndexedDB {
constructor({ stores, dbName }) {
this.dbExists = false
this.isBlocked = false
this.options = {
upgrade(db) {
Object.values(db.objectStoreNames).forEach((value) => {
db.deleteObjectStore(value)
})
stores.forEach(({ name, keyPath, indexes }) => {
const store = db.createObjectStore(name, {
keyPath,
autoIncrement: true
})
if (Array.isArray(indexes)) {
indexes.forEach(({ name, unique = false }) => {
store.createIndex(name, name, { unique })
})
}
})
}
}
this.dbName = dbName
}
async initDB() {
try {
if (this.dbExists) {
return
}
this.db = await openDB(this.dbName, 34, this.options) // version (optional): Schema version, or undefined to open the current version.
this.onEventHandler()
this.dbExists = true
} catch (err) {
// need for private mode firefox browser
if (err.message.includes(INDEX_DB_ERROR)) {
this.isBlocked = true
return
}
if (err.message.includes('less than the existing version')) {
await this._removeExist()
}
console.error(`Method initDB has error: ${err.message}`)
}
}
onEventHandler() {
this.db.addEventListener('onupgradeneeded', async () => {
await this._removeExist()
})
}
async _removeExist() {
await deleteDB(this.dbName)
this.dbExists = false
await this.initDB()
}
async getFromIndex(params) {
if (this.isBlocked) {
return undefined
}
try {
return await this._getFromIndex(params)
} catch (err) {
return undefined
}
}
async _getFromIndex({ storeName, indexName, key }) {
try {
const value = await this.db.getFromIndex(storeName, indexName, key)
return value
} catch (err) {
throw new Error(`Method getFromIndex has error: ${err.message}`)
}
}
async getAllFromIndex(params) {
if (this.isBlocked) {
return []
}
try {
return await this._getAllFromIndex(params)
} catch (err) {
return []
}
}
async _getAllFromIndex({ storeName, indexName, key, count }) {
try {
const value = await this.db.getAllFromIndex(storeName, indexName, key, count)
return value
} catch (err) {
throw new Error(`Method getAllFromIndex has error: ${err.message}`)
}
}
async getItem({ storeName, key }) {
if (this.isBlocked) {
return undefined
}
try {
const store = this.db.transaction(storeName).objectStore(storeName)
const value = await store.get(key)
return value
} catch (err) {
throw new Error(`Method getItem has error: ${err.message}`)
}
}
async addItem({ storeName, data, key = '' }) {
if (this.isBlocked) {
return
}
try {
const tx = this.db.transaction(storeName, 'readwrite')
const isExist = await tx.objectStore(storeName).get(key)
if (!isExist) {
await tx.objectStore(storeName).add(data)
}
} catch (err) {
throw new Error(`Method addItem has error: ${err.message}`)
}
}
async putItem({ storeName, data }) {
if (this.isBlocked) {
return
}
try {
const tx = this.db.transaction(storeName, 'readwrite')
await tx.objectStore(storeName).put(data)
} catch (err) {
throw new Error(`Method putItem has error: ${err.message}`)
}
}
async getAll({ storeName }) {
if (this.isBlocked) {
return []
}
try {
const tx = this.db.transaction(storeName, 'readonly')
const store = tx.objectStore(storeName)
const data = await store.getAll()
return data
} catch (err) {
throw new Error(`Method getAll has error: ${err.message}`)
}
}
async clearStore({ storeName, mode = 'readwrite' }) {
if (this.isBlocked) {
return
}
try {
const tx = this.db.transaction(storeName, mode)
await tx.objectStore(storeName).clear()
} catch (err) {
throw new Error(`Method clearStore has error: ${err.message}`)
}
}
async createTransactions({ storeName, data, mode = 'readwrite' }) {
if (this.isBlocked) {
return
}
try {
const tx = this.db.transaction(storeName, mode)
await tx.objectStore(storeName).add(data)
await tx.done
} catch (err) {
throw new Error(`Method createTransactions has error: ${err.message}`)
}
}
createMultipleTransactions({ storeName, data, index, mode = 'readwrite' }) {
if (this.isBlocked) {
return
}
try {
const tx = this.db.transaction(storeName, mode)
data.map((item) => {
if (item) {
tx.store.put({ ...item, ...index })
}
})
} catch (err) {
throw new Error(`Method createMultipleTransactions has error: ${err.message}`)
}
}
}
export default async (ctx, inject) => {
const instances = new Map()
const DEPOSIT_INDEXES = [
{ name: 'transactionHash', unique: false },
{ name: 'commitment', unique: true }
]
const WITHDRAWAL_INDEXES = [
{ name: 'nullifierHash', unique: true } // keys on which the index is created
]
const LAST_EVENT_INDEXES = [{ name: 'name', unique: false }]
const defaultState = [
{
name: 'encrypted_events',
keyPath: 'transactionHash'
},
{
name: 'lastEvents',
keyPath: 'name',
indexes: LAST_EVENT_INDEXES
}
]
for (const key of Object.keys(networkConfig)) {
const { tokens, nativeCurrency } = networkConfig[key]
const netId = Number(key.replace('netId', ''))
const stores = [...defaultState]
if (netId === 1) {
stores.push({
name: 'register_events',
keyPath: 'ensName'
})
}
Object.keys(tokens).forEach((token) => {
Object.keys(tokens[token].instanceAddress).forEach((amount) => {
if (nativeCurrency === token && netId === 1) {
stores.push({
name: `stringify_bloom_${token}_${amount}`,
keyPath: 'hashBloom'
})
}
stores.push(
{
name: `deposits_${token}_${amount}`,
keyPath: 'leafIndex', // the key by which it refers to the object must be in all instances of the storage
indexes: DEPOSIT_INDEXES
},
{
name: `withdrawals_${token}_${amount}`,
keyPath: 'blockNumber',
indexes: WITHDRAWAL_INDEXES
},
{
name: `stringify_tree_${token}_${amount}`,
keyPath: 'hashTree'
}
)
})
})
const options = {
stores,
dbName: `tornado_cash_${netId}`
}
const instance = new IndexedDB(options)
instances.set(options.dbName, instance)
await instance.initDB()
}
const getInstance = (netId) => instances.get(`tornado_cash_${netId}`)
ctx.$indexedDB = getInstance
inject('indexedDB', getInstance)
}