oxzof-db
Version:
- It is a simple data processing module based on BSON.
372 lines (328 loc) • 12.8 kB
JavaScript
const fs = require("fs");
const path = require("path");
const https = require("https");
const { BSON } = require("bson");
const { exec } = require("child_process");
const Table = require('cli-table3');
class Database {
constructor(file = false, options = {}) {
const { logMessages=false, autoUpdate=false } = options;
this.logs = logMessages;
this.autoUpdate = autoUpdate;
const filePath = process.cwd();
this.packageName = "oxzof-db";
this.isVersionChecked = false;
this.file = file
? path.join(filePath, file)
: path.join(filePath, "./oxzof-db/database.bson");
this.isWriting = false;
this.checkFile().catch((e) => this.messageLog(e.message));
}
checkPackageVersion() {
if (this.isVersionChecked) return Promise.resolve();
return new Promise((resolve) => {
fs.promises
.readFile(path.join(__dirname, "package.json"), "utf8")
.then((localData) => {
const localVersion = JSON.parse(localData).version;
const registryUrl = `https://registry.npmjs.org/${this.packageName}`;
https.get(registryUrl, (response) => {
let data = "";
response.on("data", (chunk) => (data += chunk));
response.on("end", () => {
try{
const latestVersion = JSON.parse(data)["dist-tags"].latest;
if (localVersion !== latestVersion) {
if (this.autoUpdate) {
this.messageLog(
"New update available, autoUpdate active. Updating...",
"system"
);
exec(`npm install ${this.packageName}`, (error, stdout, stderr) => {
if (error || stderr) {
this.messageLog(error || stderr);
resolve();
return;
}
this.messageLog(
`Module update completed: ${stdout}`,
"system"
);
resolve();
});
} else {
this.messageLog(
`The current version is published as ${latestVersion}. Please update the package: npm i oxzof-db@latest`,
"system"
);
resolve();
}
} else {
resolve();
}
}catch(err){
this.messageLog("NPM Error: "+err,"system")
return;
}
});
}).on("error", (err) => {
this.messageLog("Error in NPM version control: " + err.message,"system");
resolve();
});
this.isVersionChecked = true;
})
.catch((e) => {
this.messageLog("Error during version check: " + e.message,"system");
resolve();
});
});
}
checkFile() {
return new Promise((resolve, reject) => {
const folderPath = path.dirname(this.file);
fs.promises
.mkdir(folderPath, { recursive: true })
.then(() => {
if (!fs.existsSync(this.file)) {
const emptyData = BSON.serialize({});
return fs.promises.writeFile(this.file, emptyData);
} else {
return fs.promises.readFile(this.file).then((fileData) => {
if (fileData.length === 0) {
const emptyData = BSON.serialize({});
return fs.promises.writeFile(this.file, emptyData);
}
});
}
})
.then(() => {
if (!this.file.endsWith(".bson")) {
this.messageLog(
"The file extension must be .bson: " + this.filePath,"system"
);
resolve(false);
} else {
resolve(true);
}
})
.catch((e) => reject(e));
});
}
writeFile(data) {
return new Promise((resolve, reject) => {
const bsonData = BSON.serialize(data);
fs.promises
.writeFile(this.file, bsonData)
.then(() => resolve(true))
.catch((e) => {
this.messageLog(e.message);
resolve(false);
});
});
}
readFile() {
return fs.promises
.readFile(this.file)
.then((data) => (data.length === 0 ? {} : BSON.deserialize(data)))
.catch((e) => {
this.messageLog(e.message);
return false;
});
}
checkFileSync() {
if (!fs.existsSync(this.file)) {
fs.writeFileSync(this.file, BSON.serialize({}));
}
}
readFileSync() {
const fileContent = fs.readFileSync(this.file);
return BSON.deserialize(fileContent);
}
writeFileSync(data) {
const serializedData = BSON.serialize(data);
fs.writeFileSync(this.file, serializedData);
}
messageLog(msg,type="error"){
const table = new Table({
head: ['[oxzof-db] Error Type', 'Message', 'Timestamp'],
colWidths: [20, 50, 30],
});
switch(type){
case "error":
if(this.logs){
table.push(
['Error', msg, new Date().toISOString()]
);
console.warn(table.toString())
}
break;
case "warning":
if(this.logs){
table.push(
['Error', msg, new Date().toISOString()]
);
console.warn(table.toString())
}
break;
case "success":
if(this.logs){
console.warn(`\x1b[32m[oxzof-db] Success: ${msg} \x1b[0m`)
}
break;
case "system":
console.warn(`\x1b[33m[oxzof-db] System: ${msg} \x1b[0m`)
break;
default:
console.warn(`\x1b[31m[oxzof-db] Info: ${msg} \x1b[0m`)
break;
}
}
set(key, value) {
try {
this.checkFileSync();
const data = this.readFileSync();
data[key] = isNaN(value)?value:Number(value);
this.writeFileSync(data);
return value;
} catch (e) {
this.messageLog(`Error during set(): ${e.message}`, "error");
return false;
}
}
get(key = null) {
try {
this.checkFileSync();
const data = this.readFileSync();
return data[key] ? data[key] : false;
} catch (e) {
this.messageLog(`Error during get(): ${e.message}`, "error");
return false;
}
}
all() {
try {
this.checkFileSync();
const data = this.readFileSync();
return Object.entries(data).map(([key, value]) => ({ [key]: value }));
} catch (e) {
this.messageLog(`Error during all(): ${e.message}`, "error");
return false;
}
}
push(key, value) {
try {
this.checkFileSync();
const data = this.readFileSync();
if (!Array.isArray(data[key])) {
data[key] = [];
}
if (Array.isArray(value)) {
data[key].push(...value);
} else {
data[key].push(value);
}
this.writeFileSync(data);
return data[key];
} catch (e) {
this.messageLog(`Error during push(): ${e.message}`, "error");
return false;
}
}
delete(key) {
if (!key) {
this.messageLog("A key is required for the delete() function!", "error");
return false;
}
try {
this.checkFileSync();
const data = this.readFileSync();
if (data.hasOwnProperty(key)) {
delete data[key];
this.writeFileSync(data);
return true;
} else {
this.messageLog(`"${key}" is an invalid key.`, "warning");
return false;
}
} catch (e) {
this.messageLog(`Error during delete(): ${e.message}`, "error");
return false;
}
}
deleteAll() {
try {
this.checkFileSync();
const emptyData = BSON.serialize({});
fs.writeFileSync(this.file, emptyData);
return true;
} catch (e) {
this.messageLog(`Error during deleteAll(): ${e.message}`, "error");
return false;
}
}
unpush(key, element) {
try {
this.checkFileSync();
const data = this.readFileSync();
if (!data[key]) {
this.messageLog(`Key "${key}" not found.`, "warning");
return undefined;
}
if (!Array.isArray(data[key])) {
this.messageLog(`"${key}" is not an array: ${data[key]}`, "error");
return false;
}
if (Array.isArray(element)) {
element.forEach(el => {
data[key] = data[key].filter(item => item !== el);
});
} else {
data[key] = data[key].filter(item => item !== element);
}
this.writeFileSync(data);
return data[key];
} catch (e) {
this.messageLog(`Error during unpush(): ${e.message}`, "error");
return false;
}
}
unpushByIndex(key, index) {
try {
this.checkFileSync();
const data = this.readFileSync();
if (!data[key]) {
this.messageLog(`Key "${key}" not found.`, "warning");
return undefined;
}
if (!Array.isArray(data[key])) {
this.messageLog(`"${key}" is not an array: ${data[key]}`, "error");
return false;
}
if (index < 0 || index >= data[key].length || isNaN(index)) {
this.messageLog(`Invalid index number: ${index}`, "warning");
return false;
}
data[key].splice(index, 1);
this.writeFileSync(data);
return data[key];
} catch (e) {
this.messageLog(`Error during unpushByIndex(): ${e.message}`, "error");
return false;
}
}
}
function createInstance(file, filePath) {
const instance = new Database(file, filePath);
instance.checkPackageVersion();
return {
set: instance.set.bind(instance),
delete: instance.delete.bind(instance),
deleteAll: instance.deleteAll.bind(instance),
get: instance.get.bind(instance),
push: instance.push.bind(instance),
unpush: instance.unpush.bind(instance),
unpushByIndex: instance.unpushByIndex.bind(instance),
all: instance.all.bind(instance)
};
}
module.exports = createInstance;