Comparing version 1.1.2 to 1.2.0
243
index.js
@@ -0,6 +1,11 @@ | ||
// * Dependencies | ||
const fs = require("fs").promises; | ||
const path = require("path"); | ||
const { randomUUID } = require("crypto"); | ||
const readline = require('readline'); | ||
const colors = require('./lib/colors'); | ||
// const winston = require("winston"); | ||
const csv = require("csv-parser"); | ||
const crypto = require("crypto-js"); | ||
const dateFormat = require("dateformat"); | ||
const Fuse = require("fuse.js"); | ||
const colors = require("./lib/colors") | ||
@@ -10,5 +15,100 @@ class jsonverse { | ||
this.dataFolderPath = dataFolderPath; | ||
this.init() | ||
this.init(); | ||
this.searchIndex = {}; | ||
} | ||
// Encrypt sensitive data | ||
encryptData(data, secretKey) { | ||
const encryptedData = crypto.AES.encrypt( | ||
JSON.stringify(data), | ||
secretKey | ||
).toString(); | ||
return encryptedData; | ||
} | ||
// Decrypt sensitive data | ||
decryptData(encryptedData, secretKey) { | ||
const decryptedData = crypto.AES.decrypt(encryptedData, secretKey).toString( | ||
crypto.enc.Utf8 | ||
); | ||
return JSON.parse(decryptedData); | ||
} | ||
// Create Data Backup | ||
async createDataBackup(dataName) { | ||
const currentData = await this.readDataFromFile(dataName); | ||
if (currentData !== null) { | ||
const backupFileName = `${dataName}_${dateFormat( | ||
new Date(), | ||
"yyyymmddHHMMss" | ||
)}.json`; | ||
const backupFilePath = path.join(this.dataFolderPath, backupFileName); | ||
await fs.writeFile( | ||
backupFilePath, | ||
JSON.stringify(currentData, null, 2), | ||
"utf8" | ||
); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data backup created: ${backupFileName}`); | ||
} else { | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Failed to create data backup for ${dataName}`); | ||
} | ||
} | ||
// Restore Data from Backup | ||
async restoreDataFromBackup(dataName, backupFileName) { | ||
const backupFilePath = path.join(this.dataFolderPath, backupFileName); | ||
try { | ||
const backupData = await fs.readFile(backupFilePath, "utf8"); | ||
await this.writeDataByFileName(dataName, JSON.parse(backupData)); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data restored from backup: ${backupFileName}`); | ||
} catch (error) { | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Failed to restore data from backup: ${backupFileName}`); | ||
} | ||
} | ||
// Cleanup Old Backups | ||
async cleanupOldBackups(dataName, retentionDays) { | ||
const backupFiles = await this.getBackupFiles(dataName); | ||
const currentDate = new Date(); | ||
for (const backupFile of backupFiles) { | ||
const backupDateStr = backupFile.split("_")[1].split(".")[0]; | ||
const backupDate = dateFormat(backupDateStr, "yyyymmddHHMMss"); | ||
const diffInDays = | ||
(currentDate - new Date(backupDate)) / (1000 * 60 * 60 * 24); | ||
if (diffInDays > retentionDays) { | ||
const backupFilePath = path.join(this.dataFolderPath, backupFile); | ||
await fs.unlink(backupFilePath); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Backup deleted: ${backupFile}`); | ||
} | ||
} | ||
} | ||
// Get Backup Files | ||
async getBackupFiles(dataName) { | ||
const files = await fs.readdir(this.dataFolderPath); | ||
const backupFiles = files.filter((file) => file.startsWith(`${dataName}_`)); | ||
return backupFiles; | ||
} | ||
// Initialize search index for a dataName | ||
initSearchIndex(dataName, options) { | ||
const data = this.readDataFromFile(dataName); | ||
if (data !== null) { | ||
const fuse = new Fuse(data, options); | ||
this.searchIndex[dataName] = fuse; | ||
} | ||
} | ||
// Search data by query | ||
searchData(dataName, query) { | ||
if (this.searchIndex[dataName]) { | ||
const fuse = this.searchIndex[dataName]; | ||
return fuse.search(query); | ||
} else { | ||
return []; | ||
} | ||
} | ||
async init() { | ||
@@ -18,6 +118,5 @@ try { | ||
} catch (error) { | ||
console.log(`\x1b[34m[Info]: \x1b[0mThe path "${this.dataFolderPath}" doesn't exist.`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}The path "${this.dataFolderPath}" doesn't exist.`); | ||
const answer = await this.askForConfirmation( | ||
`\x1b[33m[Question]: \x1b[0mDo you want to create the path folder? (Y/N): ` | ||
`${colors.fg.yellow}[Question]: ${colors.reset}Do you want to create the path folder? (Y/N): ` | ||
); | ||
@@ -28,22 +127,21 @@ | ||
await fs.mkdir(this.dataFolderPath, { recursive: true }); | ||
console.log(`\x1b[32m[Successful]: \x1b[0mPath folder created successfully.`); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Path folder created successfully.`); | ||
} catch (error) { | ||
console.error(`\x1b[31m[Error]: \x1b[0mCreating path folder: ${error}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Creating path folder: ${error}`); | ||
} | ||
} else { | ||
console.log(`\x1b[34m[Info]: \x1b[0mPath folder not created.`); | ||
console.log(`${colors.fg.blue}[Info]: ${colors.reset}Path folder not created.`); | ||
} | ||
} | ||
return this.dataFolderPath; // Return dataFolderPath after initialization | ||
return this.dataFolderPath; | ||
} | ||
async initFile(filePath) { | ||
async initFile(filePath) { | ||
try { | ||
await fs.access(filePath); | ||
} catch (error) { | ||
console.log(`\x1b[34m[Info]: \x1b[0mThe file "${filePath}" doesn't exist.`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}The file "${filePath}" doesn't exist.`); | ||
const answer = await this.askForConfirmation( | ||
`\x1b[33m[Question]: \x1b[0mDo you want to create the file? (Y/N): ` | ||
`${colors.fg.yellow}[Question]: ${colors.reset}Do you want to create the file? (Y/N): ` | ||
); | ||
@@ -54,8 +152,8 @@ | ||
await fs.writeFile(filePath, "[]"); | ||
console.log(`\x1b[32m[Successful]: \x1b[0mFile created successfully.`); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}File created successfully.`); | ||
} catch (error) { | ||
console.error(`\x1b[31m[Error]: \x1b[0mCreating file: ${error}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Creating file: ${error}`); | ||
} | ||
} else { | ||
console.log(`\x1b[34m[Info]: \x1b[0mFile not created.`); | ||
console.log(`${colors.fg.blue}[Info]: ${colors.reset}File not created.`); | ||
} | ||
@@ -85,6 +183,71 @@ } | ||
// Export Data | ||
async exportData(dataName, format = "json") { | ||
const data = await this.readDataFromFile(dataName); | ||
if (format === "json") { | ||
// Export as JSON | ||
const filePath = this.getFilePath(dataName + ".json"); | ||
await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8"); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data exported to JSON: ${filePath}`); | ||
} else if (format === "csv") { | ||
// Export as CSV | ||
const filePath = this.getFilePath(dataName + ".csv"); | ||
const csvData = data.map((item) => { | ||
return { | ||
id: item.id, | ||
...item, // Include other properties in CSV | ||
}; | ||
}); | ||
const csvString = await this.convertToCSV(csvData); | ||
await fs.writeFile(filePath, csvString, "utf8"); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data exported to CSV: ${filePath}`); | ||
} | ||
} | ||
// Import Data | ||
async importData(dataName, format = "json", filePath) { | ||
if (format === "json") { | ||
// Import JSON | ||
const rawData = await fs.readFile(filePath, "utf8"); | ||
const newData = JSON.parse(rawData); | ||
await this.writeDataByFileName(dataName, newData); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data imported from JSON: ${filePath}`); | ||
} else if (format === "csv") { | ||
// Import CSV | ||
const csvData = await this.readCSV(filePath); | ||
await this.writeDataByFileName(dataName, csvData); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data imported from CSV: ${filePath}`); | ||
} | ||
} | ||
// Data Transformation (if needed) | ||
async transformData(dataName, transformFunction) { | ||
const data = await this.readDataFromFile(dataName); | ||
const transformedData = data.map(transformFunction); | ||
await this.writeDataByFileName(dataName, transformedData); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Data transformed and saved.`); | ||
} | ||
// Helper method to convert data to CSV | ||
async convertToCSV(data) { | ||
const { Parser } = require("json2csv"); | ||
const parser = new Parser(); | ||
return parser.parse(data); | ||
} | ||
// Helper method to read CSV | ||
async readCSV(filePath) { | ||
const results = []; | ||
const stream = fs.createReadStream(filePath).pipe(csv()); | ||
return new Promise((resolve, reject) => { | ||
stream | ||
.on("data", (data) => results.push(data)) | ||
.on("end", () => resolve(results)) | ||
.on("error", (error) => reject(error)); | ||
}); | ||
} | ||
getFilePath(dataName) { | ||
const filePath = path.join(this.dataFolderPath, `${dataName}.json`); | ||
// this.initFile(filePath); // Initialize the file if it doesn't exist | ||
return filePath; | ||
return path.join(this.dataFolderPath, `${dataName}.json`); | ||
} | ||
@@ -101,5 +264,5 @@ | ||
} catch (error) { | ||
if (error.code === 'ENOENT') { | ||
await this.initFile(filePath).catch(initError => { | ||
console.error(`\x1b[31m[Error]: \x1b[0mInitializing file: ${initError}`); | ||
if (error.code === "ENOENT") { | ||
await this.initFile(filePath).catch((initError) => { | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Initializing file: ${initError}`); | ||
}); | ||
@@ -114,7 +277,7 @@ // Retry reading the file | ||
} catch (readError) { | ||
console.error(`\x1b[31m[Error]: \x1b[0mreading file ${filePath}: ${readError}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Reading file ${filePath}: ${readError}`); | ||
return null; | ||
} | ||
} else { | ||
console.error(`\x1b[31m[Error]: \x1b[0mreading file ${filePath}: ${error}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Reading file ${filePath}: ${error}`); | ||
return null; | ||
@@ -146,3 +309,3 @@ } | ||
} catch (error) { | ||
console.error(`\x1b[31m[Error]: \x1b[0mwriting to file ${filePath}: ${error}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Writing to file ${filePath}: ${error}`); | ||
} | ||
@@ -156,3 +319,3 @@ } | ||
} catch (error) { | ||
console.error(`\x1b[31m[Error]: \x1b[0mwriting to item with ID: ${filePath}: ${error}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Writing to item with ID: ${filePath}: ${error}`); | ||
} | ||
@@ -177,5 +340,5 @@ } | ||
await this.writeDataById(id, existingData); | ||
console.log(`\x1b[32m[Successful]: \x1b[0mItem with ID ${id} has been edited.`); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Item with ID ${id} has been edited.`); | ||
} else { | ||
console.log(`\x1b[34m[Info]: \x1b[0mItem with ID ${id} not found.`); | ||
console.log(`${colors.fg.blue}[Info]: ${colors.reset}Item with ID ${id} not found.`); | ||
} | ||
@@ -189,6 +352,4 @@ } | ||
if (existingData !== null) { | ||
// Generate a random and unique ID | ||
const newId = await this.randomID(); | ||
// Create a new object with the ID and newData | ||
const newDataWithId = { | ||
@@ -199,10 +360,9 @@ id: newId, | ||
// Insert the newData object with ID at the beginning of the existing data array | ||
existingData.unshift(newDataWithId); | ||
await this.writeDataByFileName(dataName, existingData).then(() => { | ||
console.log(`\x1b[32m[Successful]: \x1b[0mNew Data added to DB: ${dataName}`) | ||
}) | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}New Data added to DB: ${dataName}`); | ||
}); | ||
} else { | ||
console.error(`\x1b[31m[Error]: \x1b[0m Data failed to be added to the DB: ${dataName}`); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Data failed to be added to the DB: ${dataName}`); | ||
} | ||
@@ -222,13 +382,13 @@ } | ||
console.log(`\x1b[32m[Successful]: \x1b[0mItem has been deleted.`); | ||
console.log(`\x1b[32m[Successful]: \x1b[0mDeleted item:`, dataItemToDelete); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Item has been deleted.`); | ||
console.log(`${colors.fg.blue}[Info]: ${colors.reset}Deleted item:`, dataItemToDelete); | ||
if (typeof window == "undefined") { | ||
console.log("\x1b[32m[Successful]: \x1b[0mItem Deleted successfully!"); | ||
console.log(`${colors.fg.green}[Successful]: ${colors.reset}Item Deleted successfully!`); | ||
} | ||
} else { | ||
console.log(`\x1b[34m[Info]: \x1b[0mItem is already deleted.`); | ||
console.log(`${colors.fg.blue}[Info]: ${colors.reset}Item is already deleted.`); | ||
} | ||
} catch (error) { | ||
console.error("\x1b[31m[Error]: \x1b[0m deleting data:", error); | ||
console.log(`${colors.fg.red}[Error]: ${colors.reset}Deleting data:`, error); | ||
} | ||
@@ -239,3 +399,2 @@ } | ||
const files = await fs.readdir(this.dataFolderPath); | ||
const allData = []; | ||
@@ -245,3 +404,3 @@ | ||
if (file.endsWith(".json")) { | ||
const dataName = file.slice(6, -5); | ||
const dataName = file.slice(0, -5); | ||
const data = await this.readDataFromFile(dataName); | ||
@@ -248,0 +407,0 @@ |
{ | ||
"name": "jsonverse", | ||
"version": "1.1.2", | ||
"version": "1.2.0", | ||
"description": "jsonVerse is a lightweight JSON-based database package for Node.js. It provides a simple interface to store, retrieve, and manage data using JSON files.", | ||
"main": "index.js", | ||
"publishConfig": { | ||
"@marco5dev:registry": "https://npm.pkg.github.com" | ||
}, | ||
"scripts": { | ||
@@ -21,2 +18,9 @@ "test": "exit 0" | ||
}, | ||
"files": [ | ||
"LICENSE", | ||
"README.md", | ||
"index.js", | ||
"lib/", | ||
"SECURITY.md" | ||
], | ||
"homepage": "https://github.com/Marco5dev/jsonverse#readme", | ||
@@ -61,3 +65,6 @@ "keywords": [ | ||
"db." | ||
] | ||
], | ||
"dependencies": { | ||
"winston": "^3.10.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
0
384
20985
1
+ Addedwinston@^3.10.0
+ Added@colors/colors@1.6.0(transitive)
+ Added@dabh/diagnostics@2.0.3(transitive)
+ Added@types/triple-beam@1.3.5(transitive)
+ Addedasync@3.2.6(transitive)
+ Addedcolor@3.2.1(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedcolor-string@1.9.1(transitive)
+ Addedcolorspace@1.1.4(transitive)
+ Addedenabled@2.0.0(transitive)
+ Addedfecha@4.2.3(transitive)
+ Addedfn.name@1.1.0(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-arrayish@0.3.2(transitive)
+ Addedis-stream@2.0.1(transitive)
+ Addedkuler@2.0.0(transitive)
+ Addedlogform@2.7.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addedone-time@1.0.0(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsafe-stable-stringify@2.5.0(transitive)
+ Addedsimple-swizzle@0.2.2(transitive)
+ Addedstack-trace@0.0.10(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedtext-hex@1.0.0(transitive)
+ Addedtriple-beam@1.4.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwinston@3.17.0(transitive)
+ Addedwinston-transport@4.9.0(transitive)