From 54a26ea85aa1fd5036fa48f039927265d34d4db2 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 08:58:18 -0500 Subject: [PATCH 01/28] Sequelize and MySQL2 installed --- GhoulAlert/package-lock.json | 226 +++++++++++++++++++++++++++++++++-- GhoulAlert/package.json | 4 +- 2 files changed, 222 insertions(+), 8 deletions(-) diff --git a/GhoulAlert/package-lock.json b/GhoulAlert/package-lock.json index 96ac84f..0ef7804 100644 --- a/GhoulAlert/package-lock.json +++ b/GhoulAlert/package-lock.json @@ -17,6 +17,16 @@ "@types/babel-types": "*" } }, + "@types/geojson": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", + "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==" + }, + "@types/node": { + "version": "11.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz", + "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -265,6 +275,11 @@ "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", "dev": true }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", @@ -478,6 +493,15 @@ "wordwrap": "0.0.2" } }, + "cls-bluebird": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", + "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=", + "requires": { + "is-bluebird": "^1.0.2", + "shimmer": "^1.1.0" + } + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -684,6 +708,11 @@ } } }, + "denque": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.0.tgz", + "integrity": "sha512-gh513ac7aiKrAgjiIBWZG0EASyDF9p4JMWwKA8YU5s9figrL5SRNEMT6FDynsegakuhWd1wVqTvqvqAoDxw7wQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -708,6 +737,11 @@ "is-obj": "^1.0.0" } }, + "dottie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.1.tgz", + "integrity": "sha512-ch5OQgvGDK2u8pSZeSYAQaV/lczImd7pMJ7BcEPXmnFVjy4yJIzP6CsODJUTH8mg1tyH1Z2abOiuJO3DjZ/GBw==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1520,6 +1554,19 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "generic-pool": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.5.0.tgz", + "integrity": "sha512-dEkxmX+egB2o4NR80c/q+xzLLzLX+k68/K8xv81XprD+Sk7ZtP14VugeCz+fUwv5FzpWq40pPtAkzPRqT8ka9w==" + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -1670,6 +1717,11 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -1704,6 +1756,11 @@ "binary-extensions": "^1.0.0" } }, + "is-bluebird": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", + "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -1843,6 +1900,11 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", @@ -1934,6 +1996,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -1949,7 +2016,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -2077,6 +2143,19 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "moment-timezone": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz", + "integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==", + "requires": { + "moment": ">= 2.9.0" + } + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -2094,6 +2173,39 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mysql2": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.6.5.tgz", + "integrity": "sha512-zedaOOyb3msuuZcJJnxIX/EGOpmljDG7B+UevRH5lqcv+yhy9eCwkArBz8/AO+/rlY3/oCsOdG8R5oD6k0hNfg==", + "requires": { + "denque": "^1.4.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.4.24", + "long": "^4.0.0", + "lru-cache": "^4.1.3", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "requires": { + "lru-cache": "^4.1.3" + } + }, "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", @@ -2357,8 +2469,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "pstree.remy": { "version": "1.1.6", @@ -2610,6 +2721,15 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry-as-promised": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", + "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", + "requires": { + "bluebird": "^3.4.6", + "debug": "^2.6.9" + } + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -2640,8 +2760,7 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "semver-diff": { "version": "2.1.0", @@ -2672,6 +2791,50 @@ "statuses": "~1.4.0" } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + }, + "sequelize": { + "version": "4.42.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.42.1.tgz", + "integrity": "sha512-W9i/CkBjCHLzEkJQkaxXaK82MA16b7F74PjtE7EUR+d7WU/X3U+YU5givWR+/VRXay1VXDyBOfXgw9/zdhDSDg==", + "requires": { + "bluebird": "^3.5.0", + "cls-bluebird": "^2.1.0", + "debug": "^3.1.0", + "depd": "^1.1.0", + "dottie": "^2.0.0", + "generic-pool": "3.5.0", + "inflection": "1.12.0", + "lodash": "^4.17.1", + "moment": "^2.20.0", + "moment-timezone": "^0.5.14", + "retry-as-promised": "^2.3.2", + "semver": "^5.5.0", + "terraformer-wkt-parser": "^1.1.2", + "toposort-class": "^1.0.1", + "uuid": "^3.2.1", + "validator": "^10.4.0", + "wkx": "^0.4.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -2726,6 +2889,11 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -2873,6 +3041,11 @@ "extend-shallow": "^3.0.0" } }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -2957,6 +3130,23 @@ "execa": "^0.7.0" } }, + "terraformer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.9.tgz", + "integrity": "sha512-YlmQ1fsMWTkKGDGibCRWgmLzrpDRUr63Q025LJ/taYQ6j1Yb8q9McKF7NBi6ACAyUXO6F/bl9w6v4MY307y5Ag==", + "requires": { + "@types/geojson": "^1.0.0" + } + }, + "terraformer-wkt-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.0.tgz", + "integrity": "sha512-QU3iA54St5lF8Za1jg1oj4NYc8sn5tCZ08aNSWDeGzrsaV48eZk1iAVWasxhNspYBoCqdHuoot1pUTUrE1AJ4w==", + "requires": { + "@types/geojson": "^1.0.0", + "terraformer": "~1.0.5" + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -3004,6 +3194,11 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -3205,6 +3400,16 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3247,6 +3452,14 @@ "acorn-globals": "^3.0.0" } }, + "wkx": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.6.tgz", + "integrity": "sha512-LHxXlzRCYQXA9ZHgs8r7Gafh0gVOE8o3QmudM1PIkOdkXXjW7Thcl+gb2P2dRuKgW8cqkitCRZkkjtmWzpHi7A==", + "requires": { + "@types/node": "*" + } + }, "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", @@ -3272,8 +3485,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { "version": "3.10.0", diff --git a/GhoulAlert/package.json b/GhoulAlert/package.json index b356bd6..2bbe884 100644 --- a/GhoulAlert/package.json +++ b/GhoulAlert/package.json @@ -12,7 +12,9 @@ "express": "~4.16.0", "http-errors": "~1.6.2", "morgan": "~1.9.0", - "pug": "^2.0.3" + "mysql2": "^1.6.5", + "pug": "^2.0.3", + "sequelize": "^4.42.1" }, "devDependencies": { "nodemon": "^1.18.10" From f6d604b7ad2ed803c4c58709b1659315e35ce98c Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 10:01:33 -0500 Subject: [PATCH 02/28] Installed sequelize-cli, used to generate Sequelize files --- GhoulAlert/config/config.json | 23 ++ GhoulAlert/models/index.js | 37 ++ GhoulAlert/package-lock.json | 645 ++++++++++++++++++++++++++++++++-- GhoulAlert/package.json | 3 +- 4 files changed, 670 insertions(+), 38 deletions(-) create mode 100644 GhoulAlert/config/config.json create mode 100644 GhoulAlert/models/index.js diff --git a/GhoulAlert/config/config.json b/GhoulAlert/config/config.json new file mode 100644 index 0000000..0f858c6 --- /dev/null +++ b/GhoulAlert/config/config.json @@ -0,0 +1,23 @@ +{ + "development": { + "username": "root", + "password": null, + "database": "database_development", + "host": "127.0.0.1", + "dialect": "mysql" + }, + "test": { + "username": "root", + "password": null, + "database": "database_test", + "host": "127.0.0.1", + "dialect": "mysql" + }, + "production": { + "username": "root", + "password": null, + "database": "database_production", + "host": "127.0.0.1", + "dialect": "mysql" + } +} diff --git a/GhoulAlert/models/index.js b/GhoulAlert/models/index.js new file mode 100644 index 0000000..c1a3d6d --- /dev/null +++ b/GhoulAlert/models/index.js @@ -0,0 +1,37 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = require(__dirname + '/../config/config.json')[env]; +const db = {}; + +let sequelize; +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +fs + .readdirSync(__dirname) + .filter(file => { + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); + }) + .forEach(file => { + const model = sequelize['import'](path.join(__dirname, file)); + db[model.name] = model; + }); + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/GhoulAlert/package-lock.json b/GhoulAlert/package-lock.json index 0ef7804..e86e86e 100644 --- a/GhoulAlert/package-lock.json +++ b/GhoulAlert/package-lock.json @@ -27,11 +27,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz", "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==" }, + "@types/semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.5", @@ -84,8 +88,7 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { "version": "3.2.1", @@ -197,8 +200,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -324,7 +326,6 @@ "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" @@ -483,6 +484,26 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, + "cli-color": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", + "requires": { + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -502,6 +523,11 @@ "shimmer": "^1.1.0" } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -527,6 +553,11 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -536,8 +567,16 @@ "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 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } }, "configstore": { "version": "3.1.2", @@ -636,6 +675,14 @@ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -748,6 +795,26 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "editorconfig": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz", + "integrity": "sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==", + "requires": { + "@types/node": "^10.11.7", + "@types/semver": "^5.5.0", + "commander": "^2.19.0", + "lru-cache": "^4.1.3", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "@types/node": { + "version": "10.12.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.27.tgz", + "integrity": "sha512-e9wgeY6gaY21on3ve0xAjgBVjGDWq/xUteK0ujsE53bUoxycMkqfnkUgMt6ffZtykZ5X12Mg3T7Pw4TRCObDKg==" + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -758,6 +825,54 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "es5-ext": { + "version": "0.10.48", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.48.tgz", + "integrity": "sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -779,6 +894,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -995,6 +1119,14 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1020,6 +1152,21 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, "fsevents": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", @@ -1567,6 +1714,11 @@ "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.5.0.tgz", "integrity": "sha512-dEkxmX+egB2o4NR80c/q+xzLLzLX+k68/K8xv81XprD+Sk7ZtP14VugeCz+fUwv5FzpWq40pPtAkzPRqT8ka9w==" }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -1579,6 +1731,19 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -1631,8 +1796,7 @@ "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, "has": { "version": "1.0.3", @@ -1722,6 +1886,15 @@ "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -1730,8 +1903,12 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" }, "ipaddr.js": { "version": "1.8.0", @@ -1834,8 +2011,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "4.0.0", @@ -1928,8 +2104,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-windows": { "version": "1.0.2", @@ -1946,8 +2121,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -1955,11 +2129,42 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "js-beautify": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz", + "integrity": "sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA==", + "requires": { + "config-chain": "^1.1.12", + "editorconfig": "^0.15.2", + "glob": "^7.1.3", + "mkdirp": "~0.5.0", + "nopt": "~4.0.1" + }, + "dependencies": { + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + } + } + }, "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -1991,6 +2196,23 @@ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", @@ -2021,6 +2243,14 @@ "yallist": "^2.1.2" } }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "requires": { + "es5-ext": "~0.10.2" + } + }, "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -2030,6 +2260,14 @@ "pify": "^3.0.0" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -2050,6 +2288,31 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memoizee": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "requires": { + "d": "1", + "es5-ext": "^0.10.45", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.5" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -2107,11 +2370,15 @@ "mime-db": "~1.38.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "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.7" } @@ -2143,6 +2410,21 @@ } } }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -2245,6 +2527,16 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "nodemon": { "version": "1.18.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.10.tgz", @@ -2299,11 +2591,15 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, "requires": { "path-key": "^2.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2362,11 +2658,114 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" }, "package-json": { "version": "4.0.1", @@ -2397,11 +2796,15 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "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 + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -2412,8 +2815,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.6", @@ -2457,6 +2859,11 @@ "asap": "~2.0.3" } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" + }, "proxy-addr": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", @@ -2591,6 +2998,15 @@ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.7.tgz", "integrity": "sha1-wA1cUSi6xYBr7BXSt+fNq+QlMfM=" }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2701,6 +3117,16 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "resolve": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", @@ -2835,6 +3261,52 @@ } } }, + "sequelize-cli": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-5.4.0.tgz", + "integrity": "sha512-4Gvl0yH0T3hhSdiiOci3+IKIfVG9x2os0hGWsbfa8QuyGgk9mZOqgTBnSCRtuxsdAyzUix9kfcTnfNolVNtprg==", + "requires": { + "bluebird": "^3.5.3", + "cli-color": "^1.4.0", + "fs-extra": "^7.0.1", + "js-beautify": "^1.8.8", + "lodash": "^4.17.5", + "resolve": "^1.5.0", + "umzug": "^2.1.0", + "yargs": "^12.0.5" + }, + "dependencies": { + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + } + } + }, "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -2846,6 +3318,11 @@ "send": "0.16.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -2878,7 +3355,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -2886,19 +3362,22 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "snapdragon": { "version": "0.8.2", @@ -3076,7 +3555,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -3095,7 +3573,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -3103,8 +3580,7 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { "version": "2.0.1", @@ -3153,6 +3629,15 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, + "timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "requires": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -3240,6 +3725,15 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "umzug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.2.0.tgz", + "integrity": "sha512-xZLW76ax70pND9bx3wqwb8zqkFGzZIK8dIHD9WdNy/CrNfjWcwQgQkGCuUqcuwEBvUm+g07z+qWvY+pxDmMEEw==", + "requires": { + "babel-runtime": "^6.23.0", + "bluebird": "^3.5.3" + } + }, "undefsafe": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", @@ -3293,6 +3787,11 @@ "crypto-random-string": "^1.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3424,11 +3923,15 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, "widest-line": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", @@ -3465,6 +3968,53 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, "write-file-atomic": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", @@ -3482,6 +4032,11 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -3497,6 +4052,22 @@ "decamelize": "^1.0.0", "window-size": "0.1.0" } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + } + } } } } diff --git a/GhoulAlert/package.json b/GhoulAlert/package.json index 2bbe884..259eef8 100644 --- a/GhoulAlert/package.json +++ b/GhoulAlert/package.json @@ -14,7 +14,8 @@ "morgan": "~1.9.0", "mysql2": "^1.6.5", "pug": "^2.0.3", - "sequelize": "^4.42.1" + "sequelize": "^4.42.1", + "sequelize-cli": "^5.4.0" }, "devDependencies": { "nodemon": "^1.18.10" From 1397225abe000c8df92c35920eff2553a2c7ee11 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 10:07:09 -0500 Subject: [PATCH 03/28] www includes models --- GhoulAlert/bin/www | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/GhoulAlert/bin/www b/GhoulAlert/bin/www index 8f90936..c8a614d 100755 --- a/GhoulAlert/bin/www +++ b/GhoulAlert/bin/www @@ -7,6 +7,7 @@ var app = require('../app'); var debug = require('debug')('ghoulalert:server'); var http = require('http'); +var models = require('../models'); /** * Get port from environment and store in Express. @@ -21,13 +22,15 @@ app.set('port', port); var server = http.createServer(app); -/** - * Listen on provided port, on all network interfaces. - */ +models.sequelize.sync().then(function(){ + /** + * Listen on provided port, on all network interfaces. + */ -server.listen(port); -server.on('error', onError); -server.on('listening', onListening); + server.listen(port); + server.on('error', onError); + server.on('listening', onListening); +}) /** * Normalize a port into a number, string, or false. From fe7ec6b5b9248d5eb9477d290c8af751bf231742 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 11:57:58 -0500 Subject: [PATCH 04/28] Database configuration, temp user model, user model used in route --- GhoulAlert/config/config.json | 4 ++-- GhoulAlert/models/user.js | 14 ++++++++++++++ GhoulAlert/routes/index.js | 8 +++++++- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 GhoulAlert/models/user.js diff --git a/GhoulAlert/config/config.json b/GhoulAlert/config/config.json index 0f858c6..843c569 100644 --- a/GhoulAlert/config/config.json +++ b/GhoulAlert/config/config.json @@ -1,14 +1,14 @@ { "development": { "username": "root", - "password": null, + "password": "password", "database": "database_development", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "root", - "password": null, + "password": "password", "database": "database_test", "host": "127.0.0.1", "dialect": "mysql" diff --git a/GhoulAlert/models/user.js b/GhoulAlert/models/user.js new file mode 100644 index 0000000..e527211 --- /dev/null +++ b/GhoulAlert/models/user.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = (sequelize, DataTypes) => { + // Define the user model + var User = sequelize.define('User', { + // The user has a "username", of the string datatype + username: DataTypes.STRING, + // The user has a "password", also of the string datatype + // TEMPORARY - A more secure option will be used when authorization is integrated + password: DataTypes.STRING + }) + + return User; +} diff --git a/GhoulAlert/routes/index.js b/GhoulAlert/routes/index.js index ecca96a..7ed5918 100644 --- a/GhoulAlert/routes/index.js +++ b/GhoulAlert/routes/index.js @@ -1,9 +1,15 @@ var express = require('express'); var router = express.Router(); +var models = require('../models'); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'Express' }); + models.User.findAll().then(function(users){ + res.render('index', { + title: 'Express', + users: users + }); + }) }); module.exports = router; From ad78f79fbdf36dcdad3ddeabd0179b5958b40dce Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 12:09:38 -0500 Subject: [PATCH 05/28] Default view now displays usernames of all existing users in database --- GhoulAlert/views/index.pug | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GhoulAlert/views/index.pug b/GhoulAlert/views/index.pug index 3d63b9a..fef48a8 100644 --- a/GhoulAlert/views/index.pug +++ b/GhoulAlert/views/index.pug @@ -3,3 +3,7 @@ extends layout block content h1= title p Welcome to #{title} + h1 Users + ol + each user in users + li= user.username From 54c0b22a0cd1536275fe5ef1d673973d6600a62e Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Feb 2019 23:27:10 -0500 Subject: [PATCH 06/28] Configuration and instructions --- GhoulAlert/config/config.json | 14 +++++----- README.md | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/GhoulAlert/config/config.json b/GhoulAlert/config/config.json index 843c569..d10a249 100644 --- a/GhoulAlert/config/config.json +++ b/GhoulAlert/config/config.json @@ -1,22 +1,22 @@ { "development": { - "username": "root", - "password": "password", - "database": "database_development", + "username": "ghoul", + "password": "ghoul_user!61", + "database": "ghoul_development", "host": "127.0.0.1", "dialect": "mysql" }, "test": { - "username": "root", - "password": "password", - "database": "database_test", + "username": "ghoul", + "password": "ghoul_user!61", + "database": "ghoul_test", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": null, - "database": "database_production", + "database": "ghoul_production", "host": "127.0.0.1", "dialect": "mysql" } diff --git a/README.md b/README.md index 3202c3a..fba35c4 100644 --- a/README.md +++ b/README.md @@ -142,3 +142,55 @@ Has debug console. Uses the nodemon package to refresh the server on changes. This is the most useful option for development. `DEBUG=ghoulalert:* npm run devstart` + +## 2/28/2019 +### MySQL with Sequelize + +This program can now connect to a MySQL database using the Sequelize module as +an ORM (Object Relational Mapping Tool). + +In order for the connection to be established, the database it is expecting needs to +actually exist. + +1. First, follow the instructions to install the MySQL Community Server: +https://dev.mysql.com/downloads/mysql/ + +2. Next, you will want to install the MySQL Workbench to help you manage the server more easily: +https://dev.mysql.com/downloads/workbench/ + +3. When you open the workbench, under MySQL connections there will be a local instance. +This is what we will be working with, so open it. + +4. In the left panel in the Administration tab, there will be an icon for "Users and Privileges". +Click this. + +5. Here, we need to set up a database user according to the configuration of the program. +Click "Add Account". Give it the login name "ghoul", and the password "ghoul_user!61". +Select the "Administrative Roles" tab, and click the "DBA" checkmark, which will select everything. +Select the "Schema Privileges" tab, and click the "Select "ALL"" button to give this user all privileges. +Finalize this by clicking the "Apply" button. + +6. With the user created, we now need to create the database itself. In the workbench, the words "Schema" and "Database" are used interchangeably, so press the button in the topbar represented by a cylinder to add the schemas that we need. + +7. All you need to enter in the Schema Editor is the schema name. Give it the name "ghoul_development" +and press the small "Apply" button to the bottom right. Repeat this process to create another schema called "ghoul_test". We will use that later when testing is implemented. + +8. This is all we need to do to set the database up - Sequelize will take care of creating tables. Navigate to the GhoulAlert directory in the command line/terminal and run the application, and it should successfully connect to and shape the database based on the existing models. + +9. Now that the database is set up, let's test the program MVC flow. As of now, we have a view (index.pug) that has a section that iterates on each user that exists, as sent to the view by the route, which in turn +attains that data from Sequelize reading the user.js model. We can use this view to make sure each step of that process works. + +10. Open the MySQL Workbench window again. We need to make a quick modification for the manual insertions we will make to work. On the left panel, open the Schemas tab. Drop down the "ghoul_development" schema, and "Tables". Hover over "Users", and click the wrench icon. Click on the row for "createdAt", and in the "Default" field, type "Now()". Do the same for the "updatedAt" row, then press the "Apply" button at the bottom right and apply these changes. + +11. In the middle section, there should be a tab starting with the word "Query". Select this tab and paste the following code (making sure it's the only text present): + +``` +INSERT INTO ghoul_development.Users(username, password) +VALUES ("Manmoth", "bright_light"); +``` +Press the lightning bolt in the bar above the code to run this query. + +12. Run the GhoulAlert application. Once it's active, navigate to localhost:3000. +Under Users, Manmoth's name should be listed. + +Catching up to this point is vital to contributing to the project. Complete these instructions and you will be ready when it's time to delegate coding work. From d2ecdc40c8d562d6849673e70bc79200c63b4c28 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sat, 23 Mar 2019 09:21:26 -0400 Subject: [PATCH 07/28] Installed passport, passport-local --- GhoulAlert/package-lock.json | 27 +++++++++++++++++++++++++++ GhoulAlert/package.json | 2 ++ 2 files changed, 29 insertions(+) diff --git a/GhoulAlert/package-lock.json b/GhoulAlert/package-lock.json index e86e86e..425db15 100644 --- a/GhoulAlert/package-lock.json +++ b/GhoulAlert/package-lock.json @@ -2790,6 +2790,28 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -2827,6 +2849,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", diff --git a/GhoulAlert/package.json b/GhoulAlert/package.json index 259eef8..33d0140 100644 --- a/GhoulAlert/package.json +++ b/GhoulAlert/package.json @@ -13,6 +13,8 @@ "http-errors": "~1.6.2", "morgan": "~1.9.0", "mysql2": "^1.6.5", + "passport": "^0.4.0", + "passport-local": "^1.0.0", "pug": "^2.0.3", "sequelize": "^4.42.1", "sequelize-cli": "^5.4.0" From 00586eda9e2e9be8d9198ec05981873da6a4e30a Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sat, 23 Mar 2019 11:02:01 -0400 Subject: [PATCH 08/28] users model altered for use with passport --- GhoulAlert/models/user.js | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/GhoulAlert/models/user.js b/GhoulAlert/models/user.js index e527211..6f420dc 100644 --- a/GhoulAlert/models/user.js +++ b/GhoulAlert/models/user.js @@ -1,14 +1,47 @@ 'use strict'; +const crypto = require('crypto'); +const jwt = require('jsonwebtoken'); module.exports = (sequelize, DataTypes) => { // Define the user model var User = sequelize.define('User', { // The user has a "username", of the string datatype username: DataTypes.STRING, - // The user has a "password", also of the string datatype - // TEMPORARY - A more secure option will be used when authorization is integrated - password: DataTypes.STRING + + // The user has a "password", composed of a hash and salt + hash: DataTypes.STRING, + salt: DataTypes.STRING }) + User.prototype.setPassword = function(password) { + this.salt = crypto.randomBytes(16).toString('hex'); + this.hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); + } + + User.prototype.validatePassword = function(password) { + const hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); + return this.hash === hash; + } + + User.prototype.generateJWT = function() { + const today = new Date(); + const expires = new Date(today); + expires.setDate(today.getDate() + 30); + + return jwt.sign({ + username: this.username, + id: this._id, + exp: parseInt(expires.getTime() / 1000, 10), + }, 'secret'); + } + + User.prototype.toAuthJSON = function() { + return { + _id: this._id, + username: this.username, + token: this.generateJWT() + }; + } + return User; } From ee54ec3d7f25eec64a5a71934c5ce3a8af01a87c Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sat, 23 Mar 2019 11:13:26 -0400 Subject: [PATCH 09/28] passport.js config --- GhoulAlert/app.js | 2 ++ GhoulAlert/config/passport.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 GhoulAlert/config/passport.js diff --git a/GhoulAlert/app.js b/GhoulAlert/app.js index ab7aed4..4580bee 100644 --- a/GhoulAlert/app.js +++ b/GhoulAlert/app.js @@ -7,6 +7,8 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); +require('./config/passport'); + var app = express(); // view engine setup diff --git a/GhoulAlert/config/passport.js b/GhoulAlert/config/passport.js new file mode 100644 index 0000000..bc63f03 --- /dev/null +++ b/GhoulAlert/config/passport.js @@ -0,0 +1,20 @@ +'use strict'; + +var models = require('../models'); +var passport = require('passport'); +var localStrat = require('passport-local'); + +var User = model.User; + +passport.use(new localStrat({ + usernameField: 'user[username]', + passwordField: 'user[password]', +}, (username, password, done) => { + User.findOne({ where: {title: 'aProject'} }).then((user) => { + if(!user || !user.validatePassword(password)) { + return done(null, false, { errors: {'username or password': 'is invalid'}}); + } + + return done(null, user); + }).catch(done); +})); From 9c77659779699e3a6fa91d82b86862e5fbc7555a Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sat, 23 Mar 2019 11:36:19 -0400 Subject: [PATCH 10/28] authorization options --- GhoulAlert/routes/authorize.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 GhoulAlert/routes/authorize.js diff --git a/GhoulAlert/routes/authorize.js b/GhoulAlert/routes/authorize.js new file mode 100644 index 0000000..85061f5 --- /dev/null +++ b/GhoulAlert/routes/authorize.js @@ -0,0 +1,26 @@ +const jwt = require('express-jwt'); + +const getTokenFromHeaders = (req) => { + const { headers: { authorization } } = req; + + if(authorization && authorization.split(' ')[0] === 'Token') { + return authorization.split(' ')[1]; + } + return null; +}; + +const auth = { + required: jwt({ + secret: 'secret', + userProperty: 'payload', + getToken: getTokenFromHeaders, + }), + optional: jwt({ + secret: 'secret', + userProperty: 'payload', + getToken: getTokenFromHeaders, + credentialsRequired: false, + }), +}; + +module.exports = auth; From 586e4219f44c03aa2d59922ca6e8ccbc621c53ad Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Mar 2019 15:12:36 -0400 Subject: [PATCH 11/28] Users API - creation, authentication, etc. --- GhoulAlert/app.js | 4 ++ GhoulAlert/config/passport.js | 2 +- GhoulAlert/models/user.js | 6 +- GhoulAlert/package-lock.json | 117 +++++++++++++++++++++++++++++++ GhoulAlert/package.json | 2 + GhoulAlert/routes/apiV1/index.js | 6 ++ GhoulAlert/routes/apiV1/users.js | 86 +++++++++++++++++++++++ GhoulAlert/routes/index.js | 5 +- 8 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 GhoulAlert/routes/apiV1/index.js create mode 100644 GhoulAlert/routes/apiV1/users.js diff --git a/GhoulAlert/app.js b/GhoulAlert/app.js index 4580bee..0a39b01 100644 --- a/GhoulAlert/app.js +++ b/GhoulAlert/app.js @@ -8,6 +8,8 @@ var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); require('./config/passport'); +var apiV1IndexRouter = require('./routes/apiV1/index'); +var apiV1UsersRouter = require('./routes/apiV1/users'); var app = express(); @@ -23,6 +25,8 @@ app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); +app.use('/apiV1', apiV1IndexRouter); +app.use('/apiV1/users', apiV1UsersRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/GhoulAlert/config/passport.js b/GhoulAlert/config/passport.js index bc63f03..ba6f6c9 100644 --- a/GhoulAlert/config/passport.js +++ b/GhoulAlert/config/passport.js @@ -4,7 +4,7 @@ var models = require('../models'); var passport = require('passport'); var localStrat = require('passport-local'); -var User = model.User; +var User = models.User; passport.use(new localStrat({ usernameField: 'user[username]', diff --git a/GhoulAlert/models/user.js b/GhoulAlert/models/user.js index 6f420dc..c463903 100644 --- a/GhoulAlert/models/user.js +++ b/GhoulAlert/models/user.js @@ -6,11 +6,11 @@ module.exports = (sequelize, DataTypes) => { // Define the user model var User = sequelize.define('User', { // The user has a "username", of the string datatype - username: DataTypes.STRING, + username: { type: DataTypes.STRING, allowNull: false, unique: true }, // The user has a "password", composed of a hash and salt - hash: DataTypes.STRING, - salt: DataTypes.STRING + hash: DataTypes.TEXT('long'), + salt: DataTypes.TEXT('long') }) User.prototype.setPassword = function(password) { diff --git a/GhoulAlert/package-lock.json b/GhoulAlert/package-lock.json index 425db15..8859cfa 100644 --- a/GhoulAlert/package-lock.json +++ b/GhoulAlert/package-lock.json @@ -160,6 +160,11 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -360,6 +365,11 @@ } } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -795,6 +805,14 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "editorconfig": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz", @@ -990,6 +1008,22 @@ "vary": "~1.1.2" } }, + "express-jwt": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz", + "integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==", + "requires": { + "async": "^1.5.0", + "express-unless": "^0.3.0", + "jsonwebtoken": "^8.1.0", + "lodash.set": "^4.0.0" + } + }, + "express-unless": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz", + "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=" + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -2165,6 +2199,30 @@ "graceful-fs": "^4.1.6" } }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "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", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -2174,6 +2232,25 @@ "promise": "^7.0.1" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -2218,6 +2295,46 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", diff --git a/GhoulAlert/package.json b/GhoulAlert/package.json index 33d0140..050e928 100644 --- a/GhoulAlert/package.json +++ b/GhoulAlert/package.json @@ -10,7 +10,9 @@ "cookie-parser": "~1.4.3", "debug": "~2.6.9", "express": "~4.16.0", + "express-jwt": "^5.3.1", "http-errors": "~1.6.2", + "jsonwebtoken": "^8.5.1", "morgan": "~1.9.0", "mysql2": "^1.6.5", "passport": "^0.4.0", diff --git a/GhoulAlert/routes/apiV1/index.js b/GhoulAlert/routes/apiV1/index.js new file mode 100644 index 0000000..09fc14e --- /dev/null +++ b/GhoulAlert/routes/apiV1/index.js @@ -0,0 +1,6 @@ +const express = require('express'); +const router = express.Router(); + +router.use('/users', require('./users')); + +module.exports = router; diff --git a/GhoulAlert/routes/apiV1/users.js b/GhoulAlert/routes/apiV1/users.js new file mode 100644 index 0000000..1cbe278 --- /dev/null +++ b/GhoulAlert/routes/apiV1/users.js @@ -0,0 +1,86 @@ +var passport = require('passport'); +var router = require('express').Router(); +var auth = require('../authorize'); +var models = require('../../models'); + +var User = models.User; + +//POST new user route (optional, everyone has access) +router.post('/', auth.optional, (req, res, next) => { + var user = req.body; + + if(!user.username) { + return res.status(422).json({ + errors: { + username: 'is required', + }, + }); + } + + if(!user.password) { + return res.status(422).json({ + errors: { + password: 'is required', + }, + }); + } + + var finalUser = User.build(user); + + finalUser.setPassword(user.password); + + return finalUser.save() + .then(() => res.json(finalUser.toAuthJSON())); +}); + +//POST login route (optional, everyone has access) +router.post('/login', auth.optional, (req, res, next) => { + var user = req.body; + + if(!user.username) { + return res.status(422).json({ + errors: { + username: 'is required', + }, + }); + } + + if(!user.password) { + return res.status(422).json({ + errors: { + password: 'is required', + }, + }); + } + + return passport.authenticate('local', { session: false }, (err, passportUser, info) => { + if(err) { + return next(err); + } + + if(passportUser) { + const user = passportUser; + user.token = passportUser.generateJWT(); + + return res.json(user.toAuthJSON()); + } + + return status(400).info; + })(req, res, next); +}); + +//GET current route (required, only authenticated users have access) +router.get('/current', auth.required, (req, res, next) => { + var username = req.payload.username; + + return User.findOne({ where: {username: username} }) + .then((user) => { + if(!user) { + return res.sendStatus(400); + } + + return res.json(user.toAuthJSON()); + }); +}); + +module.exports = router; diff --git a/GhoulAlert/routes/index.js b/GhoulAlert/routes/index.js index 7ed5918..31eae25 100644 --- a/GhoulAlert/routes/index.js +++ b/GhoulAlert/routes/index.js @@ -2,12 +2,15 @@ var express = require('express'); var router = express.Router(); var models = require('../models'); +/* API routes */ +router.use('/apiV1', require('./apiV1')); + /* GET home page. */ router.get('/', function(req, res, next) { models.User.findAll().then(function(users){ res.render('index', { title: 'Express', - users: users + users: users }); }) }); From 99a348800c4ff4610a310ad4ba7a5b6e5a7e9f97 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Mar 2019 19:06:32 -0400 Subject: [PATCH 12/28] login --- GhoulAlert/models/user.js | 11 +++++++++-- GhoulAlert/routes/apiV1/users.js | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/GhoulAlert/models/user.js b/GhoulAlert/models/user.js index c463903..0801dba 100644 --- a/GhoulAlert/models/user.js +++ b/GhoulAlert/models/user.js @@ -5,8 +5,15 @@ const jwt = require('jsonwebtoken'); module.exports = (sequelize, DataTypes) => { // Define the user model var User = sequelize.define('User', { - // The user has a "username", of the string datatype - username: { type: DataTypes.STRING, allowNull: false, unique: true }, + // The user has a "username", of the string datatype, that is unique + username: { + type: DataTypes.STRING, + allowNull: false, + unique: { + args: true, + msg: "Username already exists; it must be unique." + } + }, // The user has a "password", composed of a hash and salt hash: DataTypes.TEXT('long'), diff --git a/GhoulAlert/routes/apiV1/users.js b/GhoulAlert/routes/apiV1/users.js index 1cbe278..c33042d 100644 --- a/GhoulAlert/routes/apiV1/users.js +++ b/GhoulAlert/routes/apiV1/users.js @@ -2,6 +2,7 @@ var passport = require('passport'); var router = require('express').Router(); var auth = require('../authorize'); var models = require('../../models'); +const Sequelize = require('sequelize'); var User = models.User; @@ -30,7 +31,10 @@ router.post('/', auth.optional, (req, res, next) => { finalUser.setPassword(user.password); return finalUser.save() - .then(() => res.json(finalUser.toAuthJSON())); + .then(() => res.json(finalUser.toAuthJSON())) + .catch(Sequelize.ValidationError, function (error) { + res.json({ error: { message: error.message } }); + }); }); //POST login route (optional, everyone has access) @@ -65,7 +69,7 @@ router.post('/login', auth.optional, (req, res, next) => { return res.json(user.toAuthJSON()); } - return status(400).info; + return res.status(400).info; })(req, res, next); }); From 1a752e0ef4f6edd5bd58e1f5712512dc7f672a0e Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Thu, 28 Mar 2019 19:41:02 -0400 Subject: [PATCH 13/28] Marker model, relationship with User --- GhoulAlert/models/index.js | 4 ++++ GhoulAlert/models/marker.js | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 GhoulAlert/models/marker.js diff --git a/GhoulAlert/models/index.js b/GhoulAlert/models/index.js index c1a3d6d..0c5236d 100644 --- a/GhoulAlert/models/index.js +++ b/GhoulAlert/models/index.js @@ -31,6 +31,10 @@ Object.keys(db).forEach(modelName => { } }); +// Relationships go here +db.User.hasMany(db.Marker); +db.Marker.belongsTo(db.User); + db.sequelize = sequelize; db.Sequelize = Sequelize; diff --git a/GhoulAlert/models/marker.js b/GhoulAlert/models/marker.js new file mode 100644 index 0000000..68bda81 --- /dev/null +++ b/GhoulAlert/models/marker.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = (sequelize, DataTypes) => { + var Marker = sequelize.define('Marker', { + latitude: DataTypes.FLOAT, + longitude: DataTypes.FLOAT, + description: DataTypes.STRING + }) + + return Marker; +} From 81899f84643be818e84f8a8ae7b640a874e6c66e Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sun, 7 Apr 2019 16:56:20 -0400 Subject: [PATCH 14/28] Base API constructed --- GhoulAlert/config/passport.js | 7 +- GhoulAlert/models/marker.js | 43 ++++- GhoulAlert/models/user.js | 17 +- GhoulAlert/routes/apiV1/index.js | 1 + GhoulAlert/routes/apiV1/markers.js | 248 +++++++++++++++++++++++++++++ GhoulAlert/routes/apiV1/users.js | 13 +- 6 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 GhoulAlert/routes/apiV1/markers.js diff --git a/GhoulAlert/config/passport.js b/GhoulAlert/config/passport.js index ba6f6c9..e100fdb 100644 --- a/GhoulAlert/config/passport.js +++ b/GhoulAlert/config/passport.js @@ -7,14 +7,13 @@ var localStrat = require('passport-local'); var User = models.User; passport.use(new localStrat({ - usernameField: 'user[username]', - passwordField: 'user[password]', + usernameField: 'username', + passwordField: 'password', }, (username, password, done) => { - User.findOne({ where: {title: 'aProject'} }).then((user) => { + User.findOne({ where: { username: username } }).then((user) => { if(!user || !user.validatePassword(password)) { return done(null, false, { errors: {'username or password': 'is invalid'}}); } - return done(null, user); }).catch(done); })); diff --git a/GhoulAlert/models/marker.js b/GhoulAlert/models/marker.js index 68bda81..487839e 100644 --- a/GhoulAlert/models/marker.js +++ b/GhoulAlert/models/marker.js @@ -2,10 +2,45 @@ module.exports = (sequelize, DataTypes) => { var Marker = sequelize.define('Marker', { - latitude: DataTypes.FLOAT, - longitude: DataTypes.FLOAT, - description: DataTypes.STRING - }) + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + + latitude: { + type: DataTypes.FLOAT, + allowNull: false, + validate: { + notEmpty: true + } + }, + + longitude: { + type: DataTypes.FLOAT, + allowNull: false, + validate: { + notEmpty: true + } + }, + + name: { + type: DataTypes.STRING, + allowNull: false, + unique: { + args: true, + msg: "Name of the marker must be unique" + }, + validate: { + notEmpty: true + } + }, + + description: { + type: DataTypes.STRING, + allowNull: true, + } + }); return Marker; } diff --git a/GhoulAlert/models/user.js b/GhoulAlert/models/user.js index 0801dba..29b5ff3 100644 --- a/GhoulAlert/models/user.js +++ b/GhoulAlert/models/user.js @@ -5,6 +5,12 @@ const jwt = require('jsonwebtoken'); module.exports = (sequelize, DataTypes) => { // Define the user model var User = sequelize.define('User', { + // The user has an id, which identifies them and acts as a primary key + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, // The user has a "username", of the string datatype, that is unique username: { type: DataTypes.STRING, @@ -37,18 +43,25 @@ module.exports = (sequelize, DataTypes) => { return jwt.sign({ username: this.username, - id: this._id, + id: this.id, exp: parseInt(expires.getTime() / 1000, 10), }, 'secret'); } User.prototype.toAuthJSON = function() { return { - _id: this._id, + id: this.id, username: this.username, token: this.generateJWT() }; } + User.prototype.toJSON = function() { + return { + id: this.id, + username: this.username + } + } + return User; } diff --git a/GhoulAlert/routes/apiV1/index.js b/GhoulAlert/routes/apiV1/index.js index 09fc14e..5d2c469 100644 --- a/GhoulAlert/routes/apiV1/index.js +++ b/GhoulAlert/routes/apiV1/index.js @@ -2,5 +2,6 @@ const express = require('express'); const router = express.Router(); router.use('/users', require('./users')); +router.use('/markers', require('./markers')); module.exports = router; diff --git a/GhoulAlert/routes/apiV1/markers.js b/GhoulAlert/routes/apiV1/markers.js new file mode 100644 index 0000000..9465fa5 --- /dev/null +++ b/GhoulAlert/routes/apiV1/markers.js @@ -0,0 +1,248 @@ +var passport = require('passport'); +var router = require('express').Router(); +var auth = require('../authorize'); +var models = require('../../models'); +const Sequelize = require('sequelize'); + +var User = models.User; +var Marker = models.Marker; + +//Get all markers +router.get('/', auth.optional, (req, res, next) => { + return Marker.findAll().then((markers) => { + return res.json(markers); + }); +}); + +//Get all markers with basic user information included +router.get('/withusers', auth.optional, (req, res, next) => { + return Marker.findAll({ + attributes: ['id', 'latitude', 'longitude', 'name', 'description'], + include: [{ model: User, attributes: ['id', 'username'] }] + }).then((markers) => { + return res.json(markers); + }); +}); + +//Get a specific marker +router.get('/:mid', auth.optional, (req, res, next) => { + var marker_id = req.params.mid; + + return Marker.findOne({ where: { id: marker_id } }).then((marker) => { + if (!marker) { + return res.status(404).json({ + errors: { + marker: "not found" + } + }); + } + + return res.json(marker.toJSON()); + }); +}); + +//Get a specific marker with basic user information +router.get('/:mid/withuser', auth.optional, (req, res, next) => { + var marker_id = req.params.mid; + + return Marker.findOne({ + where: { id: marker_id }, + attributes: ['id', 'latitude', 'longitude', 'name', 'description'], + include: [{ model: User, attributes: ['id', 'username'] }] + }).then((marker) => { + if (!marker) { + return res.status(404).json({ + errors: { + marker: "not found" + } + }); + } + + return res.json(marker.toJSON()); + }); +}); + +//Post a new marker. User authentication required. +router.post('/', auth.required, (req, res, next) => { + var username = req.payload.username; + var marker = req.body; + + if(!marker.latitude) { + return res.status(422).json({ + errors: { + latitude: 'is required', + }, + }); + } + + if(!marker.longitude) { + return res.status(422).json({ + errors: { + longitude: 'is required', + }, + }); + } + + if(!marker.name) { + return res.status(422).json({ + errors: { + name: 'is required', + }, + }); + } + + return User.findOne({ where: { username: username } }).then((user) => { + if (!user) { + return res.sendStatus(400).json({ + errors: { + authentication: 'is invalid' + } + }); + } + + var finalMarker = Marker.build(marker); + finalMarker.setUser(user); + + return finalMarker.save() + .then(() => { + res.json({ + success: true, + marker: finalMarker.toJSON() + }); + }).catch(Sequelize.ValidationError, function (error) { + res.json({ + success: false, + error: { message: error.message } + }); + }); + + }); + +}); + +//Edit a marker. Authenticated user must match marker owner. +router.put('/:mid', auth.required, (req, res, next) => { + var marker_id = req.params.mid; + var username = req.payload.username; + var marker_update = req.body; + + return User.findOne({ where: { username: username } }).then((user) => { + if (!user) { + return res.sendStatus(400).json({ + errors: { + authentication: 'is invalid' + } + }); + } + + return Marker.findOne({ where: { id: marker_id } }).then((marker) => { + if (!marker) { + return res.sendStatus(404).json({ + errors: { + marker: 'not found' + } + }); + } + + marker.getUser().then((owner) => { + if (owner.id != user_id) { + return res.sendStatus(403).json({ + errors: { + user: 'not authorized to modify this marker' + } + }); + } + }); + + var preUpdateInfo = marker.toJSON(); + + marker.update(marker_update).then(() => { + return res.json({ + success: true, + editedFrom: preUpdateInfo, + editedTo: marker.toJSON() + }); + }).catch((error) => { + return res.json({ + success: false, + marker: preUpdateInfo, + error: { + message: error.message + } + }); + }); + + }); + + }); +}); + +//Delete a marker. Authenticated user must match marker owner. +router.delete('/:mid', auth.required, (req, res, next) => { + var marker_id = req.params.mid; + var username = req.payload.username; + + return User.findOne({ where: { username: username } }).then((user) => { + if (!user) { + return res.sendStatus(400).json({ + errors: { + authentication: 'is invalid' + } + }); + } + + return Marker.findOne({ where: { id: marker_id } }).then((marker) => { + if (!marker) { + return res.sendStatus(404).json({ + errors: { + marker: 'not found' + } + }); + } + + marker.getUser().then((owner) => { + if (owner.id != user.id) { + return res.sendStatus(403); + } + }); + + var destroyedInfo = marker.toJSON(); + + marker.destroy().then(() => { + return res.json({ + success: true, + deleted: destroyedInfo + }); + }).catch((error) => { + return res.json({ + success: false, + error: { + message: error.message + } + }); + }); + + }); + + }); +}); + +//GET all of a user's markers +router.get('/from/:uid', auth.optional, (req, res, next) => { + var uid = req.params.uid; + + return User.findOne({ where: { id: uid } }).then((user) =>{ + if(!user) { + return res.sendStatus(404); + } + + user.getMarkers().then((markers) => { + var markersJSON = markers.map(marker => marker.toJSON()); + return res.json(markersJSON); + }); + + }); +}); + + +module.exports = router; diff --git a/GhoulAlert/routes/apiV1/users.js b/GhoulAlert/routes/apiV1/users.js index c33042d..a1865e4 100644 --- a/GhoulAlert/routes/apiV1/users.js +++ b/GhoulAlert/routes/apiV1/users.js @@ -6,7 +6,7 @@ const Sequelize = require('sequelize'); var User = models.User; -//POST new user route (optional, everyone has access) +//Creates a new user with a given username and password router.post('/', auth.optional, (req, res, next) => { var user = req.body; @@ -37,7 +37,8 @@ router.post('/', auth.optional, (req, res, next) => { }); }); -//POST login route (optional, everyone has access) +//Creates a new token for an existing user if they provide the correct username +//and password router.post('/login', auth.optional, (req, res, next) => { var user = req.body; @@ -67,17 +68,19 @@ router.post('/login', auth.optional, (req, res, next) => { user.token = passportUser.generateJWT(); return res.json(user.toAuthJSON()); + } else { + return res.json(info); } return res.status(400).info; })(req, res, next); }); -//GET current route (required, only authenticated users have access) +//Gets information on the current user if they have authentication router.get('/current', auth.required, (req, res, next) => { - var username = req.payload.username; + var id = req.payload.id; - return User.findOne({ where: {username: username} }) + return User.findOne({ where: { id: id } }) .then((user) => { if(!user) { return res.sendStatus(400); From 31af321f594034037c09b71dd2145db7f3cf602c Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sun, 7 Apr 2019 18:37:50 -0400 Subject: [PATCH 15/28] API Readme --- GhoulAlert/routes/apiV1/README.md | 534 +++++++++++++++++++++++++++++ GhoulAlert/routes/apiV1/markers.js | 4 +- 2 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 GhoulAlert/routes/apiV1/README.md diff --git a/GhoulAlert/routes/apiV1/README.md b/GhoulAlert/routes/apiV1/README.md new file mode 100644 index 0000000..32a4f1f --- /dev/null +++ b/GhoulAlert/routes/apiV1/README.md @@ -0,0 +1,534 @@ +# API Documentation + +The API (Application Programming Interface) is the backbone of this application. +It manages the current models (as of now, the Users and Markers), and it handles +authorization for various features. + +When specific requests in the proper format are made to the API routes, the +application responds with JSON-formatted data. In addition to allowing the application +to "speak" with itself, this also allows other applications to receive some of this +application's data for their own use. + +## Authorization + +Some routes will be marked with **Authorization: Required**. This means that a +special token assigned to identify users is necessary in requests, or the application will +respond only with a 401 HTTP code. + +This token is received when a new user is created, when a user refreshes their +token through the user login route, or when a user checks their own status with the +current user route. Tokens will be valid for 30 days from when they are created/refreshed. + +This token should be attached to a header named **Authorization**, +in the format **Token [token here]**. + +## Paths + +### Users + +These are the registered users of our application. Users only need to register to +create, edit, and delete markers; simply viewing markers is accessible to anyone. + +###$ Create User + +`POST apiV1/users` + +**Authorization: Optional** + +Makes a new user with the provided username and password, and responds with a token +to be used for authorization. + +##### Expected Request Format + +``` +{ + "username": "[username{string}]", + "password": "[password{string}]" +} +``` + +**username:** The user's username +**password:** The password that this user will use to confirm their identity + +##### Expected Response + +``` +{ + "id": [id{integer}], + "username": [username{string}], + "token": [token{string}] +} +``` + +**id:** The ID number of the user as they are identified in the database +**username:** The user's username +**token:** The token that identifies the user in requests requiring authorization + +##### Constraints and Error Handling + +* Responds with an error when username or password is not provided +* Responds with a validation error if username is not unique (user with that username already exists) + +#### User Login + +`POST apiV1/users/login` + +**Authorization: Optional** + +Refreshes and responds with a new identifying token for the user with the provided +username and password. + +##### Expected Request Format + +``` +{ + "username": "[username{string}]", + "password": "[password{string}]" +} +``` + +**username:** The user's username +**password:** The password that this user will use to confirm their identity + +##### Expected Response + +``` +{ + "id": [id{integer}], + "username": [username{string}], + "token": [token{string}] +} +``` + +**id:** The ID number of the user as they are identified in the database +**username:** The user's username +**token:** The token that identifies the user in requests requiring authorization + +##### Constraints and Error Handling + +* Responds with an error when username or password is not provided +* Responds with an error if no user exists with given username +* Responds with an error if the given password for the given user is wrong + +#### Current User + +`GET apiV1/users/current` + +**Authorization: Required** + +Responds with data for the current user based on the provided authentication token. + +##### Expected Request Format + +No request body + +##### Expected Response + +``` +{ + "id": [id{integer}], + "username": [username{string}], + "token": [token{string}] +} +``` + +**id:** The ID number of the user as they are identified in the database +**username:** The user's username +**token:** The token that identifies the user in requests requiring authorization + +##### Constraints and Error Handling + +* Responds with an error if there is no authentication token +* Responds with an error if the authentication token does not map to a user + +### Markers + +These represent points on the Google Map that users can set. The database stores relevant +information in these objects and sends it to the map service to be processed onto the map. + +#### All Markers + +`GET apiV1/markers` + +**Authorization: Optional** + +Responds with an array containing all stored markers. + +##### Expected Request Format + +No request body + +##### Expected Response + +``` +[ + { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string}], + "UserId": [ID of owner{integer}] + }, + ... +] +``` + +**id:** The ID number of this marker as it is identified in the database +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker +**UserId:** The ID number of the user that created this marker + +##### Constraints and Error Handling + +N/A + +#### All Markers (And User Data) + +`GET apiV1/markers/withusers` + +**Authorization: Optional** + +Similar to the above, but includes user data (primarily the username of the owner) + +##### Expected Request Format + +No request body + +##### Expected Response + +``` +[ + { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string}], + "User": { + "id": [ID of owner{integer}], + "username": [user username{string}] + } + }, + ... +] +``` + +**id:** The ID number of this marker as it is identified in the database +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker +**User:** The user that owns this marker +**User.id** The ID number of the user that created this marker +**User.username** The username of the user that created this marker + +##### Constraints and Error Handling + +N/A + +#### Get One Marker + +`GET apiV1/markers/:id` + +**Where :id = ID of the marker** +**Authorization: Optional** + +Obtains the data for one marker by its ID + +##### Expected Request Format + +No request body + +##### Expected Response + +``` +{ + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string}], + "UserId": [ID of owner{integer}] +} +``` + +**id:** The ID number of this marker as it is identified in the database +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker +**UserId** The ID number of the user that created this marker + +##### Constraints and Error Handling + +* Responds with a 404 error if there is no marker with the given id + +#### Get One Marker (And User Data) + +`GET apiV1/markers/:id/withuser` +**Where :id = ID of the marker** +**Authorization: Optional** + +Same as above, but includes user data such as their username + +##### Expected Request Format + +No request body + +##### Expected Response + +``` +{ + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string}], + "User": { + "id": [ID of owner{integer}], + "username": [user username{string}] + } +} +``` + +**id:** The ID number of this marker as it is identified in the database +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker +**User:** The user that owns this marker +**User.id** The ID number of the user that created this marker +**User.username** The username of the user that created this marker + +##### Constraints and Error Handling + +* Responds with a 404 error if there is no marker with the given id + +#### Create a Marker + +`POST apiV1/markers` + +**Authorization: Required** + +Creates a new marker with the given parameters, owned by the user that owns the +provided authorization token. + +##### Expected Request Format + +``` +{ + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string}] , +} +``` + +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker (optional) + +##### Expected Response + +###### Success Case + +``` +{ + "success": true, + "marker": { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string/null}], + "UserId": [Owner's id{integer}], + "updatedAt": [Update date{date}], + "createdAt": [Creation date{date}] + } +} +``` +**success:** Whether or not creation succeeded +**marker.id:** The ID number of this marker as it is identified in the database +**marker.latitude:** The latitude value of the marker +**marker.longitude:** The longitude value of the marker +**marker.name:** The name for the marker +**marker.description:** A description for the marker +**marker.UserId** The ID number of the user that created this marker +**marker.updatedAt** The date this object was last updated +**marker.createdAt** The date this object was first created + +###### Failure Case + +This occurs if the error is validation based + +``` +{ + "success": false, + "error": { + "message": [Error message{string}] + } +} +``` +**success:** Whether or not creation succeeded +**error.message:** A description of the error that has occurred + +##### Constraints and Error Handling + +* Responds with an error if marker latitude, longitude, or name are not provided +* Responds with an error if there is no authorization token +* Responds with an error if the authorization token does not match a user +* Responds with an error if the name of the marker is not unique + +#### Edit a Marker + +`PUT apiV1/markers/:id` + +**Where :id = ID of marker to edit** +**Authorization: Required** + +Edits the marker with the provided ID, but only if the authorized user owns it. + +##### Expected Request Format + +The format is flexible; you only need to provide the properties that will be changed. + +``` +{ + "latitude": [latitude{float}] , + "longitude": [longitude{float}] , + "name": [name{string}] , + "description": [description{string}] , +} +``` + +**latitude:** The latitude value of the marker +**longitude:** The longitude value of the marker +**name:** The name for the marker +**description:** A description for the marker + +##### Expected Response + +###### Success Case + +``` +{ + "success": true, + "editedFrom": { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string/null}], + "UserId": [Owner's id{integer}], + "updatedAt": [Update date{date}], + "createdAt": [Creation date{date}] + }, + "editedTo": { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string/null}], + "UserId": [Owner's id{integer}], + "updatedAt": [Update date{date}], + "createdAt": [Creation date{date}] + } +} +``` + +**success:** Whether or not editing succeeded +**editedFrom:** The state of the marker before it was edited +**editedTo:** The state of the marker after being edited + +###### Failure Case + +This occurs if the error is validation based + +``` +{ + "success": false, + "error": { + "message": [Error message{string}] + } +} +``` +**success:** Whether or not editing succeeded +**error.message:** A description of the error that has occurred + +##### Constraints and Error Handling + +* Responds with an error if there is no authentication token +* Responds with an error if the authentication token does not match to a user +* Responds with an error if there is no marker with the given id +* Responds with an error if the user does not own the marker +* Responds with an error if the name is edited to one that already exists in the database + +#### Delete a Marker + +`DELETE apiV1/markers/:id` + +**Where :id = The ID of the marker to be deleted** +**Authorization: Required** + +If the authorized user owns the specified marker, it will be deleted + +##### Expected Request Format + +No request body + +##### Expected Response + +###### Success Case + +``` +{ + "success": true, + "deleted": { + "id": [id{integer}], + "latitude": [latitude{float}], + "longitude": [longitude{float}], + "name": [name{string}], + "description": [description{string/null}], + "UserId": [Owner's id{integer}], + "updatedAt": [Update date{date}], + "createdAt": [Creation date{date}] + } +} +``` + +**success:** Whether or not deletion succeeded +**deleted:** The object that was deleted +**deleted.id:** The ID number of this marker as it is identified in the database +**deleted.latitude:** The latitude value of the marker +**deleted.longitude:** The longitude value of the marker +**deleted.name:** The name for the marker +**deleted.description:** A description for the marker +**deleted.UserId** The ID number of the user that created this marker +**deleted.updatedAt** The date this object was last updated +**deleted.createdAt** The date this object was first created + +###### Failure Case + +This occurs if the error is validation based + +``` +{ + "success": false, + "error": { + "message": [Error message{string}] + } +} +``` +**success:** Whether or not deletion succeeded +**error.message:** A description of the error that has occurred + +##### Constraints and Error Handling + +* Responds with an error if there is no authentication token +* Responds with an error if the authentication token does not match to a user +* Responds with an error if there is no marker with the given id +* Responds with an error if the user does not own the marker diff --git a/GhoulAlert/routes/apiV1/markers.js b/GhoulAlert/routes/apiV1/markers.js index 9465fa5..15629b9 100644 --- a/GhoulAlert/routes/apiV1/markers.js +++ b/GhoulAlert/routes/apiV1/markers.js @@ -9,7 +9,9 @@ var Marker = models.Marker; //Get all markers router.get('/', auth.optional, (req, res, next) => { - return Marker.findAll().then((markers) => { + return Marker.findAll({ + attributes: ['id', 'latitude', 'longitude', 'name', 'description', 'UserId'] + }).then((markers) => { return res.json(markers); }); }); From b09781c0f38827d7a50061c9f3d9f33c53c10d9e Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sun, 7 Apr 2019 18:49:38 -0400 Subject: [PATCH 16/28] Formatting edits --- GhoulAlert/routes/apiV1/README.md | 159 +++++++++++++++--------------- 1 file changed, 82 insertions(+), 77 deletions(-) diff --git a/GhoulAlert/routes/apiV1/README.md b/GhoulAlert/routes/apiV1/README.md index 32a4f1f..cd6917e 100644 --- a/GhoulAlert/routes/apiV1/README.md +++ b/GhoulAlert/routes/apiV1/README.md @@ -47,8 +47,8 @@ to be used for authorization. } ``` -**username:** The user's username -**password:** The password that this user will use to confirm their identity +* **username:** The user's username +* **password:** The password that this user will use to confirm their identity ##### Expected Response @@ -60,9 +60,9 @@ to be used for authorization. } ``` -**id:** The ID number of the user as they are identified in the database -**username:** The user's username -**token:** The token that identifies the user in requests requiring authorization +* **id:** The ID number of the user as they are identified in the database +* **username:** The user's username +* **token:** The token that identifies the user in requests requiring authorization ##### Constraints and Error Handling @@ -87,8 +87,8 @@ username and password. } ``` -**username:** The user's username -**password:** The password that this user will use to confirm their identity +* **username:** The user's username +* **password:** The password that this user will use to confirm their identity ##### Expected Response @@ -100,9 +100,9 @@ username and password. } ``` -**id:** The ID number of the user as they are identified in the database -**username:** The user's username -**token:** The token that identifies the user in requests requiring authorization +* **id:** The ID number of the user as they are identified in the database +* **username:** The user's username +* **token:** The token that identifies the user in requests requiring authorization ##### Constraints and Error Handling @@ -132,9 +132,9 @@ No request body } ``` -**id:** The ID number of the user as they are identified in the database -**username:** The user's username -**token:** The token that identifies the user in requests requiring authorization +* **id:** The ID number of the user as they are identified in the database +* **username:** The user's username +* **token:** The token that identifies the user in requests requiring authorization ##### Constraints and Error Handling @@ -174,12 +174,12 @@ No request body ] ``` -**id:** The ID number of this marker as it is identified in the database -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker -**UserId:** The ID number of the user that created this marker +* **id:** The ID number of this marker as it is identified in the database +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker +* **UserId:** The ID number of the user that created this marker ##### Constraints and Error Handling @@ -216,14 +216,14 @@ No request body ] ``` -**id:** The ID number of this marker as it is identified in the database -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker -**User:** The user that owns this marker -**User.id** The ID number of the user that created this marker -**User.username** The username of the user that created this marker +* **id:** The ID number of this marker as it is identified in the database +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker +* **User:** The user that owns this marker +* **User.id** The ID number of the user that created this marker +* **User.username** The username of the user that created this marker ##### Constraints and Error Handling @@ -234,6 +234,7 @@ N/A `GET apiV1/markers/:id` **Where :id = ID of the marker** + **Authorization: Optional** Obtains the data for one marker by its ID @@ -255,12 +256,12 @@ No request body } ``` -**id:** The ID number of this marker as it is identified in the database -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker -**UserId** The ID number of the user that created this marker +* **id:** The ID number of this marker as it is identified in the database +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker +* **UserId** The ID number of the user that created this marker ##### Constraints and Error Handling @@ -269,7 +270,9 @@ No request body #### Get One Marker (And User Data) `GET apiV1/markers/:id/withuser` + **Where :id = ID of the marker** + **Authorization: Optional** Same as above, but includes user data such as their username @@ -294,14 +297,14 @@ No request body } ``` -**id:** The ID number of this marker as it is identified in the database -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker -**User:** The user that owns this marker -**User.id** The ID number of the user that created this marker -**User.username** The username of the user that created this marker +* **id:** The ID number of this marker as it is identified in the database +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker +* **User:** The user that owns this marker +* **User.id** The ID number of the user that created this marker +* **User.username** The username of the user that created this marker ##### Constraints and Error Handling @@ -327,10 +330,10 @@ provided authorization token. } ``` -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker (optional) +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker (optional) ##### Expected Response @@ -351,15 +354,15 @@ provided authorization token. } } ``` -**success:** Whether or not creation succeeded -**marker.id:** The ID number of this marker as it is identified in the database -**marker.latitude:** The latitude value of the marker -**marker.longitude:** The longitude value of the marker -**marker.name:** The name for the marker -**marker.description:** A description for the marker -**marker.UserId** The ID number of the user that created this marker -**marker.updatedAt** The date this object was last updated -**marker.createdAt** The date this object was first created +* **success:** Whether or not creation succeeded +* **marker.id:** The ID number of this marker as it is identified in the database +* **marker.latitude:** The latitude value of the marker +* **marker.longitude:** The longitude value of the marker +* **marker.name:** The name for the marker +* **marker.description:** A description for the marker +* **marker.UserId** The ID number of the user that created this marker +* **marker.updatedAt** The date this object was last updated +* **marker.createdAt** The date this object was first created ###### Failure Case @@ -373,8 +376,8 @@ This occurs if the error is validation based } } ``` -**success:** Whether or not creation succeeded -**error.message:** A description of the error that has occurred +* **success:** Whether or not creation succeeded +* **error.message:** A description of the error that has occurred ##### Constraints and Error Handling @@ -388,6 +391,7 @@ This occurs if the error is validation based `PUT apiV1/markers/:id` **Where :id = ID of marker to edit** + **Authorization: Required** Edits the marker with the provided ID, but only if the authorized user owns it. @@ -405,10 +409,10 @@ The format is flexible; you only need to provide the properties that will be cha } ``` -**latitude:** The latitude value of the marker -**longitude:** The longitude value of the marker -**name:** The name for the marker -**description:** A description for the marker +* **latitude:** The latitude value of the marker +* **longitude:** The longitude value of the marker +* **name:** The name for the marker +* **description:** A description for the marker ##### Expected Response @@ -440,9 +444,9 @@ The format is flexible; you only need to provide the properties that will be cha } ``` -**success:** Whether or not editing succeeded -**editedFrom:** The state of the marker before it was edited -**editedTo:** The state of the marker after being edited +* **success:** Whether or not editing succeeded +* **editedFrom:** The state of the marker before it was edited +* **editedTo:** The state of the marker after being edited ###### Failure Case @@ -456,8 +460,8 @@ This occurs if the error is validation based } } ``` -**success:** Whether or not editing succeeded -**error.message:** A description of the error that has occurred +* **success:** Whether or not editing succeeded +* **error.message:** A description of the error that has occurred ##### Constraints and Error Handling @@ -472,6 +476,7 @@ This occurs if the error is validation based `DELETE apiV1/markers/:id` **Where :id = The ID of the marker to be deleted** + **Authorization: Required** If the authorized user owns the specified marker, it will be deleted @@ -500,16 +505,16 @@ No request body } ``` -**success:** Whether or not deletion succeeded -**deleted:** The object that was deleted -**deleted.id:** The ID number of this marker as it is identified in the database -**deleted.latitude:** The latitude value of the marker -**deleted.longitude:** The longitude value of the marker -**deleted.name:** The name for the marker -**deleted.description:** A description for the marker -**deleted.UserId** The ID number of the user that created this marker -**deleted.updatedAt** The date this object was last updated -**deleted.createdAt** The date this object was first created +* **success:** Whether or not deletion succeeded +* **deleted:** The object that was deleted +* **deleted.id:** The ID number of this marker as it is identified in the database +* **deleted.latitude:** The latitude value of the marker +* **deleted.longitude:** The longitude value of the marker +* **deleted.name:** The name for the marker +* **deleted.description:** A description for the marker +* **deleted.UserId** The ID number of the user that created this marker +* **deleted.updatedAt** The date this object was last updated +* **deleted.createdAt** The date this object was first created ###### Failure Case @@ -523,8 +528,8 @@ This occurs if the error is validation based } } ``` -**success:** Whether or not deletion succeeded -**error.message:** A description of the error that has occurred +* **success:** Whether or not deletion succeeded +* **error.message:** A description of the error that has occurred ##### Constraints and Error Handling From 305100de2b4af26bd950f8d428bd6b49f1a58cdc Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sun, 7 Apr 2019 18:50:18 -0400 Subject: [PATCH 17/28] Another quick edit --- GhoulAlert/routes/apiV1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GhoulAlert/routes/apiV1/README.md b/GhoulAlert/routes/apiV1/README.md index cd6917e..2f8a92f 100644 --- a/GhoulAlert/routes/apiV1/README.md +++ b/GhoulAlert/routes/apiV1/README.md @@ -29,7 +29,7 @@ in the format **Token [token here]**. These are the registered users of our application. Users only need to register to create, edit, and delete markers; simply viewing markers is accessible to anyone. -###$ Create User +#### Create User `POST apiV1/users` From 884d67117e144bb7bccc827dc9e6a3de05bcc483 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Sun, 7 Apr 2019 18:53:43 -0400 Subject: [PATCH 18/28] More edits --- GhoulAlert/routes/apiV1/README.md | 100 +++++++++++++++--------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/GhoulAlert/routes/apiV1/README.md b/GhoulAlert/routes/apiV1/README.md index 2f8a92f..40d83c3 100644 --- a/GhoulAlert/routes/apiV1/README.md +++ b/GhoulAlert/routes/apiV1/README.md @@ -9,7 +9,7 @@ application responds with JSON-formatted data. In addition to allowing the appli to "speak" with itself, this also allows other applications to receive some of this application's data for their own use. -## Authorization +# Authorization Some routes will be marked with **Authorization: Required**. This means that a special token assigned to identify users is necessary in requests, or the application will @@ -22,14 +22,14 @@ current user route. Tokens will be valid for 30 days from when they are created/ This token should be attached to a header named **Authorization**, in the format **Token [token here]**. -## Paths +# Paths -### Users +## Users These are the registered users of our application. Users only need to register to create, edit, and delete markers; simply viewing markers is accessible to anyone. -#### Create User +### Create User `POST apiV1/users` @@ -38,7 +38,7 @@ create, edit, and delete markers; simply viewing markers is accessible to anyone Makes a new user with the provided username and password, and responds with a token to be used for authorization. -##### Expected Request Format +#### Expected Request Format ``` { @@ -50,7 +50,7 @@ to be used for authorization. * **username:** The user's username * **password:** The password that this user will use to confirm their identity -##### Expected Response +#### Expected Response ``` { @@ -64,12 +64,12 @@ to be used for authorization. * **username:** The user's username * **token:** The token that identifies the user in requests requiring authorization -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error when username or password is not provided * Responds with a validation error if username is not unique (user with that username already exists) -#### User Login +### User Login `POST apiV1/users/login` @@ -78,7 +78,7 @@ to be used for authorization. Refreshes and responds with a new identifying token for the user with the provided username and password. -##### Expected Request Format +#### Expected Request Format ``` { @@ -90,7 +90,7 @@ username and password. * **username:** The user's username * **password:** The password that this user will use to confirm their identity -##### Expected Response +#### Expected Response ``` { @@ -104,13 +104,13 @@ username and password. * **username:** The user's username * **token:** The token that identifies the user in requests requiring authorization -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error when username or password is not provided * Responds with an error if no user exists with given username * Responds with an error if the given password for the given user is wrong -#### Current User +### Current User `GET apiV1/users/current` @@ -118,11 +118,11 @@ username and password. Responds with data for the current user based on the provided authentication token. -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response ``` { @@ -136,17 +136,17 @@ No request body * **username:** The user's username * **token:** The token that identifies the user in requests requiring authorization -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error if there is no authentication token * Responds with an error if the authentication token does not map to a user -### Markers +## Markers These represent points on the Google Map that users can set. The database stores relevant information in these objects and sends it to the map service to be processed onto the map. -#### All Markers +### All Markers `GET apiV1/markers` @@ -154,11 +154,11 @@ information in these objects and sends it to the map service to be processed ont Responds with an array containing all stored markers. -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response ``` [ @@ -181,11 +181,11 @@ No request body * **description:** A description for the marker * **UserId:** The ID number of the user that created this marker -##### Constraints and Error Handling +#### Constraints and Error Handling N/A -#### All Markers (And User Data) +### All Markers (And User Data) `GET apiV1/markers/withusers` @@ -193,11 +193,11 @@ N/A Similar to the above, but includes user data (primarily the username of the owner) -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response ``` [ @@ -225,11 +225,11 @@ No request body * **User.id** The ID number of the user that created this marker * **User.username** The username of the user that created this marker -##### Constraints and Error Handling +#### Constraints and Error Handling N/A -#### Get One Marker +### Get One Marker `GET apiV1/markers/:id` @@ -239,11 +239,11 @@ N/A Obtains the data for one marker by its ID -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response ``` { @@ -263,11 +263,11 @@ No request body * **description:** A description for the marker * **UserId** The ID number of the user that created this marker -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with a 404 error if there is no marker with the given id -#### Get One Marker (And User Data) +### Get One Marker (And User Data) `GET apiV1/markers/:id/withuser` @@ -277,11 +277,11 @@ No request body Same as above, but includes user data such as their username -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response ``` { @@ -306,11 +306,11 @@ No request body * **User.id** The ID number of the user that created this marker * **User.username** The username of the user that created this marker -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with a 404 error if there is no marker with the given id -#### Create a Marker +### Create a Marker `POST apiV1/markers` @@ -319,7 +319,7 @@ No request body Creates a new marker with the given parameters, owned by the user that owns the provided authorization token. -##### Expected Request Format +#### Expected Request Format ``` { @@ -335,9 +335,9 @@ provided authorization token. * **name:** The name for the marker * **description:** A description for the marker (optional) -##### Expected Response +#### Expected Response -###### Success Case +##### Success Case ``` { @@ -364,7 +364,7 @@ provided authorization token. * **marker.updatedAt** The date this object was last updated * **marker.createdAt** The date this object was first created -###### Failure Case +##### Failure Case This occurs if the error is validation based @@ -379,14 +379,14 @@ This occurs if the error is validation based * **success:** Whether or not creation succeeded * **error.message:** A description of the error that has occurred -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error if marker latitude, longitude, or name are not provided * Responds with an error if there is no authorization token * Responds with an error if the authorization token does not match a user * Responds with an error if the name of the marker is not unique -#### Edit a Marker +### Edit a Marker `PUT apiV1/markers/:id` @@ -396,7 +396,7 @@ This occurs if the error is validation based Edits the marker with the provided ID, but only if the authorized user owns it. -##### Expected Request Format +#### Expected Request Format The format is flexible; you only need to provide the properties that will be changed. @@ -414,9 +414,9 @@ The format is flexible; you only need to provide the properties that will be cha * **name:** The name for the marker * **description:** A description for the marker -##### Expected Response +#### Expected Response -###### Success Case +##### Success Case ``` { @@ -448,7 +448,7 @@ The format is flexible; you only need to provide the properties that will be cha * **editedFrom:** The state of the marker before it was edited * **editedTo:** The state of the marker after being edited -###### Failure Case +##### Failure Case This occurs if the error is validation based @@ -463,7 +463,7 @@ This occurs if the error is validation based * **success:** Whether or not editing succeeded * **error.message:** A description of the error that has occurred -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error if there is no authentication token * Responds with an error if the authentication token does not match to a user @@ -471,7 +471,7 @@ This occurs if the error is validation based * Responds with an error if the user does not own the marker * Responds with an error if the name is edited to one that already exists in the database -#### Delete a Marker +### Delete a Marker `DELETE apiV1/markers/:id` @@ -481,13 +481,13 @@ This occurs if the error is validation based If the authorized user owns the specified marker, it will be deleted -##### Expected Request Format +#### Expected Request Format No request body -##### Expected Response +#### Expected Response -###### Success Case +##### Success Case ``` { @@ -516,7 +516,7 @@ No request body * **deleted.updatedAt** The date this object was last updated * **deleted.createdAt** The date this object was first created -###### Failure Case +##### Failure Case This occurs if the error is validation based @@ -531,7 +531,7 @@ This occurs if the error is validation based * **success:** Whether or not deletion succeeded * **error.message:** A description of the error that has occurred -##### Constraints and Error Handling +#### Constraints and Error Handling * Responds with an error if there is no authentication token * Responds with an error if the authentication token does not match to a user From 2bd1ca126f937cdd6734251d261cc479eb0dc674 Mon Sep 17 00:00:00 2001 From: CaroRoquita <47201096+CaroRoquita@users.noreply.github.com> Date: Fri, 12 Apr 2019 13:36:41 -0400 Subject: [PATCH 19/28] Create index2.pug This has the homepage of the App, with the following sections & elements: > Google Map with top 31 haunted places in America. > Log In Option for members > Sign Up Option for new members --- GhoulAlert/views/index2.pug | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 GhoulAlert/views/index2.pug diff --git a/GhoulAlert/views/index2.pug b/GhoulAlert/views/index2.pug new file mode 100644 index 0000000..b23a299 --- /dev/null +++ b/GhoulAlert/views/index2.pug @@ -0,0 +1,118 @@ +extends layout +block content + head + meta(name='viewport', content='width=device-width, initial-scale=1') + script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js') + body + h3 Welcome to Ghoul Alert App + iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') + section + button(onclick="document.getElementById('id01').style.display='block'", style='width:auto;') LOG IN + #id01.modal + form.modal-content.animate(action='/UserProfile.pug') + h3 Member Log In + .imgcontainer + span.close(onclick="document.getElementById('id01').style.display='none'", title='Close Modal') × + .container + label(for='uname') + b Username + input#username(type='text', placeholder='Enter Username', name='uname', required='') + label(for='psw') + b Password + input#password(type='password', placeholder='Enter Password', name='psw', required='') + button#login(type='submit') Login + label + input(type='checkbox', checked='checked', name='remember') + | Remember me + .container(style='background-color:#f1f1f1;') + button.cancelbtn(type='button', onclick="document.getElementById('id01').style.display='none'") Cancel + span.psw + | Forgot + a(href='#') password? + script. + // Get the modal + var modal = document.getElementById('id01'); + // When the user clicks anywhere outside of the modal, close it + window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } + } + script. + $(document).ready(function () { + $("#login").click(function (e) { + $.ajax({ + type: "POST", + url: "apiV1/users/login", + contentType: "application/json; charset=utf-8", + data: '{"username":"' + $("#username").val() + '","password":"' + $("#password").val() + '"}', + dataType: "json", + success: function (result, status, xhr) { + if (result.d == "Success") { + $("#messageSpan").text("Login Successful, Redireting to your profile page."); + setTimeout(function () { window.location = "UserProfile.html"; }, 2000); + } + else + $("#messageSpan").text("Login failed, Please try again."); + }, + error: function (xhr, status, error) { + $("#dbData").html("Result: " + status + " " + error + " " + xhr.status + " " + xhr.statusText) + } + }); + }); + }); + section + button(onclick="document.getElementById('id02').style.display='block'", style='width:auto;') SIGN UP + #id02.modal + form.modal-content.animate(action='/UserProfile.pug') + h3 Member Sign Up + .imgcontainer + span.close(onclick="document.getElementById('id02').style.display='none'", title='Close Modal') × + .container + label(for='uname') + b Username + input#username(type='text', placeholder='Enter Username', name='uname', required='') + label(for='psw') + b Password + input#password(type='password', placeholder='Enter Password', name='psw', required='') + button(type='submit') Sign Up + label + input(type='checkbox', checked='checked', name='remember') + | Remember me + .container(style='background-color:#f1f1f1;') + button.cancelbtn(type='button', onclick="document.getElementById('id02').style.display='none'") Cancel + span.psw + | Forgot + a(href='#') password? + script. + // Get the modal + var modal = document.getElementById('id02'); + // When the user clicks anywhere outside of the modal, close it + window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } + } + script. + $(document).ready(function () { + $("#submit").click(function (e) { + $.ajax({ + type: "POST", + url: "apiV1/users", + contentType: "application/json; charset=utf-8", + data: '{"username":"' + $("#username").val() + '","password":"' + $("#password").val() + '"}', + dataType: "json", + success: function (result, status, xhr) { + if (result.d == "Success") { + $("#messageSpan").text("Login Successful, Redireting to your profile page."); + setTimeout(function () { window.location = "UserProfile.html"; }, 2000); + } + else + $("#messageSpan").text("Login failed, Please try again."); + }, + error: function (xhr, status, error) { + $("#dbData").html("Result: " + status + " " + error + " " + xhr.status + " " + xhr.statusText) + } + }); + }); + }); From af3095f4ac7c864ca2d373625afff617b746e089 Mon Sep 17 00:00:00 2001 From: CaroRoquita <47201096+CaroRoquita@users.noreply.github.com> Date: Fri, 12 Apr 2019 13:37:55 -0400 Subject: [PATCH 20/28] Create UserProfile.pug This has the frontend section and elements for the User Profile. --- GhoulAlert/views/UserProfile.pug | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 GhoulAlert/views/UserProfile.pug diff --git a/GhoulAlert/views/UserProfile.pug b/GhoulAlert/views/UserProfile.pug new file mode 100644 index 0000000..4b37653 --- /dev/null +++ b/GhoulAlert/views/UserProfile.pug @@ -0,0 +1,53 @@ +extends layout +block content + head + meta(name='viewport', content='width=device-width, initial-scale=1') + script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js') + body + h3 Welcome to Ghoul Alert App + section + iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') + section + button(onclick="document.getElementById('id01').style.display='block'", style='width:auto;') ADD MARKER + #id01.modal + form.modal-content.animate(action='/') + h3 Adding New Marker + .imgcontainer + span.close(onclick="document.getElementById('id01').style.display='none'", title='Close Modal') × + .container + label(for='name') + b Marker Name + input#markerName(type='text', placeholder='Enter name for Marker', name='name', required='') + label(for='description') + b Description + input#description(type='text', placeholder='Enter description', name='description', required='') + button#add(type='submit') CREATE + .container(style='background-color:#f1f1f1;') + button.cancelbtn(type='button', onclick="document.getElementById('id01').style.display='none'") Cancel + script. + // Get the modal + var modal = document.getElementById('id01'); + // When the user clicks anywhere outside of the modal, close it + window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } + } + script. + $(document).ready(function () { + $("#login").click(function (e) { + $.ajax({ + type: "POST", + url: "apiV1/users/login", + contentType: "application/json; charset=utf-8", + data: '{"markerName":"' + $("#markerName").val() + '","description":"' + $("#description").val() + '"}', + dataType: "json", + success: function (result, status, xhr) { + }, + error: function (xhr, status, error) { + e.default(); + } + }); + }); + }); + section From 31d1a60305993ac26158f42645b3770c3d1094b3 Mon Sep 17 00:00:00 2001 From: rkinoshi <49567913+rkinoshi@users.noreply.github.com> Date: Fri, 12 Apr 2019 14:34:10 -0400 Subject: [PATCH 21/28] Create style.css This covers the style for index2.pug and UserProfile.pug. --- style.css | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 style.css diff --git a/style.css b/style.css new file mode 100644 index 0000000..7c27227 --- /dev/null +++ b/style.css @@ -0,0 +1,115 @@ +body {text-align:center; font-family: Arial, Helvetica, sans-serif; background-color:black;} + +/* #808080 h4 { color: #EADD84; font-family: 'Rouge Script', cursive; font-size: 70px; font-weight: normal; line-height: 48px; margin: 0 0 50px; text-align: center; text-shadow: 1px 1px 2px #082b34; }*/ + +/*h4 { color: #EADD84; font-family: "Great Vibes", cursive; font-size: 65px; line-height: 160px; font-weight: normal; margin-bottom: 0px; margin-top: 40px; text-align: center; text-shadow: 0 1px 1px #fff; }*/ + +h3, h1 { color: #EADD84; font-family: 'Rouge Script', sans-serif; font-size: 45px; font-weight: bold; letter-spacing: -1px; line-height: 1; text-align: center; } + +section { + float: center; +} +aside { + float: center; +} + +/* Full-width input fields */ +input[type=text], input[type=password] { + width: 100%; + padding: 12px 20px; + margin: 8px; + display: inline-block; + border: 1px solid #EADD84; + box-sizing: border-box; +} + +/* Set a style for all buttons */ +button { + background-color: #EADD84; + color: white; + padding: 14px 20px; + margin: 8px 0; + border: none; + width: 100%; + position: centered; +} + +button:hover { + opacity: 0.8; +} + +/* Extra styles for the cancel button */ +.cancelbtn { + width: auto; + padding: 10px 18px; + background-color: #A9A9A9; +} + +.container { + padding: 16px; +} + +span.psw { + float: right; + padding-top: 16px; +} + +/* The Modal (background) */ +.modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 1; /* Sit on top */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ + padding-top: 60px; +} + +/* Modal Content/Box */ +.modal-content { + background-color: #fefefe; + margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */ + border: 1px solid #EADD84; + width: 80%; /* Could be more or less, depending on screen size */ +} + +/* The Close Button (x) */ +.close { + position: absolute; + right: 25px; + top: 0; + color: #A9A9A9; + font-size: 35px; + font-weight: bold; +} + +/* Add Zoom Animation */ +.animate { + -webkit-animation: animatezoom 0.6s; + animation: animatezoom 0.6s +} + +@-webkit-keyframes animatezoom { + from {-webkit-transform: scale(0)} + to {-webkit-transform: scale(1)} +} + +@keyframes animatezoom { + from {transform: scale(0)} + to {transform: scale(1)} +} + +/* Change styles for span and cancel button on extra small screens */ +@media screen and (max-width: 300px) { + span.psw { + display: block; + float: none; + } + .cancelbtn { + width: 100%; + } +} From bc1c06b9e36a57581dbd7a6aff0c85311e0fd26b Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 14:48:53 -0400 Subject: [PATCH 22/28] Move style.css to directory --- GhoulAlert/package-lock.json | 41 ++++++--- GhoulAlert/public/stylesheets/style.css | 117 +++++++++++++++++++++++- style.css | 115 ----------------------- 3 files changed, 142 insertions(+), 131 deletions(-) delete mode 100644 style.css diff --git a/GhoulAlert/package-lock.json b/GhoulAlert/package-lock.json index 8859cfa..5de3660 100644 --- a/GhoulAlert/package-lock.json +++ b/GhoulAlert/package-lock.json @@ -1221,7 +1221,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1242,12 +1243,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1262,17 +1265,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1389,7 +1395,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1401,6 +1408,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1415,6 +1423,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1422,12 +1431,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1446,6 +1457,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1526,7 +1538,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1538,6 +1551,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1623,7 +1637,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1659,6 +1674,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1678,6 +1694,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1721,12 +1738,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/GhoulAlert/public/stylesheets/style.css b/GhoulAlert/public/stylesheets/style.css index 9453385..7c27227 100644 --- a/GhoulAlert/public/stylesheets/style.css +++ b/GhoulAlert/public/stylesheets/style.css @@ -1,8 +1,115 @@ -body { - padding: 50px; - font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +body {text-align:center; font-family: Arial, Helvetica, sans-serif; background-color:black;} + +/* #808080 h4 { color: #EADD84; font-family: 'Rouge Script', cursive; font-size: 70px; font-weight: normal; line-height: 48px; margin: 0 0 50px; text-align: center; text-shadow: 1px 1px 2px #082b34; }*/ + +/*h4 { color: #EADD84; font-family: "Great Vibes", cursive; font-size: 65px; line-height: 160px; font-weight: normal; margin-bottom: 0px; margin-top: 40px; text-align: center; text-shadow: 0 1px 1px #fff; }*/ + +h3, h1 { color: #EADD84; font-family: 'Rouge Script', sans-serif; font-size: 45px; font-weight: bold; letter-spacing: -1px; line-height: 1; text-align: center; } + +section { + float: center; +} +aside { + float: center; +} + +/* Full-width input fields */ +input[type=text], input[type=password] { + width: 100%; + padding: 12px 20px; + margin: 8px; + display: inline-block; + border: 1px solid #EADD84; + box-sizing: border-box; +} + +/* Set a style for all buttons */ +button { + background-color: #EADD84; + color: white; + padding: 14px 20px; + margin: 8px 0; + border: none; + width: 100%; + position: centered; +} + +button:hover { + opacity: 0.8; +} + +/* Extra styles for the cancel button */ +.cancelbtn { + width: auto; + padding: 10px 18px; + background-color: #A9A9A9; +} + +.container { + padding: 16px; +} + +span.psw { + float: right; + padding-top: 16px; +} + +/* The Modal (background) */ +.modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 1; /* Sit on top */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ + padding-top: 60px; +} + +/* Modal Content/Box */ +.modal-content { + background-color: #fefefe; + margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */ + border: 1px solid #EADD84; + width: 80%; /* Could be more or less, depending on screen size */ +} + +/* The Close Button (x) */ +.close { + position: absolute; + right: 25px; + top: 0; + color: #A9A9A9; + font-size: 35px; + font-weight: bold; +} + +/* Add Zoom Animation */ +.animate { + -webkit-animation: animatezoom 0.6s; + animation: animatezoom 0.6s +} + +@-webkit-keyframes animatezoom { + from {-webkit-transform: scale(0)} + to {-webkit-transform: scale(1)} +} + +@keyframes animatezoom { + from {transform: scale(0)} + to {transform: scale(1)} } -a { - color: #00B7FF; +/* Change styles for span and cancel button on extra small screens */ +@media screen and (max-width: 300px) { + span.psw { + display: block; + float: none; + } + .cancelbtn { + width: 100%; + } } diff --git a/style.css b/style.css deleted file mode 100644 index 7c27227..0000000 --- a/style.css +++ /dev/null @@ -1,115 +0,0 @@ -body {text-align:center; font-family: Arial, Helvetica, sans-serif; background-color:black;} - -/* #808080 h4 { color: #EADD84; font-family: 'Rouge Script', cursive; font-size: 70px; font-weight: normal; line-height: 48px; margin: 0 0 50px; text-align: center; text-shadow: 1px 1px 2px #082b34; }*/ - -/*h4 { color: #EADD84; font-family: "Great Vibes", cursive; font-size: 65px; line-height: 160px; font-weight: normal; margin-bottom: 0px; margin-top: 40px; text-align: center; text-shadow: 0 1px 1px #fff; }*/ - -h3, h1 { color: #EADD84; font-family: 'Rouge Script', sans-serif; font-size: 45px; font-weight: bold; letter-spacing: -1px; line-height: 1; text-align: center; } - -section { - float: center; -} -aside { - float: center; -} - -/* Full-width input fields */ -input[type=text], input[type=password] { - width: 100%; - padding: 12px 20px; - margin: 8px; - display: inline-block; - border: 1px solid #EADD84; - box-sizing: border-box; -} - -/* Set a style for all buttons */ -button { - background-color: #EADD84; - color: white; - padding: 14px 20px; - margin: 8px 0; - border: none; - width: 100%; - position: centered; -} - -button:hover { - opacity: 0.8; -} - -/* Extra styles for the cancel button */ -.cancelbtn { - width: auto; - padding: 10px 18px; - background-color: #A9A9A9; -} - -.container { - padding: 16px; -} - -span.psw { - float: right; - padding-top: 16px; -} - -/* The Modal (background) */ -.modal { - display: none; /* Hidden by default */ - position: fixed; /* Stay in place */ - z-index: 1; /* Sit on top */ - left: 0; - top: 0; - width: 100%; /* Full width */ - height: 100%; /* Full height */ - overflow: auto; /* Enable scroll if needed */ - background-color: rgb(0,0,0); /* Fallback color */ - background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ - padding-top: 60px; -} - -/* Modal Content/Box */ -.modal-content { - background-color: #fefefe; - margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */ - border: 1px solid #EADD84; - width: 80%; /* Could be more or less, depending on screen size */ -} - -/* The Close Button (x) */ -.close { - position: absolute; - right: 25px; - top: 0; - color: #A9A9A9; - font-size: 35px; - font-weight: bold; -} - -/* Add Zoom Animation */ -.animate { - -webkit-animation: animatezoom 0.6s; - animation: animatezoom 0.6s -} - -@-webkit-keyframes animatezoom { - from {-webkit-transform: scale(0)} - to {-webkit-transform: scale(1)} -} - -@keyframes animatezoom { - from {transform: scale(0)} - to {transform: scale(1)} -} - -/* Change styles for span and cancel button on extra small screens */ -@media screen and (max-width: 300px) { - span.psw { - display: block; - float: none; - } - .cancelbtn { - width: 100%; - } -} From 60544951fccd6c0875ead9c5eb6898fe7f3e5097 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 19:11:18 -0400 Subject: [PATCH 23/28] User login system implemented on frontend --- GhoulAlert/public/javascripts/hello.js | 1 + GhoulAlert/public/javascripts/js.cookie.js | 165 +++++++++++++++++++ GhoulAlert/public/javascripts/main.js | 178 +++++++++++++++++++++ GhoulAlert/public/stylesheets/style.css | 109 ++++++++++++- GhoulAlert/views/UserProfile.pug | 53 ------ GhoulAlert/views/index.pug | 92 ++++++++++- GhoulAlert/views/index_temp.pug | 9 ++ 7 files changed, 543 insertions(+), 64 deletions(-) create mode 100644 GhoulAlert/public/javascripts/hello.js create mode 100644 GhoulAlert/public/javascripts/js.cookie.js create mode 100644 GhoulAlert/public/javascripts/main.js delete mode 100644 GhoulAlert/views/UserProfile.pug create mode 100644 GhoulAlert/views/index_temp.pug diff --git a/GhoulAlert/public/javascripts/hello.js b/GhoulAlert/public/javascripts/hello.js new file mode 100644 index 0000000..bf6b817 --- /dev/null +++ b/GhoulAlert/public/javascripts/hello.js @@ -0,0 +1 @@ +console.log("Hello"); diff --git a/GhoulAlert/public/javascripts/js.cookie.js b/GhoulAlert/public/javascripts/js.cookie.js new file mode 100644 index 0000000..9a0945e --- /dev/null +++ b/GhoulAlert/public/javascripts/js.cookie.js @@ -0,0 +1,165 @@ +/*! + * JavaScript Cookie v2.2.0 + * https://github.com/js-cookie/js-cookie + * + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack + * Released under the MIT license + */ +;(function (factory) { + var registeredInModuleLoader = false; + if (typeof define === 'function' && define.amd) { + define(factory); + registeredInModuleLoader = true; + } + if (typeof exports === 'object') { + module.exports = factory(); + registeredInModuleLoader = true; + } + if (!registeredInModuleLoader) { + var OldCookies = window.Cookies; + var api = window.Cookies = factory(); + api.noConflict = function () { + window.Cookies = OldCookies; + return api; + }; + } +}(function () { + function extend () { + var i = 0; + var result = {}; + for (; i < arguments.length; i++) { + var attributes = arguments[ i ]; + for (var key in attributes) { + result[key] = attributes[key]; + } + } + return result; + } + + function init (converter) { + function api (key, value, attributes) { + var result; + if (typeof document === 'undefined') { + return; + } + + // Write + + if (arguments.length > 1) { + attributes = extend({ + path: '/' + }, api.defaults, attributes); + + if (typeof attributes.expires === 'number') { + var expires = new Date(); + expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); + attributes.expires = expires; + } + + // We're using "expires" because "max-age" is not supported by IE + attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; + + try { + result = JSON.stringify(value); + if (/^[\{\[]/.test(result)) { + value = result; + } + } catch (e) {} + + if (!converter.write) { + value = encodeURIComponent(String(value)) + .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); + } else { + value = converter.write(value, key); + } + + key = encodeURIComponent(String(key)); + key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); + key = key.replace(/[\(\)]/g, escape); + + var stringifiedAttributes = ''; + + for (var attributeName in attributes) { + if (!attributes[attributeName]) { + continue; + } + stringifiedAttributes += '; ' + attributeName; + if (attributes[attributeName] === true) { + continue; + } + stringifiedAttributes += '=' + attributes[attributeName]; + } + return (document.cookie = key + '=' + value + stringifiedAttributes); + } + + // Read + + if (!key) { + result = {}; + } + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling "get()" + var cookies = document.cookie ? document.cookie.split('; ') : []; + var rdecode = /(%[0-9A-Z]{2})+/g; + var i = 0; + + for (; i < cookies.length; i++) { + var parts = cookies[i].split('='); + var cookie = parts.slice(1).join('='); + + if (!this.json && cookie.charAt(0) === '"') { + cookie = cookie.slice(1, -1); + } + + try { + var name = parts[0].replace(rdecode, decodeURIComponent); + cookie = converter.read ? + converter.read(cookie, name) : converter(cookie, name) || + cookie.replace(rdecode, decodeURIComponent); + + if (this.json) { + try { + cookie = JSON.parse(cookie); + } catch (e) {} + } + + if (key === name) { + result = cookie; + break; + } + + if (!key) { + result[name] = cookie; + } + } catch (e) {} + } + + return result; + } + + api.set = api; + api.get = function (key) { + return api.call(api, key); + }; + api.getJSON = function () { + return api.apply({ + json: true + }, [].slice.call(arguments)); + }; + api.defaults = {}; + + api.remove = function (key, attributes) { + api(key, '', extend(attributes, { + expires: -1 + })); + }; + + api.withConverter = init; + + return api; + } + + return init(function () {}); +})); diff --git a/GhoulAlert/public/javascripts/main.js b/GhoulAlert/public/javascripts/main.js new file mode 100644 index 0000000..5189b24 --- /dev/null +++ b/GhoulAlert/public/javascripts/main.js @@ -0,0 +1,178 @@ +$(document).ready(() => { + // Message functions + var msg = { + showAlert: function(message) { + $(".alert-message").text(message); + $(".alert").slideDown(); + }, + + showAffirmation: function(message) { + $(".affirmation-message").text(message); + $(".affirmation").slideDown(); + }, + + closeAlert: function() { + $(".alert-message").text(""); + $(".alert").slideUp(); + }, + + closeAffirmation: function() { + $(".affirmation-message").text(""); + $(".affirmation").slideUp(); + } + } + + // User data & functions + var user = { + username: null, + token: null, + isLogged: false, + + loadFromCookie: function() { + if (Cookies.get('username') && Cookies.get('token')) { + this.username = Cookies.get('username'); + this.token = Cookies.get('token'); + this.isLogged = true; + $(".login-name").text(this.username); + $(".no-login").hide(); + $(".has-login").show(); + } + }, + + loadData: function(data) { + this.username = data.username; + this.token = data.token; + this.isLogged = true; + + Cookies.set("username", this.username, { expires: 30 }); + Cookies.set("token", this.token, { expires: 30 }); + }, + + handleServerErrors: function(response) { + var msg_string = ""; + if (response.errors) { + Object.keys(response.errors).forEach( (key, idx) => { + msg_string += key + " " + response.errors[key] + ". "; + }); + } else if (response.error) { + msg_string = response.error.message; + } else { + msg_string = "Failed user operations. Try again."; + } + + msg.showAlert(msg_string); + }, + + login: function(username, password) { + $.post('apiV1/users/login', { username: username, password: password }, (data) => { + if (data.error || data.errors) { + this.handleServerErrors(data); + return false; + } else { + this.loadData(data); + + msg.showAffirmation("Welcome back, " + this.username + "!"); + + $(".login-name").text(this.username); + $(".use-login").hide(); + $(".has-login").show(); + + return true; + } + }).fail((data) => { + var response = data.responseJSON; + + this.handleServerErrors(response); + + return false; + }); + }, + + logout: function() { + this.username = null; + this.token = null; + this.isLogged = false; + $(".login-name").text(""); + Cookies.remove('username'); + Cookies.remove('token'); + }, + + create: function(username, password) { + $.post('apiV1/users', { username: username, password: password }, (data) => { + if (data.error || data.errors) { + this.handleServerErrors(data); + return false; + } else { + this.loadData(data); + + msg.showAffirmation("Welcome, " + this.username + "!"); + + $(".login-name").text(this.username); + $(".create-login").hide(); + $(".has-login").show(); + + return true; + } + }).fail((data) => { + var response = data.responseJSON; + + this.handleServerErrors(response); + + return false; + }); + } + + }; + + user.loadFromCookie(); + + // Input Handling + $("#create-login-button").click((e) => { + $(".no-login").hide(); + $(".create-login").show(); + }); + + $("#use-login-button").click((e) => { + $(".no-login").hide(); + $(".use-login").show(); + }); + + $("#destroy-login-button").click((e) => { + user.logout(); + $(".has-login").hide(); + $(".no-login").show(); + }); + + $("#new-user-submit").click((e) => { + e.preventDefault(); + var username = $("#new-user-username").val(); + var password = $("#new-user-password").val(); + user.create(username, password); + }); + + $("#new-user-cancel").click((e) => { + $(".create-login").hide(); + $(".no-login").show(); + }); + + $("#existing-user-submit").click((e) => { + e.preventDefault(); + var username = $("#existing-user-username").val(); + var password = $("#existing-user-password").val(); + user.login(username, password); + }); + + $("#existing-user-cancel").click((e) => { + $(".use-login").hide(); + $(".no-login").show(); + }); + + $(".close-alert").click((e) => { + msg.closeAlert(); + }); + + $(".close-affirmation").click((e) => { + msg.closeAffirmation(); + }); + +}); diff --git a/GhoulAlert/public/stylesheets/style.css b/GhoulAlert/public/stylesheets/style.css index 7c27227..31a9af5 100644 --- a/GhoulAlert/public/stylesheets/style.css +++ b/GhoulAlert/public/stylesheets/style.css @@ -23,6 +23,13 @@ input[type=text], input[type=password] { box-sizing: border-box; } +.small_input { + max-width: 128px; + max-height: 24px; + margin: 0; + margin-right: 8px; +} + /* Set a style for all buttons */ button { background-color: #EADD84; @@ -38,6 +45,102 @@ button:hover { opacity: 0.8; } +button.small_button { + max-width: 128px; + max-height: 32px; + padding: 8px 12px; + margin-right: 8px; + background-color: blue; + border-radius: 1em; + +} + +.inline { + display: inline-block; +} + +/* Navbar styles */ +navbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + background: #4c4c4c; + background: -moz-linear-gradient(top, #4c4c4c 0%, #595959 12%, #666666 25%, #474747 39%, #2c2c2c 50%, #000000 51%, #111111 60%, #2b2b2b 76%, #1c1c1c 91%, #131313 100%); + background: -webkit-linear-gradient(top, #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); + background: linear-gradient(to bottom, #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); + height: 6em; +} + +.title, .login, .login-name { + display:inline-block; +} + +.title { + float: left; + font-size: 2em; + font-weight: bold; + color: white; + padding-top: 0.25em; + padding-left: 0.25em; +} + +.login { + float: right; + width: 50%; + text-align: right; +} + +.has-login, .create-login, .use-login, .alert, .affirmation { + display: none; +} + +.login-name { + color: white; + font-weight: bold; + margin-right: 8px; +} + +/* Message styles */ +#messages { + position: fixed; + top: 6em; + left: 0; + width: 100%; +} + +.alert, .affirmation { + width: 100%; + color: white; + min-height: 4em; +} + +.alert-message, .affirmation-message { + font-size: 1.25em; + vertical-align: middle; + padding: 2em; +} + +.alert { + background-color: red; +} + +.affirmation { + background-color: green; +} + +.close-alert, .close-affirmation { + position: fixed; + top: 8em; + right: 0; +} + +/* Map Styles */ +#map { + margin-top: 6.5em; +} + /* Extra styles for the cancel button */ .cancelbtn { width: auto; @@ -94,12 +197,12 @@ span.psw { } @-webkit-keyframes animatezoom { - from {-webkit-transform: scale(0)} + from {-webkit-transform: scale(0)} to {-webkit-transform: scale(1)} } - + @keyframes animatezoom { - from {transform: scale(0)} + from {transform: scale(0)} to {transform: scale(1)} } diff --git a/GhoulAlert/views/UserProfile.pug b/GhoulAlert/views/UserProfile.pug deleted file mode 100644 index 4b37653..0000000 --- a/GhoulAlert/views/UserProfile.pug +++ /dev/null @@ -1,53 +0,0 @@ -extends layout -block content - head - meta(name='viewport', content='width=device-width, initial-scale=1') - script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js') - body - h3 Welcome to Ghoul Alert App - section - iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') - section - button(onclick="document.getElementById('id01').style.display='block'", style='width:auto;') ADD MARKER - #id01.modal - form.modal-content.animate(action='/') - h3 Adding New Marker - .imgcontainer - span.close(onclick="document.getElementById('id01').style.display='none'", title='Close Modal') × - .container - label(for='name') - b Marker Name - input#markerName(type='text', placeholder='Enter name for Marker', name='name', required='') - label(for='description') - b Description - input#description(type='text', placeholder='Enter description', name='description', required='') - button#add(type='submit') CREATE - .container(style='background-color:#f1f1f1;') - button.cancelbtn(type='button', onclick="document.getElementById('id01').style.display='none'") Cancel - script. - // Get the modal - var modal = document.getElementById('id01'); - // When the user clicks anywhere outside of the modal, close it - window.onclick = function(event) { - if (event.target == modal) { - modal.style.display = "none"; - } - } - script. - $(document).ready(function () { - $("#login").click(function (e) { - $.ajax({ - type: "POST", - url: "apiV1/users/login", - contentType: "application/json; charset=utf-8", - data: '{"markerName":"' + $("#markerName").val() + '","description":"' + $("#description").val() + '"}', - dataType: "json", - success: function (result, status, xhr) { - }, - error: function (xhr, status, error) { - e.default(); - } - }); - }); - }); - section diff --git a/GhoulAlert/views/index.pug b/GhoulAlert/views/index.pug index fef48a8..e583857 100644 --- a/GhoulAlert/views/index.pug +++ b/GhoulAlert/views/index.pug @@ -1,9 +1,85 @@ extends layout - -block content - h1= title - p Welcome to #{title} - h1 Users - ol - each user in users - li= user.username +block content + head + meta(name='viewport', content='width=device-width, initial-scale=1') + script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js') + script(src='javascripts/js.cookie.js') + script(src='javascripts/main.js') + body + navbar.title-login + .title Ghoul Alert + .login + .no-login + button#create-login-button.small_button.inline(type='button') New Account + button#use-login-button.small_button.inline(type='button') Log In + .has-login + .login-name + button#destroy-login-button.small_button(type='button') Log Out + .create-login + form.user-login-create-form + input#new-user-username.small_input.inline(type='text', name='new-username', placeholder='Username', required) + input#new-user-password.small_input.inline(type='password', name='new-password', placeholder='Password', required) + button#new-user-submit.small_button.inline(type='submit') Create User + button#new-user-cancel.small_button.inline(type='button') Cancel + .use-login + form.user-login-use-form + input#existing-user-username.small_input.inline(type='text', name='existing-username', placeholder='Username', required) + input#existing-user-password.small_input.inline(type='password', name='existing-password', placeholder='Password', required) + button#existing-user-submit.small_button.inline(type='submit') Log In + button#existing-user-cancel.small_button.inline(type='button') Cancel + #messages + .alert + .alert-message + button.close-alert.small_button × + .affirmation + .affirmation-message + button.close-affirmation.small_button × + + + section#map + iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') + + section + button(onclick="document.getElementById('id01').style.display='block'", style='width:auto;') ADD MARKER + #id01.modal + form.modal-content.animate(action='/') + h3 Adding New Marker + .imgcontainer + span.close(onclick="document.getElementById('id01').style.display='none'", title='Close Modal') × + .container + label(for='name') + b Marker Name + input#markerName(type='text', placeholder='Enter name for Marker', name='name', required='') + label(for='description') + b Description + input#description(type='text', placeholder='Enter description', name='description', required='') + button#add(type='submit') CREATE + .container(style='background-color:#f1f1f1;') + button.cancelbtn(type='button', onclick="document.getElementById('id01').style.display='none'") Cancel + script. + // Get the modal + var modal = document.getElementById('id01'); + // When the user clicks anywhere outside of the modal, close it + window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } + } + script. + $(document).ready(function () { + $("#login").click(function (e) { + $.ajax({ + type: "POST", + url: "apiV1/users/login", + contentType: "application/json; charset=utf-8", + data: '{"markerName":"' + $("#markerName").val() + '","description":"' + $("#description").val() + '"}', + dataType: "json", + success: function (result, status, xhr) { + }, + error: function (xhr, status, error) { + e.default(); + } + }); + }); + }); + section diff --git a/GhoulAlert/views/index_temp.pug b/GhoulAlert/views/index_temp.pug new file mode 100644 index 0000000..fef48a8 --- /dev/null +++ b/GhoulAlert/views/index_temp.pug @@ -0,0 +1,9 @@ +extends layout + +block content + h1= title + p Welcome to #{title} + h1 Users + ol + each user in users + li= user.username From 1afee1f4ba392275a88d01850274131525eb8bb5 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 19:42:12 -0400 Subject: [PATCH 24/28] Consolidated existing javascript --- GhoulAlert/public/javascripts/main.js | 44 +++++++++++++++++++++++++++ GhoulAlert/views/index.pug | 40 +++++------------------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/GhoulAlert/public/javascripts/main.js b/GhoulAlert/public/javascripts/main.js index 5189b24..2a197be 100644 --- a/GhoulAlert/public/javascripts/main.js +++ b/GhoulAlert/public/javascripts/main.js @@ -126,6 +126,37 @@ $(document).ready(() => { user.loadFromCookie(); + // Marker data and functions + var markers = { + collection: [], + + retrieve: function() { + $.get("apiV1/markers/withusers", (data) => { + if (!(data.error || data.errors)) { + this.collection = data; + } else { + console.log("WARNING: Marker retrieval failure"); + } + }).fail((data) => { + console.log("WARNING: Marker retrieval failure"); + }); + }, + + handleErrorsInModal: function() { + + }, + + create: function(lat, long, name, desc) { + $.post("apiV1/markers", {latitude: latitude, longitude: longitude, name: name, description: desc}, (data) => { + if (data.error || data.errors) { + this.handleErrorsInModal(data); + } + }); + } + }; + + markers.retrieve(); + // Input Handling $("#create-login-button").click((e) => { $(".no-login").hide(); @@ -175,4 +206,17 @@ $(document).ready(() => { msg.closeAffirmation(); }); + $("#add-marker-button").click((e) => { + if (user.isLogged){ + $("#newmarker").show(); + } else { + msg.showAlert("You must be logged in to add a marker."); + } + }); + + $(".cancelbtn").click((e) => { + $("#newmarker").hide(); + }); + + }); diff --git a/GhoulAlert/views/index.pug b/GhoulAlert/views/index.pug index e583857..58e3ca6 100644 --- a/GhoulAlert/views/index.pug +++ b/GhoulAlert/views/index.pug @@ -17,14 +17,14 @@ block content button#destroy-login-button.small_button(type='button') Log Out .create-login form.user-login-create-form - input#new-user-username.small_input.inline(type='text', name='new-username', placeholder='Username', required) - input#new-user-password.small_input.inline(type='password', name='new-password', placeholder='Password', required) + input#new-user-username.small_input.inline(type='text', name='new-username', placeholder='Username', required='') + input#new-user-password.small_input.inline(type='password', name='new-password', placeholder='Password', required='') button#new-user-submit.small_button.inline(type='submit') Create User button#new-user-cancel.small_button.inline(type='button') Cancel .use-login form.user-login-use-form - input#existing-user-username.small_input.inline(type='text', name='existing-username', placeholder='Username', required) - input#existing-user-password.small_input.inline(type='password', name='existing-password', placeholder='Password', required) + input#existing-user-username.small_input.inline(type='text', name='existing-username', placeholder='Username', required='') + input#existing-user-password.small_input.inline(type='password', name='existing-password', placeholder='Password', required='') button#existing-user-submit.small_button.inline(type='submit') Log In button#existing-user-cancel.small_button.inline(type='button') Cancel #messages @@ -40,8 +40,8 @@ block content iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') section - button(onclick="document.getElementById('id01').style.display='block'", style='width:auto;') ADD MARKER - #id01.modal + button#add-marker-button(style='width:auto;') ADD MARKER + #newmarker.modal form.modal-content.animate(action='/') h3 Adding New Marker .imgcontainer @@ -55,31 +55,5 @@ block content input#description(type='text', placeholder='Enter description', name='description', required='') button#add(type='submit') CREATE .container(style='background-color:#f1f1f1;') - button.cancelbtn(type='button', onclick="document.getElementById('id01').style.display='none'") Cancel - script. - // Get the modal - var modal = document.getElementById('id01'); - // When the user clicks anywhere outside of the modal, close it - window.onclick = function(event) { - if (event.target == modal) { - modal.style.display = "none"; - } - } - script. - $(document).ready(function () { - $("#login").click(function (e) { - $.ajax({ - type: "POST", - url: "apiV1/users/login", - contentType: "application/json; charset=utf-8", - data: '{"markerName":"' + $("#markerName").val() + '","description":"' + $("#description").val() + '"}', - dataType: "json", - success: function (result, status, xhr) { - }, - error: function (xhr, status, error) { - e.default(); - } - }); - }); - }); + button.cancelbtn(type='button') Cancel section From 52f894ae9dc00b2714607445e58b43191a3a9385 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 23:08:29 -0400 Subject: [PATCH 25/28] Google Maps successfully integrated --- GhoulAlert/public/javascripts/main.js | 101 +++++++++++++++++++++++- GhoulAlert/public/stylesheets/style.css | 18 ++++- GhoulAlert/views/index.pug | 16 ++-- 3 files changed, 125 insertions(+), 10 deletions(-) diff --git a/GhoulAlert/public/javascripts/main.js b/GhoulAlert/public/javascripts/main.js index 2a197be..2d3be37 100644 --- a/GhoulAlert/public/javascripts/main.js +++ b/GhoulAlert/public/javascripts/main.js @@ -1,3 +1,34 @@ +// Global variables +var map; +var modalMap; +var modalMapMarker; + +// Map initialization +function initMap() { + $(document).ready(() => { + map = new google.maps.Map(document.getElementById('map'), { + center: {lat: 42.3143286, lng: -71.0403235}, + zoom: 8 + }); + + modalMap = new google.maps.Map(document.getElementById('add-marker-map'), { + center: {lat: 42.3143286, lng: -71.0403235}, + zoom: 14 + }); + + modalMapMarker = new google.maps.Marker({ + position: modalMap.getCenter(), + map: modalMap, + title: 'Sighting Occurred Here' + }); + + google.maps.event.addListener(modalMap, 'click', function(event) { + modalMapMarker.setPosition(event.latLng); + modalMap.panTo(event.latLng); + }); + }); +} + $(document).ready(() => { // Message functions var msg = { @@ -28,6 +59,14 @@ $(document).ready(() => { token: null, isLogged: false, + setTokenHeader: function() { + $.ajaxSetup({ + headers: { + Authorization: "Token " + this.token + } + }); + }, + loadFromCookie: function() { if (Cookies.get('username') && Cookies.get('token')) { this.username = Cookies.get('username'); @@ -36,6 +75,7 @@ $(document).ready(() => { $(".login-name").text(this.username); $(".no-login").hide(); $(".has-login").show(); + this.setTokenHeader(); } }, @@ -46,6 +86,7 @@ $(document).ready(() => { Cookies.set("username", this.username, { expires: 30 }); Cookies.set("token", this.token, { expires: 30 }); + this.setTokenHeader(); }, handleServerErrors: function(response) { @@ -130,10 +171,31 @@ $(document).ready(() => { var markers = { collection: [], + toMap: function() { + for (var i = 0; i < this.collection.length; i++) { + var dbMarker = this.collection[i]; + new google.maps.Marker({ + position: { lat: parseFloat(dbMarker.latitude), lng: parseFloat(dbMarker.longitude) }, + map: map, + title: dbMarker.name + }); + } + }, + + newToMap: function(newMarker) { + this.collection.push(newMarker); + new google.maps.Marker({ + position: {lat: parseFloat(newMarker.latitude), lng: parseFloat(newMarker.longitude)}, + map: map, + title: newMarker.name + }); + }, + retrieve: function() { $.get("apiV1/markers/withusers", (data) => { if (!(data.error || data.errors)) { this.collection = data; + this.toMap() } else { console.log("WARNING: Marker retrieval failure"); } @@ -142,21 +204,44 @@ $(document).ready(() => { }); }, - handleErrorsInModal: function() { + handleErrorsInModal: function(response) { + var msg_string = ""; + if (response.errors) { + Object.keys(response.errors).forEach( (key, idx) => { + msg_string += key + " " + response.errors[key] + ". "; + }); + } else if (response.error) { + msg_string = response.error.message; + } else { + msg_string = "Failed marker operations. Try again."; + } + $(".marker-errors").text(msg_string); }, create: function(lat, long, name, desc) { - $.post("apiV1/markers", {latitude: latitude, longitude: longitude, name: name, description: desc}, (data) => { + $.post("apiV1/markers", {latitude: lat, longitude: long, name: name, description: desc}, (data) => { if (data.error || data.errors) { this.handleErrorsInModal(data); + return false; + } else { + $("#newmarker").hide(); + msg.showAffirmation("Successfully created a marker for '" + data.marker.name + "'."); + this.newToMap(data.marker); + map.panTo({ lat: parseFloat(data.marker.latitude), lng: parseFloat(data.marker.longitude) }); + return true; } + }).fail((data) => { + var response = data.responseJSON; + this.handleErrorsInModal(response); + return false; }); } }; markers.retrieve(); + // Input Handling $("#create-login-button").click((e) => { $(".no-login").hide(); @@ -208,6 +293,8 @@ $(document).ready(() => { $("#add-marker-button").click((e) => { if (user.isLogged){ + modalMap.setCenter(map.getCenter()); + modalMapMarker.setPosition(map.getCenter()); $("#newmarker").show(); } else { msg.showAlert("You must be logged in to add a marker."); @@ -215,8 +302,18 @@ $(document).ready(() => { }); $(".cancelbtn").click((e) => { + $(".marker-errors").text(""); $("#newmarker").hide(); }); + $("#add").click((e) => { + e.preventDefault(); + $(".marker-errors").text(""); + var mLat = modalMapMarker.getPosition().lat; + var mLng = modalMapMarker.getPosition().lng; + var mName = $("#markerName").val(); + var mDesc = $("#description").val(); + markers.create(mLat, mLng, mName, mDesc); + }); }); diff --git a/GhoulAlert/public/stylesheets/style.css b/GhoulAlert/public/stylesheets/style.css index 31a9af5..c5e87b4 100644 --- a/GhoulAlert/public/stylesheets/style.css +++ b/GhoulAlert/public/stylesheets/style.css @@ -14,7 +14,7 @@ aside { } /* Full-width input fields */ -input[type=text], input[type=password] { +input[type=text], input[type=password], textarea { width: 100%; padding: 12px 20px; margin: 8px; @@ -138,9 +138,17 @@ navbar { /* Map Styles */ #map { + height: 40em; + width: 100%; margin-top: 6.5em; } +.marker-errors { + color: red; + font-weight: bold; + margin: 1em; +} + /* Extra styles for the cancel button */ .cancelbtn { width: auto; @@ -190,6 +198,14 @@ span.psw { font-weight: bold; } +#add-marker-map, #add-marker-form { + display: inline-block; + width: 45%; + height: 25em; + margin-bottom: 1em; + vertical-align: top; +} + /* Add Zoom Animation */ .animate { -webkit-animation: animatezoom 0.6s; diff --git a/GhoulAlert/views/index.pug b/GhoulAlert/views/index.pug index 58e3ca6..3e077a3 100644 --- a/GhoulAlert/views/index.pug +++ b/GhoulAlert/views/index.pug @@ -5,6 +5,7 @@ block content script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js') script(src='javascripts/js.cookie.js') script(src='javascripts/main.js') + script(src='https://maps.googleapis.com/maps/api/js?key=AIzaSyBK2Mfs4UEpqJXALJwBje0UdMKWjt88Zh8&callback=initMap') body navbar.title-login .title Ghoul Alert @@ -37,23 +38,24 @@ block content section#map - iframe(src='https://www.google.com/maps/d/embed?mid=1_UI6WRqjCzomDvp1b1sLZYz7qY8&hl=en_US', width='640', height='480') section - button#add-marker-button(style='width:auto;') ADD MARKER + button#add-marker-button(style='width:auto;') ADD SIGHTING #newmarker.modal form.modal-content.animate(action='/') - h3 Adding New Marker + h3 Marking a New Sighting .imgcontainer span.close(onclick="document.getElementById('id01').style.display='none'", title='Close Modal') × - .container + #add-marker-map.container + #add-marker-form.container label(for='name') - b Marker Name + b Sighting Name input#markerName(type='text', placeholder='Enter name for Marker', name='name', required='') label(for='description') b Description - input#description(type='text', placeholder='Enter description', name='description', required='') - button#add(type='submit') CREATE + textarea#description(placeholder='Enter description', name='description', rows='8', required='') + button#add(type='submit') CONFIRM SIGHTING + .marker-errors .container(style='background-color:#f1f1f1;') button.cancelbtn(type='button') Cancel section From 0f1568bb81081877c73ca3c86a05077f3b7c426f Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 23:21:45 -0400 Subject: [PATCH 26/28] Responsiveness tweaks --- GhoulAlert/public/stylesheets/style.css | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/GhoulAlert/public/stylesheets/style.css b/GhoulAlert/public/stylesheets/style.css index c5e87b4..ce14df0 100644 --- a/GhoulAlert/public/stylesheets/style.css +++ b/GhoulAlert/public/stylesheets/style.css @@ -223,7 +223,7 @@ span.psw { } /* Change styles for span and cancel button on extra small screens */ -@media screen and (max-width: 300px) { +@media screen and (max-width: 420px) { span.psw { display: block; float: none; @@ -231,4 +231,25 @@ span.psw { .cancelbtn { width: 100%; } + + .title { + display: none; + } + + .login { + width: 100%; + } +} + +/* Change map size at low height */ +@media screen and (max-height: 750px) { + #map { + height: 30em; + } +} + +@media screen and (max-height: 640px) { + #map { + height: 25em; + } } From 6787e6f82b2a6213d2d8673c5401b3648b728ece Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 23:38:20 -0400 Subject: [PATCH 27/28] Messages close automatically --- GhoulAlert/public/javascripts/main.js | 14 ++++++-------- GhoulAlert/public/stylesheets/style.css | 15 +++++++++++---- GhoulAlert/views/index.pug | 11 +++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/GhoulAlert/public/javascripts/main.js b/GhoulAlert/public/javascripts/main.js index 2d3be37..c1f5aaf 100644 --- a/GhoulAlert/public/javascripts/main.js +++ b/GhoulAlert/public/javascripts/main.js @@ -35,11 +35,17 @@ $(document).ready(() => { showAlert: function(message) { $(".alert-message").text(message); $(".alert").slideDown(); + setTimeout(function() { + $('.alert').slideUp('fast'); + }, 3000); }, showAffirmation: function(message) { $(".affirmation-message").text(message); $(".affirmation").slideDown(); + setTimeout(function() { + $('.affirmation').slideUp('fast'); + }, 3000); }, closeAlert: function() { @@ -283,14 +289,6 @@ $(document).ready(() => { $(".no-login").show(); }); - $(".close-alert").click((e) => { - msg.closeAlert(); - }); - - $(".close-affirmation").click((e) => { - msg.closeAffirmation(); - }); - $("#add-marker-button").click((e) => { if (user.isLogged){ modalMap.setCenter(map.getCenter()); diff --git a/GhoulAlert/public/stylesheets/style.css b/GhoulAlert/public/stylesheets/style.css index ce14df0..00d398c 100644 --- a/GhoulAlert/public/stylesheets/style.css +++ b/GhoulAlert/public/stylesheets/style.css @@ -45,14 +45,21 @@ button:hover { opacity: 0.8; } +button.small_button, button.tiny_button { + background-color: blue; + border-radius: 1em; +} + button.small_button { - max-width: 128px; - max-height: 32px; padding: 8px 12px; margin-right: 8px; - background-color: blue; - border-radius: 1em; + max-width: 128px; + max-height: 32px; +} +button.tiny_button { + max-width: 18px; + max-height: 18px; } .inline { diff --git a/GhoulAlert/views/index.pug b/GhoulAlert/views/index.pug index 3e077a3..10a59bd 100644 --- a/GhoulAlert/views/index.pug +++ b/GhoulAlert/views/index.pug @@ -28,17 +28,16 @@ block content input#existing-user-password.small_input.inline(type='password', name='existing-password', placeholder='Password', required='') button#existing-user-submit.small_button.inline(type='submit') Log In button#existing-user-cancel.small_button.inline(type='button') Cancel + + + section#map + #messages .alert .alert-message - button.close-alert.small_button × .affirmation .affirmation-message - button.close-affirmation.small_button × - - - section#map - + section button#add-marker-button(style='width:auto;') ADD SIGHTING #newmarker.modal From 80a45a8afdbb17cb5b6ec9856239a71824fa20c0 Mon Sep 17 00:00:00 2001 From: Michael Nizzari Date: Fri, 12 Apr 2019 23:41:21 -0400 Subject: [PATCH 28/28] App title --- GhoulAlert/views/layout.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GhoulAlert/views/layout.pug b/GhoulAlert/views/layout.pug index 15af079..3eda6ba 100644 --- a/GhoulAlert/views/layout.pug +++ b/GhoulAlert/views/layout.pug @@ -1,7 +1,7 @@ doctype html html head - title= title + title Ghoul Alert link(rel='stylesheet', href='/stylesheets/style.css') body block content