I am working on a application that is made completely in JavaScript (other than the database) and I've created a simple user management system for the application.
This user management system creates a query to the database containing the user info (the database contains the user info not the query) based on a few command line arguments, decrypts the data in the database, and then prints the result to the console.
I have been programming for 2 years but am self-taught and was looking for some feedback/criticism on my code.
Note: Some of the modules in the Main script are not included but, for the most part, their functions should be intuitive.
Edit: for more information about the DataBase
class see this question.
Here is my code:
Main script:
// this is a instance of a `DataBase` class:
const users = require('../../Code/MiddleWare/Database-comms/createUser').users
const lookup = require('./lookup')
const decryptAll = require('./decrypt')
void async function() {
if(process.argv[2] === '-e') {
console.dir(decryptAll(await lookup('Email', process.argv[3], users, process.argv[4] === '-ex')), {colors: true, depth: Infinity})
} else if(process.argv[2] ? process.argv[2].match(/^\d+$/) : false) {
console.dir(decryptAll(await lookup('ID', parseInt(process.argv[2]), users, process.argv[3] === '-ex', false)), {colors: true, depth: Infinity})
} else {
console.dir(decryptAll(await lookup('Username', process.argv[2] ? process.argv[2] : '', users, process.argv[3] === '-ex')), {colors: true, depth: Infinity})
}
}()
"lookup" module:
module.exports = async function(type, string, users, exact, encrypted = true) {
const aes = require('../../Code/MiddleWare/Security/index').encryption.AES
const retval = []
await users.query('Users').then((data) => {
for(let i of data) {
if(encrypted ? (exact? aes.decrypt(i[type]) === string : aes.decrypt(i[type]).match(string)) : i[type] === string) {
retval.push(i)
}
}
})
return retval.length === 0 ? false : retval
}
The "decrypt" module:
const decrypt = require('../../Code/MiddleWare/Security/encryption').AES.decrypt
module.exports = function(array) {
if(!array) {
return false
}
const retval = []
function decryptAll(obj) {
return {ID: obj.ID, Username: decrypt(obj.Username), Email: decrypt(obj.Email), Data: decrypt(obj.Data), Password: obj.Password}
}
for(const i of array) {
const item = decryptAll(i)
item.Data = JSON.parse(item.Data)
retval.push(item)
}
return retval
}
1 Answer 1
Main script
- Consider using the
commander
module. One benefit you get is that it parses arguments for you. Another is that the API that declares the arguments and options autogenerates--help
text for you. This way, you avoid having to write all the argument parsing yourself and you get a self-documenting CLI.
Lookup module
Move that
require
call outside the function. You only need to importaes
once, not on every call to the lookup function.When inside an
async
function, you can useawait
to write async operations in a synchronous-looking fashion. When you useawait
, you can assign the resolved value of an async operation to a variable in the same way you do it with synchronous operation.The code inside the
for-of
is essentially a filter operation. Consider usingarray.filter()
instead, assumingdata
is just an array.I also recommend breaking out that nested ternary into nested
if
statements for readability. You could arrange the conditions so that it's cascading rather than nested.I recommend returning an empty array instead of
false
when no matches are found. This way, the consuming code won't have to deal with type checking.
Decrypt module
The
decryptAll
function does not appear to rely on any variables in the scope of the exported function. You can movedecryptAll
out of that function.That
for-of
is essentially a mapping function (transforming one array's values into another array of values). Usearray.map()
instead.Since
decryptAll
is just mapping select properties into a new object, you can use a combination of destructuring and shorthand properties to simplify it.
Bottom line, your code could look like this:
Lookup
const aes = require('../../Code/MiddleWare/Security/index').encryption.AES
module.exports = async function(type, string, users, exact, encrypted = true) {
return await users.query('Users').filter(i => {
// Technically, the encrypted block is a nested ternary in the
// false portion of the first ternary. But written this way,
// it looks like a flat list.
return !encrypted ? i[type] === string
: exact ? aes.decrypt(i[type]) === string
: aes.decrypt(i[type]).match(string)
})
}
Decrypt
const decrypt = require('../../Code/MiddleWare/Security/encryption').AES.decrypt
// Destructure arguments, then return an object with shorthand properties.
// Eliminates obj.
const decryptAll = ({ ID, Username, Email, Data, Password }) => {
return { ID, Username, Email, Data, Password }
}
module.exports = function(array) {
// We're expecting an array so the check is no longer needed.
const retval = array.map(i => {
const item = decryptAll(i)
const itemWithDecryptedData = { ...item, Data: JSON.parse(item.Data) }
return itemWithDecryptedData
})
return retval
}