service-core/core/db.mjs

143 lines
3.5 KiB
JavaScript

import { setTimeout } from 'timers/promises'
import { Low, JSONFile, Memory } from 'lowdb'
import fs from 'fs'
import { defaults, isObject } from './defaults.mjs'
export default function GetDB(config, log, orgFilename = 'db.json') {
let adapter = new Memory()
let fullpath = 'in-memory'
if (orgFilename) {
fullpath = orgFilename
adapter = new JSONFile(fullpath)
}
const db = new Low(adapter)
db.id = 'id'
db.filename = fullpath
db.config = config
db.createId = function(collection) {
if (collection.length) {
return (collection[collection.length - 1].id || 0) + 1
}
return 1
}
db.get = function(collection, id, returnIndex = false) {
let col = db.getCollection(collection)
for (let i = col.length - 1; i >= 0; i--) {
if (col[i][db.id] === id) {
if (returnIndex) return i
return col[i]
}
}
return null
}
db.getCollection = function(collection) {
if (typeof(collection) === 'string') {
return db.data[collection]
}
return collection
}
db.upsert = function(collection, item) {
let col = db.getCollection(collection)
if (item[db.id]) {
let i = db.get(col, item[db.id], true)
if (i !== null) {
col[i] = item
return
}
} else {
item[db.id] = db.createId(col)
}
col.push(item)
}
db.upsertFirst = function(collection, item) {
let col = db.getCollection(collection)
if (item[db.id]) {
let i = db.get(col, item[db.id], true)
if (i !== null) {
col[i] = item
return
}
} else {
item[db.id] = db.createId(col)
}
col.splice(0, 0, item)
}
db.remove = function(collection, itemOrId) {
let col = db.getCollection(collection)
let id = itemOrId
if (typeof(id) === 'object') {
id = id[db.id]
}
for (let i = col.length - 1; i >= 0; i--) {
if (col[i][db.id] === id) {
col.splice(i, 1)
return true
}
}
return false
}
db.addApplication = function(name) {
db.data.core[name] ||= {}
defaults(db.data.core[name], {
active: '',
latestInstalled: '',
updater: '',
versions: [],
})
}
db.log = log
db._write = db.write.bind(db)
db.write = function() {
return this._write()
// Do couple of retries. Sometimes it fails randomly doing atomic writes.
.catch(() => { return setTimeout(20).then(() => { return this._write() }) })
.catch(() => { return setTimeout(50).then(() => { return this._write() }) })
.catch(() => { return setTimeout(100).then(() => { return this._write() }) })
.catch((err) => {
try {
db.writeSync()
} catch {
this.log.error(err, 'Error saving to db')
}
})
}
db.writeSync = function() {
try {
fs.writeFileSync(db.filename, JSON.stringify(db.data))
} catch(err) {
db.log.error(err, `Error during writeSync to ${db.filename}`)
}
}
return db.read()
.then(function() {
if (!isObject(db.data)) {
if (fullpath !== 'in-memory') {
db.log.warn(`File ${fullpath} was empty or not a json object, clearing it.`)
}
db.data = {}
}
defaults(db.data, { core: { version: 1 } })
return db.write()
})
.then(
function() { return db },
function(err) {
let wrapped = new Error(`Error writing to ${fullpath}: ${err.message} (${err.code})`)
wrapped.code = err.code
throw wrapped
}
)
}