diff --git a/package-lock.json b/package-lock.json
index 952da7a..f0b8a54 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
       "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
       "requires": {
-        "mime-types": "~2.1.18",
+        "mime-types": "2.1.21",
         "negotiator": "0.6.1"
       }
     },
@@ -18,21 +18,37 @@
       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
       "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
     },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
     "body-parser": {
       "version": "1.18.3",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
       "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
       "requires": {
         "bytes": "3.0.0",
-        "content-type": "~1.0.4",
+        "content-type": "1.0.4",
         "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "http-errors": "~1.6.3",
+        "depd": "1.1.2",
+        "http-errors": "1.6.3",
         "iconv-lite": "0.4.23",
-        "on-finished": "~2.3.0",
+        "on-finished": "2.3.0",
         "qs": "6.5.2",
         "raw-body": "2.3.3",
-        "type-is": "~1.6.16"
+        "type-is": "1.6.16"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "1.0.0",
+        "concat-map": "0.0.1"
       }
     },
     "buffer-equal-constant-time": {
@@ -45,6 +61,31 @@
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
       "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
     },
+    "cli": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+      "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+      "dev": true,
+      "requires": {
+        "exit": "0.1.2",
+        "glob": "7.1.6"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "console-browserify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+      "dev": true,
+      "requires": {
+        "date-now": "0.1.4"
+      }
+    },
     "content-disposition": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@@ -65,6 +106,18 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
     },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
+    },
+    "date-now": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+      "dev": true
+    },
     "debug": {
       "version": "2.6.9",
       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -83,12 +136,61 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "dom-serializer": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+      "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "2.0.1",
+        "entities": "2.0.0"
+      },
+      "dependencies": {
+        "domelementtype": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
+          "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
+          "dev": true
+        },
+        "entities": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+          "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
+          "dev": true
+        }
+      }
+    },
+    "domelementtype": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+      "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+      "dev": true
+    },
+    "domhandler": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+      "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+      "dev": true,
+      "requires": {
+        "domelementtype": "1.3.1"
+      }
+    },
+    "domutils": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+      "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+      "dev": true,
+      "requires": {
+        "dom-serializer": "0.2.2",
+        "domelementtype": "1.3.1"
+      }
+    },
     "ecdsa-sig-formatter": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz",
       "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=",
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "ee-first": {
@@ -101,6 +203,12 @@
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
       "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
     },
+    "entities": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+      "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+      "dev": true
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -111,41 +219,47 @@
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
       "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
     },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+      "dev": true
+    },
     "express": {
       "version": "4.16.4",
       "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
       "integrity": "sha1-/d72GSYQniTFFeqX/S8b2/Yt8S4=",
       "requires": {
-        "accepts": "~1.3.5",
+        "accepts": "1.3.5",
         "array-flatten": "1.1.1",
         "body-parser": "1.18.3",
         "content-disposition": "0.5.2",
-        "content-type": "~1.0.4",
+        "content-type": "1.0.4",
         "cookie": "0.3.1",
         "cookie-signature": "1.0.6",
         "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
+        "depd": "1.1.2",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "etag": "1.8.1",
         "finalhandler": "1.1.1",
         "fresh": "0.5.2",
         "merge-descriptors": "1.0.1",
-        "methods": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.2",
+        "methods": "1.1.2",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
         "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.4",
+        "proxy-addr": "2.0.4",
         "qs": "6.5.2",
-        "range-parser": "~1.2.0",
+        "range-parser": "1.2.0",
         "safe-buffer": "5.1.2",
         "send": "0.16.2",
         "serve-static": "1.13.2",
         "setprototypeof": "1.1.0",
-        "statuses": "~1.4.0",
-        "type-is": "~1.6.16",
+        "statuses": "1.4.0",
+        "type-is": "1.6.16",
         "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
+        "vary": "1.1.2"
       },
       "dependencies": {
         "statuses": {
@@ -161,12 +275,12 @@
       "integrity": "sha1-7r9O2EAHnIP0JJA4ydcDAIMBsQU=",
       "requires": {
         "debug": "2.6.9",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.2",
-        "statuses": "~1.4.0",
-        "unpipe": "~1.0.0"
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
+        "statuses": "1.4.0",
+        "unpipe": "1.0.0"
       },
       "dependencies": {
         "statuses": {
@@ -186,15 +300,48 @@
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
       "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
     },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "1.0.0",
+        "inflight": "1.0.6",
+        "inherits": "2.0.3",
+        "minimatch": "3.0.4",
+        "once": "1.4.0",
+        "path-is-absolute": "1.0.1"
+      }
+    },
+    "htmlparser2": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+      "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+      "dev": true,
+      "requires": {
+        "domelementtype": "1.3.1",
+        "domhandler": "2.3.0",
+        "domutils": "1.5.1",
+        "entities": "1.0.0",
+        "readable-stream": "1.1.14"
+      }
+    },
     "http-errors": {
       "version": "1.6.3",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
       "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
       "requires": {
-        "depd": "~1.1.2",
+        "depd": "1.1.2",
         "inherits": "2.0.3",
         "setprototypeof": "1.1.0",
-        "statuses": ">= 1.4.0 < 2"
+        "statuses": "1.5.0"
       }
     },
     "iconv-lite": {
@@ -202,7 +349,17 @@
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
       "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=",
       "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
+        "safer-buffer": "2.1.2"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
       }
     },
     "inherits": {
@@ -215,20 +372,42 @@
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
       "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
     },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+      "dev": true
+    },
+    "jshint": {
+      "version": "2.10.3",
+      "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.3.tgz",
+      "integrity": "sha512-d8AoXcNNYzmm7cdmulQ3dQApbrPYArtVBO6n4xOICe4QsXGNHCAKDcFORzqP52LhK61KX0VhY39yYzCsNq+bxQ==",
+      "dev": true,
+      "requires": {
+        "cli": "1.0.1",
+        "console-browserify": "1.1.0",
+        "exit": "0.1.2",
+        "htmlparser2": "3.8.3",
+        "lodash": "4.17.15",
+        "minimatch": "3.0.4",
+        "shelljs": "0.3.0",
+        "strip-json-comments": "1.0.4"
+      }
+    },
     "jsonwebtoken": {
       "version": "8.4.0",
       "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz",
       "integrity": "sha1-h1f3tMt0QNhtXi877O+nBTbI5Go=",
       "requires": {
-        "jws": "^3.1.5",
-        "lodash.includes": "^4.3.0",
-        "lodash.isboolean": "^3.0.3",
-        "lodash.isinteger": "^4.0.4",
-        "lodash.isnumber": "^3.0.3",
-        "lodash.isplainobject": "^4.0.6",
-        "lodash.isstring": "^4.0.1",
-        "lodash.once": "^4.0.0",
-        "ms": "^2.1.1"
+        "jws": "3.1.5",
+        "lodash.includes": "4.3.0",
+        "lodash.isboolean": "3.0.3",
+        "lodash.isinteger": "4.0.4",
+        "lodash.isnumber": "3.0.3",
+        "lodash.isplainobject": "4.0.6",
+        "lodash.isstring": "4.0.1",
+        "lodash.once": "4.1.1",
+        "ms": "2.1.1"
       },
       "dependencies": {
         "ms": {
@@ -245,7 +424,7 @@
       "requires": {
         "buffer-equal-constant-time": "1.0.1",
         "ecdsa-sig-formatter": "1.0.10",
-        "safe-buffer": "^5.0.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "jws": {
@@ -253,10 +432,16 @@
       "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz",
       "integrity": "sha1-gNEtBbKT0ehB58uLTmnlYa3Pg08=",
       "requires": {
-        "jwa": "^1.1.5",
-        "safe-buffer": "^5.0.1"
+        "jwa": "1.1.6",
+        "safe-buffer": "5.1.2"
       }
     },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+      "dev": true
+    },
     "lodash.includes": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -322,7 +507,16 @@
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
       "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=",
       "requires": {
-        "mime-db": "~1.37.0"
+        "mime-db": "1.37.0"
+      }
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "1.1.11"
       }
     },
     "ms": {
@@ -343,11 +537,26 @@
         "ee-first": "1.1.1"
       }
     },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1.0.2"
+      }
+    },
     "parseurl": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
       "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
     },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
     "path-to-regexp": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
@@ -358,7 +567,7 @@
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
       "integrity": "sha1-7PxzO/Iv+Mb0B/onUye5q2fki5M=",
       "requires": {
-        "forwarded": "~0.1.2",
+        "forwarded": "0.1.2",
         "ipaddr.js": "1.8.0"
       }
     },
@@ -383,6 +592,18 @@
         "unpipe": "1.0.0"
       }
     },
+    "readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+      "dev": true,
+      "requires": {
+        "core-util-is": "1.0.2",
+        "inherits": "2.0.3",
+        "isarray": "0.0.1",
+        "string_decoder": "0.10.31"
+      }
+    },
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -399,18 +620,18 @@
       "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=",
       "requires": {
         "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "destroy": "~1.0.4",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
+        "depd": "1.1.2",
+        "destroy": "1.0.4",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "etag": "1.8.1",
         "fresh": "0.5.2",
-        "http-errors": "~1.6.2",
+        "http-errors": "1.6.3",
         "mime": "1.4.1",
         "ms": "2.0.0",
-        "on-finished": "~2.3.0",
-        "range-parser": "~1.2.0",
-        "statuses": "~1.4.0"
+        "on-finished": "2.3.0",
+        "range-parser": "1.2.0",
+        "statuses": "1.4.0"
       },
       "dependencies": {
         "statuses": {
@@ -425,9 +646,9 @@
       "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
       "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=",
       "requires": {
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.2",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "parseurl": "1.3.2",
         "send": "0.16.2"
       }
     },
@@ -436,18 +657,36 @@
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
       "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY="
     },
+    "shelljs": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+      "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
+      "dev": true
+    },
     "statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
       "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
     },
+    "string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+      "dev": true
+    },
+    "strip-json-comments": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+      "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+      "dev": true
+    },
     "type-is": {
       "version": "1.6.16",
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
       "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
       "requires": {
         "media-typer": "0.3.0",
-        "mime-types": "~2.1.18"
+        "mime-types": "2.1.21"
       }
     },
     "unpipe": {
@@ -464,6 +703,12 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index 1fa65c2..78c9c38 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "description": "",
   "main": "index.js",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "jshint src/"
   },
   "author": "",
   "license": "ISC",
@@ -12,5 +12,8 @@
     "body-parser": "^1.18.3",
     "express": "^4.16.3",
     "jsonwebtoken": "^8.4.0"
+  },
+  "devDependencies": {
+    "jshint": "^2.10.3"
   }
 }
diff --git a/public/js/.jshintignore b/public/js/.jshintignore
new file mode 100644
index 0000000..d4ea404
--- /dev/null
+++ b/public/js/.jshintignore
@@ -0,0 +1,2 @@
+bundle.js
+lib/*
diff --git a/public/js/.jshintrc b/public/js/.jshintrc
new file mode 100644
index 0000000..7ac1881
--- /dev/null
+++ b/public/js/.jshintrc
@@ -0,0 +1,10 @@
+{
+  "undef": true,
+  "unused": true,
+  "esversion": 6,
+  "browser": true,
+  "globals": {
+    "L": true,
+    "m": true
+  }
+}
diff --git a/src/.jshintrc b/src/.jshintrc
new file mode 100644
index 0000000..2247609
--- /dev/null
+++ b/src/.jshintrc
@@ -0,0 +1,6 @@
+{
+  "undef": true,
+  "unused": true,
+  "esversion": 6,
+  "node": true
+}
diff --git a/src/api/inbox.js b/src/api/inbox.js
index db072d5..6405420 100644
--- a/src/api/inbox.js
+++ b/src/api/inbox.js
@@ -2,9 +2,6 @@
 const app = require("../app");
 const tokencheck = require("./tokencheck");
 
-const bodyParser = require('body-parser');
-const jsonParser = bodyParser.json();
-
 const playermessages = require("../promise/playermessages");
 const deletemessage = require("../promise/deletemessage");
 
@@ -21,7 +18,7 @@ app.get('/api/inbox', function(req, res){
 				})
 			);
 		})
-		.catch(e => res.status(500).end());
+		.catch(() => res.status(500).end());
 	}
 
 });
diff --git a/src/api/login.js b/src/api/login.js
index 45aa220..29bd36c 100644
--- a/src/api/login.js
+++ b/src/api/login.js
@@ -5,13 +5,13 @@ const app = require("../app");
 const doLogin = require("../promise/login");
 const token = require("../token");
 
-const bodyParser = require('body-parser')
-const jsonParser = bodyParser.json()
+const bodyParser = require('body-parser');
+const jsonParser = bodyParser.json();
 
 app.post('/api/login', jsonParser, function(req, res){
 
 	if (!req.body.username || !req.body.password){
-		res.status(500).end()
+		res.status(500).end();
 		return;
 	}
 
@@ -31,10 +31,9 @@ app.post('/api/login', jsonParser, function(req, res){
 			token: t
 		});
 	})
-	.catch(e => {
+	.catch(() => {
 		res.status(500).end();
 	});
 
 
 });
-
diff --git a/src/api/minetest/channel.js b/src/api/minetest/channel.js
index a18f617..afb5ae7 100644
--- a/src/api/minetest/channel.js
+++ b/src/api/minetest/channel.js
@@ -3,8 +3,8 @@ const app = require("../../app");
 const events = require("../../events");
 const keycheck = require("./keycheck");
 
-const bodyParser = require('body-parser')
-const jsonParser = bodyParser.json({ limit: '10mb' })
+const bodyParser = require('body-parser');
+const jsonParser = bodyParser.json({ limit: '10mb' });
 
 const debug = process.env.WEBMAIL_DEBUG;
 
diff --git a/src/api/minetest/keycheck.js b/src/api/minetest/keycheck.js
index d9feeda..08880cd 100644
--- a/src/api/minetest/keycheck.js
+++ b/src/api/minetest/keycheck.js
@@ -1,11 +1,11 @@
 
 module.exports = function(req, res){
 
-	if (!req.headers["webmailkey"] || req.headers["webmailkey"] != process.env.WEBMAILKEY){
-		console.error("Invalid key: " + req.headers["webmailkey"]);
+	if (!req.headers.webmailkey || req.headers.webmailkey != process.env.WEBMAILKEY){
+		console.error("Invalid key: " + req.headers.webmailkey);
 		res.status(403).end();
 		return false;
 	}
 
 	return true;
-}
+};
diff --git a/src/api/tokencheck.js b/src/api/tokencheck.js
index 099ecd9..b7e6834 100644
--- a/src/api/tokencheck.js
+++ b/src/api/tokencheck.js
@@ -3,7 +3,7 @@ const token = require("../token");
 
 module.exports = function(req, res){
 
-	var auth = req.headers["authorization"];
+	var auth = req.headers.authorization;
 	var payload = token.verify(auth);
 
 	if (auth && payload){
@@ -12,4 +12,4 @@ module.exports = function(req, res){
 
 	res.status(403).end();
 	return false;
-}
+};
diff --git a/src/app.js b/src/app.js
index c3ef3d4..dc060dc 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,6 +1,6 @@
-const express = require('express')
+const express = require('express');
+const app = express();
 
-const app = express()
 app.use(express.static('public'));
 app.disable('etag');
 
diff --git a/src/index.js b/src/index.js
index b8f0a98..5e0195d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -7,5 +7,4 @@ if (!process.env.WEBMAILKEY){
 const app = require("./app");
 require("./api");
 
-app.listen(8080, () => console.log('Listening on http://127.0.0.1:8080'))
-
+app.listen(8080, () => console.log('Listening on http://127.0.0.1:8080'));
diff --git a/src/promise/deletemessage.js b/src/promise/deletemessage.js
index 2b1a95a..4f055a3 100644
--- a/src/promise/deletemessage.js
+++ b/src/promise/deletemessage.js
@@ -2,7 +2,7 @@
 
 const events = require("../events");
 
-module.exports = (playername, index) => new Promise(function(resolve, reject){
+module.exports = (playername, index) => new Promise(function(resolve){
 
 	events.emit("channel-send", {
 		type: "delete-mail",
@@ -11,4 +11,4 @@ module.exports = (playername, index) => new Promise(function(resolve, reject){
 	});
 
 	resolve();
-});
\ No newline at end of file
+});
diff --git a/src/promise/markmessage.js b/src/promise/markmessage.js
index 4c2bd54..d31bc08 100644
--- a/src/promise/markmessage.js
+++ b/src/promise/markmessage.js
@@ -2,7 +2,7 @@
 
 const events = require("../events");
 
-module.exports = (playername, index) => new Promise(function(resolve, reject){
+module.exports = (playername, index) => new Promise(function(resolve){
 
 	events.emit("channel-send", {
 		type: "mark-mail-read",
@@ -11,4 +11,4 @@ module.exports = (playername, index) => new Promise(function(resolve, reject){
 	});
 
 	resolve();
-});
\ No newline at end of file
+});
diff --git a/src/promise/sendmessage.js b/src/promise/sendmessage.js
index a1808c0..2f0572a 100644
--- a/src/promise/sendmessage.js
+++ b/src/promise/sendmessage.js
@@ -2,7 +2,7 @@
 
 const events = require("../events");
 
-module.exports = (playername, recipient, subject, text) => new Promise(function(resolve, reject){
+module.exports = (playername, recipient, subject, text) => new Promise(function(resolve){
 
 	events.emit("channel-send", {
 		type: "send",
@@ -15,4 +15,4 @@ module.exports = (playername, recipient, subject, text) => new Promise(function(
 	});
 
 	resolve();
-});
\ No newline at end of file
+});