diff --git a/packages/xo-server/package.json b/packages/xo-server/package.json index 9896d80a6..bf02b9d47 100644 --- a/packages/xo-server/package.json +++ b/packages/xo-server/package.json @@ -86,6 +86,7 @@ "iterable-backoff": "^0.1.0", "jest-worker": "^26.3.0", "js-yaml": "^3.10.0", + "json-stringify-safe": "^5.0.1", "json-rpc-peer": "^0.17.0", "json5": "^2.0.1", "kindof": "^2.0.0", diff --git a/packages/xo-server/src/xo-mixins/store.js b/packages/xo-server/src/xo-mixins/store.js index 9432c3210..609681c49 100644 --- a/packages/xo-server/src/xo-mixins/store.js +++ b/packages/xo-server/src/xo-mixins/store.js @@ -1,7 +1,11 @@ +import jsonStringifySafe from 'json-stringify-safe' import levelup from 'level-party' import sublevel from 'subleveldown' +import { createLogger } from '@xen-orchestra/log' import { ensureDir } from 'fs-extra' +const log = createLogger('xo:store') + // =================================================================== const _levelHas = async function has(key, cb) { @@ -27,6 +31,22 @@ const levelHas = db => { // =================================================================== +const valueEncoding = { + buffer: false, + decode: JSON.parse, + encode: data => { + try { + return JSON.stringify(data) + } catch (error) { + log.warn(error) + + // attempts to stringify by removing circular references + return jsonStringifySafe(data) + } + }, + type: 'safe-json', +} + export default class { constructor(xo, config) { const dir = `${config.datadir}/leveldb` @@ -34,10 +54,6 @@ export default class { } async getStore(namespace) { - return levelHas( - sublevel(await this._db, namespace, { - valueEncoding: 'json', - }) - ) + return levelHas(sublevel(await this._db, namespace, { valueEncoding })) } }