Migrate to SQLite for persistence, and fix native modules in Electron
parent
63e7c883f3
commit
a5ffac8d75
@ -0,0 +1,17 @@
|
|||||||
|
detectedArch=`uname -m`
|
||||||
|
|
||||||
|
case "$detectedArch" in
|
||||||
|
i?86) arch="x32" ;;
|
||||||
|
x86_64) arch="x64" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$OSTYPE" in
|
||||||
|
darwin*) platform="darwin" ;;
|
||||||
|
linux*) platform="linux" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Rebuilding SQLite3 for Electron... ($platform, $arch)"
|
||||||
|
|
||||||
|
npm rebuild --runtime="node" --target="5.1.0" --arch="$arch" sqlite3
|
||||||
|
mkdir -p node_modules/sqlite3/lib/binding/electron-v1.0-$platform-$arch
|
||||||
|
cp "node_modules/sqlite3/lib/binding/node-v47-$platform-$arch/node_sqlite3.node" "node_modules/sqlite3/lib/binding/electron-v1.0-$platform-$arch/node_sqlite3.node"
|
@ -0,0 +1,164 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Promise = require("bluebird");
|
||||||
|
const knex = require("knex");
|
||||||
|
const fs = Promise.promisifyAll(require("fs"));
|
||||||
|
const matchObject = require("match-object");
|
||||||
|
const uuid = require("uuid");
|
||||||
|
const pick = require("object-pick");
|
||||||
|
const snakeCase = require("snake-case");
|
||||||
|
const rfr = require("rfr");
|
||||||
|
|
||||||
|
const knexErrors = rfr("lib/db/knex-error-type");
|
||||||
|
|
||||||
|
function loadCollection(db, collection) {
|
||||||
|
return Promise.try(() => {
|
||||||
|
return db(collection).select();
|
||||||
|
}).then((results) => {
|
||||||
|
return results.map((result) => {
|
||||||
|
return Object.assign(JSON.parse(result.value), {
|
||||||
|
$id: result.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(path) {
|
||||||
|
let collections = {};
|
||||||
|
|
||||||
|
let db = knex({
|
||||||
|
client: "sqlite3",
|
||||||
|
connection: {
|
||||||
|
filename: path
|
||||||
|
},
|
||||||
|
useNullAsDefault: true,
|
||||||
|
debug: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function createTable(name) {
|
||||||
|
return db.schema.createTable(name, function(table) {
|
||||||
|
table.text("id");
|
||||||
|
table.text("value");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollection(name) {
|
||||||
|
let indexes; // TODO
|
||||||
|
|
||||||
|
function findIdIndex(id) {
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collections[name].findIndex((item) => item.$id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
find: function(query) {
|
||||||
|
return collections[name].filter((item) => matchObject(query, item));
|
||||||
|
},
|
||||||
|
findOne: function(query) {
|
||||||
|
let result = collections[name].find((item) => matchObject(query, item));
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
throw new Error("No results found");
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
insert: function(object, options = {}) {
|
||||||
|
/* Intentional mutation. */
|
||||||
|
Object.assign(object, {$id: uuid.v4()});
|
||||||
|
collections[name].push(object);
|
||||||
|
|
||||||
|
return db(name).insert({
|
||||||
|
id: object.$id,
|
||||||
|
value: JSON.stringify(object)
|
||||||
|
}).then(() => {});
|
||||||
|
},
|
||||||
|
update: function(object, options = {}) {
|
||||||
|
let index;
|
||||||
|
if (index = findIdIndex(object.$id)) {
|
||||||
|
if (options.patch === true) {
|
||||||
|
Object.assign(collections[name][index], object);
|
||||||
|
} else {
|
||||||
|
collections[name][index] = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return db(name).where({id: object.$id}).update({
|
||||||
|
value: JSON.stringify(collections[name][index])
|
||||||
|
}).then(() => {});
|
||||||
|
} else {
|
||||||
|
throw new Error("No such object exists");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
upsert: function(object, options = {}) {
|
||||||
|
try {
|
||||||
|
this.update(object, options);
|
||||||
|
} catch (err) {
|
||||||
|
this.insert(object, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
upsertBy: function(keys, object, options = {}) {
|
||||||
|
let query = pick(object, keys);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let result = this.findOne(query);
|
||||||
|
// FIXME: The following shouldn't be in the try block...
|
||||||
|
object.$id = result.$id;
|
||||||
|
return this.update(object, options);
|
||||||
|
} catch (err) {
|
||||||
|
return this.insert(object, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
delete: function(object) {
|
||||||
|
let index;
|
||||||
|
if (index = findIdIndex(object.$id)) {
|
||||||
|
collections[name].splice(index, 1);
|
||||||
|
|
||||||
|
return db(name).where({id: object.$id}).delete().then(() => {});
|
||||||
|
} else {
|
||||||
|
throw new Error("No such object exists");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteBy: function(query) {
|
||||||
|
let toRemove = collections[name].filter((item) => matchObject(query, item));
|
||||||
|
collections[name] = collections[name].filter((item) => (toRemove.indexOf(item) !== -1));
|
||||||
|
|
||||||
|
return Promise.map(toRemove, (item) => {
|
||||||
|
return db(name).where({id: item.$id}).delete();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ensureIndex: function(property) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbAPI = {
|
||||||
|
close: function() {},
|
||||||
|
collection: function(name) {
|
||||||
|
return Promise.try(() => {
|
||||||
|
let snakeCasedName = snakeCase(name);
|
||||||
|
|
||||||
|
if (collections[snakeCasedName] != null) {
|
||||||
|
return getCollection(snakeCasedName);
|
||||||
|
} else {
|
||||||
|
return Promise.try(() => {
|
||||||
|
return loadCollection(db, snakeCasedName);
|
||||||
|
}).then((collectionData) => {
|
||||||
|
collections[snakeCasedName] = collectionData;
|
||||||
|
}).catch(knexErrors.sqlite.NoSuchTable, (err) => {
|
||||||
|
collections[snakeCasedName] = [];
|
||||||
|
return createTable(snakeCasedName);
|
||||||
|
}).then(() => {
|
||||||
|
return getCollection(snakeCasedName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbAPI;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sqlite: {
|
||||||
|
NoSuchTable: function(err) {
|
||||||
|
return err.message.includes("SQLITE_ERROR: no such table:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue