4
\$\begingroup\$

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
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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 import aes once, not on every call to the lookup function.

  • When inside an async function, you can use await to write async operations in a synchronous-looking fashion. When you use await, 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 using array.filter() instead, assuming data 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 move decryptAll out of that function.

  • That for-of is essentially a mapping function (transforming one array's values into another array of values). Use array.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
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.