limelightdb
Version:
A lightweight local database
315 lines (314 loc) • 15.2 kB
JavaScript
;
exports.__esModule = true;
exports.Table = exports.Database = exports.LimelightDB = void 0;
var ajv_1 = require("ajv");
var crypto = require("crypto");
var fs = require("fs");
var path = require("path");
var server_1 = require("./server");
var LimelightDB = /** @class */ (function () {
function LimelightDB(filename, humanReadable, key, port) {
if (key === void 0) { key = null; }
var _this = this;
this.initialize = function () {
if (!fs.existsSync(_this.filename) || fs.statSync(_this.filename).size == 0)
_this.write(new Database());
else {
var database = void 0;
try {
database = JSON.parse(fs.readFileSync(_this.filename, "utf-8"));
}
catch (e) {
throw new Error("Database is corrupted.");
}
if (!database.iv && !database.encryptedData && _this.key) {
console.log("Database is not encrypted, but a key is provided. Rebuilding...");
_this.write(new Database(database));
console.log("Database is now encrypted! If this was accidental, you can remove the key with the \".decrypt()\" method.");
}
else if (database.iv && database.encryptedData && !_this.key)
throw new Error("Database is encrypted, but no key is provided.");
else if (database.iv && database.encryptedData && _this.key) {
try {
_this.read();
}
catch (e) {
throw new Error("Database is encrypted, and the key provided is invalid, or the database is corrupted.");
}
}
else {
try {
_this.read();
}
catch (e) {
throw new Error("Database is corrupted.");
}
}
if ((database.iv && database.encryptedData) || (!database.iv && !database.encryptedData && _this.key))
_this.encrypted = true;
}
if (_this.port && _this.key) {
_this.server = (0, server_1.startServer)(_this);
}
_this.version = "3.1.5";
return _this;
};
this.stopServer = function () {
if (_this.server)
_this.server.close();
};
this.decrypt = function (key) {
var database = JSON.parse(fs.readFileSync(_this.filename, "utf-8"));
if (!database.iv && !database.encryptedData)
throw new Error("Database is not encrypted.");
console.log("Decrypting database with key...");
if (_this.key)
_this.write(new Database(JSON.parse(decrypt(database, _this.key))), true);
else if (key)
_this.write(new Database(JSON.parse(decrypt(database, key))), true);
else
throw new Error("No key is provided.");
console.log("Database is now decrypted. Do not provide a key when initializing the database or it will be re-encrypted.");
_this.key = null;
_this.encrypted = false;
return _this;
};
this.alter = function (table, changes) {
var database = _this.read();
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (!selectedTable)
throw new Error("Table \"".concat(table, "\" does not exist. Did you mean to create it (\".create(...)\" method)?"));
selectedTable.alterTable(changes, database);
_this.write(database);
};
this.select = function (table, filter, limit) {
var database = _this.read();
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (!selectedTable)
throw new Error("Table \"".concat(table, "\" does not exist. Did you mean to create it (\".create(...)\" method)?"));
if (!selectedTable.rows)
return [];
if (!limit)
return selectedTable.rows.filter(function (x) { return filter(x); });
else
return selectedTable.rows.filter(function (x) { return filter(x); }).slice(0, limit);
};
this.create = function (table, cols, schema, autoID) {
var database = _this.read();
if (!database.tables)
database.tables = [];
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (selectedTable)
throw new Error("Table \"".concat(table, "\" already exists."));
if (cols.length != Object.keys(schema).length)
throw new Error("Schema does not match columns.");
cols.forEach(function (x) {
if (Object.keys(schema).indexOf(x) == -1)
throw new Error("Schema does not match columns.");
});
if (!schema["id"])
schema["id"] = { type: "number" };
database.tables.push(new Table(table, cols, {
type: "object",
properties: schema,
required: cols,
additionalProperties: false
}, autoID));
_this.write(database);
};
this.insert = function (table, rows) {
var database = _this.read();
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (!selectedTable)
throw new Error("Table \"".concat(table, "\" does not exist. Did you mean to create it (\".create(...)\" method)?"));
rows.forEach(function (x) { return selectedTable.createRow(x); });
_this.write(database);
};
this.update = function (table, filter, row) {
var database = _this.read();
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (!selectedTable)
throw new Error("Table \"".concat(table, "\" does not exist. Did you mean to create it (\".create(...)\" method)?"));
var rows = selectedTable.rows.filter(function (x) { return filter(x); });
if (!(rows === null || rows === void 0 ? void 0 : rows.length))
return;
var _loop_1 = function () {
var newRow = Object.assign({}, rows[i], row);
var tempId = void 0;
if (newRow["id"] && selectedTable.autoId) {
tempId = newRow["id"];
delete newRow["id"];
}
Object.keys(newRow).forEach(function (x) { if (selectedTable.schema.properties[x].type == "number")
newRow[x] = parseInt(newRow[x]); });
Object.keys(row).forEach(function (x) { if (selectedTable.schema.properties[x].type == "number")
row[x] = parseInt(row[x]); });
if (!new ajv_1["default"]().compile(selectedTable.schema)(newRow))
throw new Error("Error while updating row in \"".concat(table, "\"\n ").concat(JSON.stringify(newRow), " does not match\n ").concat(JSON.stringify(selectedTable.schema.properties), "."));
if (tempId && selectedTable.autoId)
newRow["id"] = tempId;
Object.assign(rows[i], row);
};
for (var i = 0; i < rows.length; i++) {
_loop_1();
}
_this.write(database);
};
this["delete"] = function (table, filter) {
var database = _this.read();
var selectedTable = database.tables.find(function (x) { return x.name == table; });
if (!selectedTable)
throw new Error("Table \"".concat(table, "\" does not exist. Did you mean to create it (\".create(...)\" method)?"));
var rows = selectedTable.rows.filter(function (x) { return filter(x); });
if (!(rows === null || rows === void 0 ? void 0 : rows.length))
return;
for (var i = 0; i < rows.length; i++) {
var index = selectedTable.rows.indexOf(rows[i]);
if (index == undefined)
return;
selectedTable.rows.splice(index, 1);
}
_this.write(database);
};
this.read = function () {
if (!_this.key) {
return new Database(JSON.parse(fs.readFileSync(_this.filename, "utf-8")));
}
else {
return new Database(JSON.parse(decrypt(JSON.parse(fs.readFileSync(_this.filename, "utf-8")), _this.key)));
}
};
this.write = function (database, encryptOverride) {
if (!_this.key || encryptOverride) {
fs.writeFileSync(_this.filename, database.raw(_this.humanReadable));
}
else {
fs.writeFileSync(_this.filename, JSON.stringify(encrypt(database.raw(_this.humanReadable), _this.key), null, _this.humanReadable ? 2 : 0));
}
};
this.filename = filename;
if (!path.extname(filename))
this.filename += ".limelight";
this.humanReadable = humanReadable ? true : false;
this.key = key;
if (port)
this.port = port;
}
return LimelightDB;
}());
exports.LimelightDB = LimelightDB;
var Database = /** @class */ (function () {
function Database(database) {
var _this = this;
this.raw = function (humanReadable) {
return JSON.stringify(_this, null, humanReadable ? 2 : 0);
};
if (database === null || database === void 0 ? void 0 : database.tables) {
this.tables = [];
database.tables.forEach(function (x) {
_this.tables.push(new Table(x.name, x.cols, x.schema, x.autoId, x.rows));
});
}
}
return Database;
}());
exports.Database = Database;
var Table = /** @class */ (function () {
function Table(name, cols, schema, autoId, rows) {
var _this = this;
this.alterTable = function (changes, database) {
var _a, _b;
if (changes.schema) {
Object.keys(changes.schema).forEach(function (x) {
var _a;
if (!_this.schema.properties[x] || _this.schema.properties[x].type != changes.schema[x].type) {
(_a = _this.rows) === null || _a === void 0 ? void 0 : _a.forEach(function (y) {
switch (changes.schema[x].type) {
case "number":
case "integer":
y[x] = 0;
break;
case "string":
y[x] = "";
break;
case "boolean":
y[x] = false;
break;
case "array":
y[x] = [];
break;
case "object":
y[x] = {};
break;
case "null":
y[x] = null;
break;
}
});
}
});
Object.keys(_this.schema.properties).forEach(function (x) {
var _a;
if (!changes.schema[x] && x != "id")
(_a = _this.rows) === null || _a === void 0 ? void 0 : _a.forEach(function (y) { return (delete y[x]); });
});
_this.cols = Object.keys(changes.schema);
_this.schema.properties = changes.schema;
_this.schema.required = Object.keys(changes.schema);
}
if (changes.name) {
if (database.tables.find(function (x) { return x.name == changes.name; }))
throw new Error("Table \"".concat(changes.name, "\" already exists."));
_this.name = changes.name;
}
if (changes.autoId) {
if (changes.autoId) {
(_a = _this.rows) === null || _a === void 0 ? void 0 : _a.forEach(function (x) {
x["id"] = _this.rows.length + 1;
});
}
else {
(_b = _this.rows) === null || _b === void 0 ? void 0 : _b.forEach(function (x) { return (delete x["id"]); });
}
_this.autoId = changes.autoId;
}
};
this.createRow = function (row) {
Object.keys(row).forEach(function (x) { if (_this.schema.properties[x].type == "number")
row[x] = parseInt(row[x]); });
if (!new ajv_1["default"]().compile(_this.schema)(row))
throw new Error("Error while inserting new row into \"".concat(_this.name, "\"\n ").concat(JSON.stringify(row), " does not match\n ").concat(JSON.stringify(_this.schema.properties), "."));
if (!_this.rows)
_this.rows = [];
if (_this.autoId) {
row["id"] = _this.rows.length + 1;
}
_this.rows.push(row);
};
this.name = name;
this.cols = cols;
if (rows)
this.rows = rows;
this.schema = schema;
this.autoId = autoId ? true : false;
}
return Table;
}());
exports.Table = Table;
function encrypt(data, key) {
var iv = crypto.randomBytes(16);
key = crypto.createHash("md5").update(key).digest("hex");
var cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(key), iv);
var encrypted = cipher.update(data);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return { iv: iv.toString("hex"), encryptedData: encrypted.toString("hex") };
}
function decrypt(data, key) {
var iv = Buffer.from(data.iv, "hex");
key = crypto.createHash("md5").update(key).digest("hex");
var encryptedText = Buffer.from(data.encryptedData, "hex");
var decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key), iv);
var decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}