157 lines
No EOL
3.9 KiB
JavaScript
157 lines
No EOL
3.9 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const cache = {};
|
|
|
|
function tableCache(table) {
|
|
let tableCache = cache[table];
|
|
if (!tableCache) {
|
|
tableCache = {};
|
|
cache[table] = tableCache;
|
|
}
|
|
return tableCache;
|
|
}
|
|
|
|
function dbSerialize(data) {
|
|
return JSON.stringify(data, replacer, 2);
|
|
}
|
|
module.exports.dbSerialize = dbSerialize;
|
|
|
|
function dbDeserialize(data) {
|
|
return JSON.parse(data, reviver);
|
|
}
|
|
module.exports.dbDeserialize = dbDeserialize;
|
|
|
|
function dbDelete(table, id) {
|
|
const file = dbFile(table, id);
|
|
if (fs.existsSync(file)) {
|
|
fs.unlinkSync(file);
|
|
}
|
|
delete tableCache(table)[id];
|
|
}
|
|
module.exports.dbDelete = dbDelete;
|
|
|
|
function dbWrite(table, id, data) {
|
|
const file = dbFile(table, id);
|
|
fs.writeFileSync(file, dbSerialize(data));
|
|
tableCache(table)[id] = data;
|
|
}
|
|
module.exports.dbWrite = dbWrite;
|
|
|
|
/**
|
|
* Gets an entry from the database.
|
|
* @param {string} table The table name
|
|
* @param {string} id The record ID.
|
|
* @returns {object | null} The entry
|
|
*/
|
|
function dbGet(table, id) {
|
|
const file = dbFile(table, id);
|
|
let data = tableCache(table)[id];
|
|
if (data === undefined) {
|
|
if (fs.existsSync(file)) {
|
|
data = dbDeserialize(fs.readFileSync(file));
|
|
} else {
|
|
data = null;
|
|
}
|
|
tableCache(table)[id] = data;
|
|
}
|
|
return data;
|
|
}
|
|
module.exports.dbGet = dbGet;
|
|
|
|
/**
|
|
* Gets all entires of a given table from the database.
|
|
* @param {string} table The table name
|
|
* @returns {object[]} The entries
|
|
*/
|
|
function dbGetAll(table) {
|
|
const files = fs.readdirSync(dbDir(table));
|
|
const all = [];
|
|
for (const file of files) {
|
|
const id = file.split('.')[0];
|
|
const data = dbGet(table, id);
|
|
if (!data) {
|
|
continue;
|
|
}
|
|
all.push(data);
|
|
}
|
|
return all;
|
|
}
|
|
module.exports.dbGetAll = dbGetAll;
|
|
|
|
function dbDir(table) {
|
|
return path.join('data', dbSafe(table));
|
|
}
|
|
|
|
function dbFile(table, id) {
|
|
return path.join('data', dbSafe(table), `${dbSafe(id)}.json`);
|
|
}
|
|
|
|
function dbSafe(str) {
|
|
return str.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
}
|
|
|
|
function reviver(key, value) {
|
|
if (typeof value === "object" && value != null) {
|
|
for (const customTypeName in customTypes) {
|
|
if (value.hasOwnProperty(customTypeName)) {
|
|
const customTypeValue = value[customTypeName];
|
|
const customType = customTypes[customTypeName];
|
|
return customType.reviver(customTypeValue);
|
|
}
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function replacer(key, value) {
|
|
const rawValue = this[key];
|
|
for (const customTypeName in customTypes) {
|
|
const customType = customTypes[customTypeName];
|
|
if (customType.condition(rawValue)) {
|
|
return { [customTypeName]: customType.replacer(rawValue) };
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Custom types support for JSON files. Make sure that no custom type names conflict with actual possible JSON keys.
|
|
*
|
|
* When serializing:
|
|
* - Custom types work by running the `condition` function of every value to be serialized.
|
|
* - If it returns `true`, the `replacer` function is called with the value.
|
|
* - The return value of the `replacer` will be saved to JSON wrapped in an object using the key of the custom type. (e.g. `{"$myType": "hello"}`).
|
|
*
|
|
* When deserializing:
|
|
* - Each value is checked if it is an object with a key of any custom type.
|
|
* - If one is found, the `reviver` is called for the value of this key.
|
|
* - The return value is used as the actual value.
|
|
*/
|
|
const customTypes = {
|
|
$date: {
|
|
condition: function (value) {
|
|
return value instanceof Date;
|
|
},
|
|
replacer: function (value) {
|
|
return value.toISOString();
|
|
},
|
|
reviver: function (value) {
|
|
return new Date(value);
|
|
},
|
|
},
|
|
$set: {
|
|
/** @param {Set} value */
|
|
condition: function (value) {
|
|
return value instanceof Set;
|
|
},
|
|
/** @param {Set} value */
|
|
replacer: function (value) {
|
|
return [...value];
|
|
},
|
|
/** @param {Array} value */
|
|
reviver: function (value) {
|
|
return new Set(value);
|
|
},
|
|
},
|
|
} |