Skip to content

Instantly share code, notes, and snippets.

@appinteractive
Created January 6, 2024 15:03
Show Gist options
  • Select an option

  • Save appinteractive/3816bbbed73b25816a6b3bc191f74e55 to your computer and use it in GitHub Desktop.

Select an option

Save appinteractive/3816bbbed73b25816a6b3bc191f74e55 to your computer and use it in GitHub Desktop.
import crypto from 'crypto'
import PouchDB from 'pouchdb'
import md5 from 'md5'
import { app } from 'electron'
import { join } from 'path'
import { existsSync, mkdirSync, rmdirSync } from 'fs'
class EncryptedPouchDB extends PouchDB {
constructor(name, options = {}) {
const userData = app.getPath('userData')
const dir = join(userData, 'data')
if (!existsSync(dir)) {
console.log('DB - creating database directory', name, dir)
mkdirSync(dir)
}
const dbDir = join(dir, name)
if (!existsSync(dbDir)) {
console.log('DB - need to created db dir', dbDir)
}
super(dbDir, options)
this.cryptSalt = md5(name).slice(0, 16)
this.cryptFields = options.cryptFields || [
'name',
'title',
'amount',
'total',
'tags',
'account',
]
}
async remove() {
const dir = this.name
if (!existsSync(dir)) {
return
}
rmdirSync(dir, { recursive: true })
return
}
async allDocs(...args) {
const res = await super.allDocs(...args)
if (res.total_rows) {
res.rows.map((row) => {
row.doc = this.encryptOrDecryptDoc(row.doc, false)
return row
})
}
return res
}
async bulkDocs(...args) {
const docs = args[0].docs || args[0]
if (docs) {
const encrypted = docs.map((doc) => this.encryptOrDecryptDoc(doc, true))
return await super.bulkDocs(encrypted, ...args.slice(1))
}
return super.bulkDocs(docs)
}
async upsertBulk(docs, opts = {}) {
const ids = docs.map((d) => d._id)
const res = await this.allDocs({ keys: ids, include_docs: true })
res.rows.forEach((item, i) => {
if (!item.error && item.doc) {
docs[i] = { ...item.doc, ...docs[i], ...{ _rev: item.doc._rev } }
} else {
delete docs[i]._rev
}
})
return this.bulkDocs(docs)
}
encryptOrDecryptDoc(doc, encrypt = true) {
const data = {}
if (!doc) return data
Object.keys(doc).forEach((key) => {
if (this.cryptFields.includes(key)) {
data[key] = encrypt ? this.encrypt(doc[key]) : this.decrypt(doc[key])
} else {
data[key] = doc[key]
}
})
return data
}
encrypt(text) {
const cipher = crypto.createCipheriv(
'aes-256-cbc',
EncryptedPouchDB.cryptKey,
this.cryptSalt
)
const crypted = cipher.update(JSON.stringify(text), 'utf8', 'base64')
return crypted + cipher.final('base64')
}
decrypt(text) {
try {
const decipher = crypto.createDecipheriv(
'aes-256-cbc',
EncryptedPouchDB.cryptKey,
this.cryptSalt
)
const dec = decipher.update(text, 'base64', 'utf8')
return JSON.parse(dec + decipher.final('utf8'))
} catch (err) {
console.log(`Error while decrypting`, err.message)
console.error(err)
return
}
}
static setEncryptionKey(key) {
EncryptedPouchDB.cryptKey = key ? Buffer.from(md5(key)) : null
}
}
export default EncryptedPouchDB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment