From 9cd57f17faa5eab5162d8a00c5fe366191e744e0 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Mon, 25 Jan 2021 07:32:47 +1100 Subject: [PATCH] MySQL + Sequelize is working, signup emails working --- .dockerignore | 1 - .envdev | 12 +- Dockerfile | 15 - README.md | 37 ++ client/components/pages/signup.jsx | 17 +- docker-compose.yml | 17 - mongo-auth.dev | 2 - package-lock.json | 642 ++++++++++++++-------- package.json | 4 +- server/accounts/signup.js | 134 ++++- server/database/index.js | 11 + server/database/models/accounts.js | 36 ++ server/database/models/banned-emails.js | 25 + server/database/models/index.js | 5 + server/database/models/pending-signups.js | 18 + server/server.js | 3 + sql/change_database.sql | 2 + sql/create_database.sql | 10 + sql/update_database.sql.old | 52 ++ 19 files changed, 760 insertions(+), 283 deletions(-) delete mode 100644 .dockerignore delete mode 100644 Dockerfile delete mode 100644 docker-compose.yml delete mode 100644 mongo-auth.dev create mode 100644 server/database/index.js create mode 100644 server/database/models/accounts.js create mode 100644 server/database/models/banned-emails.js create mode 100644 server/database/models/index.js create mode 100644 server/database/models/pending-signups.js create mode 100644 sql/change_database.sql create mode 100644 sql/create_database.sql create mode 100644 sql/update_database.sql.old diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index b512c09..0000000 --- a/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -node_modules \ No newline at end of file diff --git a/.envdev b/.envdev index c9cbcd7..13a60f3 100644 --- a/.envdev +++ b/.envdev @@ -1 +1,11 @@ -WEB_PORT=8080 +WEB_PROTOCOL=http +WEB_ADDRESS=localhost +WEB_PORT=3000 + +MAIL_SMTP=smtp.example.com +MAIL_USERNAME=foobar@example.com +MAIL_PASSWORD=foobar + +DB_DATABASE=template +DB_USERNAME=template +DB_PASSWORD=pikachu diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 6380ed6..0000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM node:15.6 - -WORKDIR /app - -COPY package*.json ./ - -RUN npm install - -COPY . . - -ENV PORT=3000 - -EXPOSE 3000 - -CMD [ "npm", "start" ] \ No newline at end of file diff --git a/README.md b/README.md index 76e8b65..92d4774 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ # MERN-template A website template using the MERN stack. + +# Setup Development + +To set up this template, please ensure mariadb is running on the host computer, and run `npm install` as normal. + +1. Run `sql/create_database.sql` +2. Run `cp .envdev .env` and enter your details into the new file +3. Execute `npm run dev` + +This should get the template working in development mode. + +# TODO list + +- Account system + - ~~sign up~~ + - verify email + - login (with cookies) + - logout + - account deletion and management +- Administration Panel + - Exclusive to admin accounts + - ban/unban accounts + - inspect user data +- News blog system + - access an external news feed + - build the microservice to provide the news feed + +# Email settings + +Some of the external requirements can be tricky, so let me outline what is needed. If you decide to use gmail as your email provider, then use the following `.env` settings: + + MAIL_SMTP=smtp.gmail.com + MAIL_USERNAME=you@gmail.com + MAIL_PASSWORD=yourpassword + +you'll also need to enable "less secure apps" for the specified email address. Remember - don't ever commit the `.env` file! You might even want to create a dedicated email address just for your project. + diff --git a/client/components/pages/signup.jsx b/client/components/pages/signup.jsx index e5ddde6..91e5696 100644 --- a/client/components/pages/signup.jsx +++ b/client/components/pages/signup.jsx @@ -15,7 +15,7 @@ const SignUp = props => { evt => { evt.preventDefault(); handleSubmit(emailElement.value, usernameElement.value, passwordElement.value, retypeElement.value) - .then(res => alert(res)) + .then(res => res ? alert(res) : null) .then(() => emailElement.value = usernameElement.value = passwordElement.value = retypeElement.value = '') //clear input .catch(e => console.error(e)) ; @@ -54,19 +54,18 @@ const handleSubmit = async (email, username, password, retype) => { const err = handleValidation(email, username, password, retype); if (err) { - alert(err); - return; + return err; } //generate a new formdata payload let formData = new FormData(); - + formData.append('email', email); formData.append('username', username); formData.append('password', password); const result = await fetch('/api/accounts/signup', { method: 'POST', body: formData }); - + if (result.ok) { return result.text(); } else { @@ -79,19 +78,19 @@ const handleValidation = (email, username, password, retype) => { if (!validateEmail(email)) { return 'invalid email'; } - + if (!validateUsername(username)) { return 'invalid username'; } - + if (password.length < 8) { return 'invalid password (Must be at least 8 characters long)'; } - + if (password !== retype) { return 'passwords do not match'; } - + return null; }; diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 842d0ca..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: '3.8' - -services: - web: - build: . - ports: - - "3000:3000" - - mongo: - image: mongo - environment: - MONGO_INITDB_ROOT_PASSWORD_FILE: ./mongo-auth.dev - volumes: - - db-data:/db-data - -volumes: - db-data: \ No newline at end of file diff --git a/mongo-auth.dev b/mongo-auth.dev deleted file mode 100644 index 2d56c30..0000000 --- a/mongo-auth.dev +++ /dev/null @@ -1,2 +0,0 @@ -MONGO_INITDB_ROOT_USERNAME=username -MONGO_INITDB_ROOT_PASSWORD=password \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6611833..0afbf47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,10 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-formidable": "^1.2.0", + "mariadb": "^2.5.2", + "nodemailer": "^6.4.17", "regenerator-runtime": "^0.13.7", - "sendmail": "^1.6.1" + "sequelize": "^6.4.0" }, "devDependencies": { "@babel/core": "^7.12.10", @@ -1268,6 +1270,11 @@ "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, "node_modules/@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -1299,8 +1306,7 @@ "node_modules/@types/node": { "version": "14.14.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", - "dev": true + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" }, "node_modules/@types/source-list-map": { "version": "0.1.2", @@ -1569,11 +1575,6 @@ "node": ">=0.4.0" } }, - "node_modules/addressparser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", - "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1663,6 +1664,11 @@ "node": ">=8" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "node_modules/anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", @@ -2078,37 +2084,6 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, - "node_modules/buildmail": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-3.10.0.tgz", - "integrity": "sha1-xoJtcW55RbtvaxQ0tTmF4CmgMVk=", - "dependencies": { - "addressparser": "1.0.1", - "libbase64": "0.1.0", - "libmime": "2.1.0", - "libqp": "1.1.0", - "nodemailer-fetch": "1.6.0", - "nodemailer-shared": "1.1.0" - } - }, - "node_modules/buildmail/node_modules/iconv-lite": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/buildmail/node_modules/libmime": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.0.tgz", - "integrity": "sha1-Ubx23iKDFh65BRxLyArtcT5P0c0=", - "dependencies": { - "iconv-lite": "0.4.13", - "libbase64": "0.1.0", - "libqp": "1.1.0" - } - }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -3008,6 +2983,14 @@ "node": ">=6" } }, + "node_modules/denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -3027,14 +3010,6 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, - "node_modules/dkim-signer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dkim-signer/-/dkim-signer-0.2.2.tgz", - "integrity": "sha1-qoHsBx7u02IngbqpIgRNeADl8wg=", - "dependencies": { - "libmime": "^2.0.3" - } - }, "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -3146,6 +3121,11 @@ "node": ">=8" } }, + "node_modules/dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -4520,6 +4500,14 @@ "node": ">=0.8.19" } }, + "node_modules/inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", + "engines": [ + "node >= 0.4.0" + ] + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5065,34 +5053,6 @@ "node": ">=8" } }, - "node_modules/libbase64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", - "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=" - }, - "node_modules/libmime": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.3.tgz", - "integrity": "sha1-JQF8pataHpiq2+JyUBfPHUikKgw=", - "dependencies": { - "iconv-lite": "0.4.15", - "libbase64": "0.1.0", - "libqp": "1.1.0" - } - }, - "node_modules/libmime/node_modules/iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/libqp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", - "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=" - }, "node_modules/loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -5131,8 +5091,7 @@ "node_modules/lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "node_modules/loglevel": { "version": "1.7.1", @@ -5143,6 +5102,11 @@ "node": ">= 0.6.0" } }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5179,31 +5143,15 @@ "node": ">=0.10.0" } }, - "node_modules/mailcomposer": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-3.12.0.tgz", - "integrity": "sha1-nF4RiKqOHGLsi4a9Q0aBArY56Pk=", + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "buildmail": "3.10.0", - "libmime": "2.1.0" - } - }, - "node_modules/mailcomposer/node_modules/iconv-lite": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", + "yallist": "^4.0.0" + }, "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/mailcomposer/node_modules/libmime": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.0.tgz", - "integrity": "sha1-Ubx23iKDFh65BRxLyArtcT5P0c0=", - "dependencies": { - "iconv-lite": "0.4.13", - "libbase64": "0.1.0", - "libqp": "1.1.0" + "node": ">=10" } }, "node_modules/make-dir": { @@ -5248,6 +5196,34 @@ "node": ">=0.10.0" } }, + "node_modules/mariadb": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.5.2.tgz", + "integrity": "sha512-SfaBl5/LiX2qJNNr7wCQvizVjtWxVm1CUWYKe+y4OMeyYMM6g0GhwX7/BbGtv/O3WthnGrM+Kj1imFnlescO0w==", + "dependencies": { + "@types/geojson": "^7946.0.7", + "@types/node": "^14.14.7", + "denque": "^1.4.1", + "iconv-lite": "^0.6.2", + "long": "^4.0.0", + "moment-timezone": "^0.5.32", + "please-upgrade-node": "^3.2.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/mariadb/node_modules/iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5555,6 +5531,25 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.32", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", + "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5653,17 +5648,12 @@ "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==", "dev": true }, - "node_modules/nodemailer-fetch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", - "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=" - }, - "node_modules/nodemailer-shared": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", - "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", - "dependencies": { - "nodemailer-fetch": "1.6.0" + "node_modules/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ==", + "engines": { + "node": ">=6.0.0" } }, "node_modules/nodemon": { @@ -6198,6 +6188,14 @@ "node": ">=10" } }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dependencies": { + "semver-compare": "^1.0.0" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -6807,6 +6805,14 @@ "node": ">= 4" } }, + "node_modules/retry-as-promised": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", + "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", + "dependencies": { + "any-promise": "^1.3.0" + } + }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -6898,6 +6904,11 @@ "semver": "bin/semver" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, "node_modules/semver-diff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", @@ -6960,16 +6971,98 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, - "node_modules/sendmail": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sendmail/-/sendmail-1.6.1.tgz", - "integrity": "sha512-lIhvnjSi5e5jL8wA1GPP6j2QVlx6JOEfmdn0QIfmuJdmXYGmJ375kcOU0NSm/34J+nypm4sa1AXrYE5w3uNIIA==", + "node_modules/sequelize": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.4.0.tgz", + "integrity": "sha512-XiSAaYMidgLHgOFz0d0rMlSXP07YoL3GwuG0KTtXR6moR+lfdAA93vhLaN9K6f1ElLMutNTx2f7bNK6mACYfIA==", "dependencies": { - "dkim-signer": "0.2.2", - "mailcomposer": "3.12.0" + "debug": "^4.1.1", + "dottie": "^2.0.0", + "inflection": "1.12.0", + "lodash": "^4.17.20", + "moment": "^2.26.0", + "moment-timezone": "^0.5.31", + "retry-as-promised": "^3.2.0", + "semver": "^7.3.2", + "sequelize-pool": "^6.0.0", + "toposort-class": "^1.0.1", + "uuid": "^8.1.0", + "validator": "^10.11.0", + "wkx": "^0.5.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz", + "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/sequelize/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/serialize-javascript": { @@ -7791,6 +7884,11 @@ "node": ">=0.6" } }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, "node_modules/totalist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", @@ -8151,6 +8249,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -8923,6 +9029,14 @@ "node": ">=8" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -9020,6 +9134,11 @@ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -10331,6 +10450,11 @@ "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "dev": true }, + "@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -10362,8 +10486,7 @@ "@types/node": { "version": "14.14.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", - "dev": true + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" }, "@types/source-list-map": { "version": "0.1.2", @@ -10619,11 +10742,6 @@ "integrity": "sha512-zn/7dYtoTVkG4EoMU55QlQU4F+m+T7Kren6Vj3C2DapWPnakG/DL9Ns5aPAPW5Ixd3uxXrV/BoMKKVFIazPcdg==", "dev": true }, - "addressparser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", - "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -10697,6 +10815,11 @@ "color-convert": "^2.0.1" } }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", @@ -11042,36 +11165,6 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, - "buildmail": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-3.10.0.tgz", - "integrity": "sha1-xoJtcW55RbtvaxQ0tTmF4CmgMVk=", - "requires": { - "addressparser": "1.0.1", - "libbase64": "0.1.0", - "libmime": "2.1.0", - "libqp": "1.1.0", - "nodemailer-fetch": "1.6.0", - "nodemailer-shared": "1.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=" - }, - "libmime": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.0.tgz", - "integrity": "sha1-Ubx23iKDFh65BRxLyArtcT5P0c0=", - "requires": { - "iconv-lite": "0.4.13", - "libbase64": "0.1.0", - "libqp": "1.1.0" - } - } - } - }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -11815,6 +11908,11 @@ "rimraf": "^2.6.3" } }, + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -11831,14 +11929,6 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, - "dkim-signer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dkim-signer/-/dkim-signer-0.2.2.tgz", - "integrity": "sha1-qoHsBx7u02IngbqpIgRNeADl8wg=", - "requires": { - "libmime": "^2.0.3" - } - }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -11948,6 +12038,11 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, + "dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -13078,6 +13173,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=" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -13507,33 +13607,6 @@ "package-json": "^6.3.0" } }, - "libbase64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", - "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=" - }, - "libmime": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.3.tgz", - "integrity": "sha1-JQF8pataHpiq2+JyUBfPHUikKgw=", - "requires": { - "iconv-lite": "0.4.15", - "libbase64": "0.1.0", - "libqp": "1.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" - } - } - }, - "libqp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", - "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=" - }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -13563,8 +13636,7 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "loglevel": { "version": "1.7.1", @@ -13572,6 +13644,11 @@ "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", "dev": true }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -13604,30 +13681,12 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "mailcomposer": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-3.12.0.tgz", - "integrity": "sha1-nF4RiKqOHGLsi4a9Q0aBArY56Pk=", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "buildmail": "3.10.0", - "libmime": "2.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=" - }, - "libmime": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.1.0.tgz", - "integrity": "sha1-Ubx23iKDFh65BRxLyArtcT5P0c0=", - "requires": { - "iconv-lite": "0.4.13", - "libbase64": "0.1.0", - "libqp": "1.1.0" - } - } + "yallist": "^4.0.0" } }, "make-dir": { @@ -13662,6 +13721,30 @@ "object-visit": "^1.0.0" } }, + "mariadb": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.5.2.tgz", + "integrity": "sha512-SfaBl5/LiX2qJNNr7wCQvizVjtWxVm1CUWYKe+y4OMeyYMM6g0GhwX7/BbGtv/O3WthnGrM+Kj1imFnlescO0w==", + "requires": { + "@types/geojson": "^7946.0.7", + "@types/node": "^14.14.7", + "denque": "^1.4.1", + "iconv-lite": "^0.6.2", + "long": "^4.0.0", + "moment-timezone": "^0.5.32", + "please-upgrade-node": "^3.2.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13921,6 +14004,19 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.32", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", + "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "requires": { + "moment": ">= 2.9.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -14009,18 +14105,10 @@ "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==", "dev": true }, - "nodemailer-fetch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", - "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=" - }, - "nodemailer-shared": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", - "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", - "requires": { - "nodemailer-fetch": "1.6.0" - } + "nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ==" }, "nodemon": { "version": "2.0.7", @@ -14441,6 +14529,14 @@ "find-up": "^5.0.0" } }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "requires": { + "semver-compare": "^1.0.0" + } + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -14953,6 +15049,14 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, + "retry-as-promised": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", + "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", + "requires": { + "any-promise": "^1.3.0" + } + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -15032,6 +15136,11 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, "semver-diff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", @@ -15091,15 +15200,59 @@ } } }, - "sendmail": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sendmail/-/sendmail-1.6.1.tgz", - "integrity": "sha512-lIhvnjSi5e5jL8wA1GPP6j2QVlx6JOEfmdn0QIfmuJdmXYGmJ375kcOU0NSm/34J+nypm4sa1AXrYE5w3uNIIA==", + "sequelize": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.4.0.tgz", + "integrity": "sha512-XiSAaYMidgLHgOFz0d0rMlSXP07YoL3GwuG0KTtXR6moR+lfdAA93vhLaN9K6f1ElLMutNTx2f7bNK6mACYfIA==", "requires": { - "dkim-signer": "0.2.2", - "mailcomposer": "3.12.0" + "debug": "^4.1.1", + "dottie": "^2.0.0", + "inflection": "1.12.0", + "lodash": "^4.17.20", + "moment": "^2.26.0", + "moment-timezone": "^0.5.31", + "retry-as-promised": "^3.2.0", + "semver": "^7.3.2", + "sequelize-pool": "^6.0.0", + "toposort-class": "^1.0.1", + "uuid": "^8.1.0", + "validator": "^10.11.0", + "wkx": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, + "sequelize-pool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz", + "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==" + }, "serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -15794,6 +15947,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, "totalist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", @@ -16095,6 +16253,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + }, "value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -16712,6 +16875,14 @@ "string-width": "^4.0.0" } }, + "wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "requires": { + "@types/node": "*" + } + }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -16796,6 +16967,11 @@ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index dea5ae5..7e3acbb 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,10 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-formidable": "^1.2.0", + "mariadb": "^2.5.2", + "nodemailer": "^6.4.17", "regenerator-runtime": "^0.13.7", - "sendmail": "^1.6.1" + "sequelize": "^6.4.0" }, "devDependencies": { "@babel/core": "^7.12.10", diff --git a/server/accounts/signup.js b/server/accounts/signup.js index f9c6734..954b5e8 100644 --- a/server/accounts/signup.js +++ b/server/accounts/signup.js @@ -1,16 +1,142 @@ //libraries -const util = require('util'); const bcrypt = require('bcryptjs'); -const sendmail = require('sendmail')({silent: true}); +const nodemailer = require('nodemailer'); + +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const { bannedEmails, accounts, pendingSignups } = require('../database/models'); //utilities const validateEmail = require('../../common/utilities/validate-email.js'); const validateUsername = require('../../common/utilities/validate-username.js'); +const sequelize = require('../database'); //api/accounts/signup -const route = (req, res) => { - res.status(200).send("The server has received your information."); +const route = async (req, res) => { + //validate the given details + const validateErr = await validateDetails(req.fields); + if (validateErr) { + return res.status(401).send(validateErr); + } + + //generate the password hash + const salt = await bcrypt.genSalt(11); + const hash = await bcrypt.hash(req.fields.password, salt); + + //generate the validation field + const verify = Math.floor(Math.random() * 2000000000); + + //register signup + const signupErr = await registerPendingSignup(req.fields, hash, verify); + if (signupErr) { + return res.status(500).send(signupErr); + } + + //send the validation email + const emailErr = await sendValidationEmail(req.fields.email, verify); + if (emailErr) { + return res.status(500).send(emailErr); + } + + //finally + res.status(200).send("Validation email sent!"); + return null; } +const validateDetails = async (fields) => { + //basic formatting + if (!validateEmail(fields.email)) { + return 'invalid email'; + } + + if (!validateUsername(fields.username)) { + return 'invalid username'; + } + + //check for existing (banned) + const banned = await bannedEmails.findAll({ + where: { + [Op.and]: { + email: fields.email, + expiry: { + [Op.or]: { + [Op.gt]: Sequelize.fn('NOW'), + [Op.eq]: null + } + } + } + } + }); + + if (banned.length > 0) { + return 'banned email'; + } + + //check for existing email + const email = await accounts.findOne({ + where: { + email: fields.email + } + }); + + if (email) { + return 'email already exists'; + } + + //check for existing username + const username = await accounts.findOne({ + where: { + username: fields.username + } + }); + + if (username) { + return 'username already exists'; + } + + return null; +}; + +const registerPendingSignup = async (fields, hash, verify) => { + const record = await pendingSignups.upsert({ + email: fields.email, + username: fields.username, + hash: hash, + verify: verify + }); + + return null; +}; + +const sendValidationEmail = async (email, verify) => { + const addr = `${process.env.WEB_PROTOCOL}://${process.env.WEB_ADDRESS}/api/verify?verify=${verify}`; + const msg = `Hello! Please visit the following address to verify your account: ${addr}`; + + //what exactly is a transport? + let transporter = nodemailer.createTransport({ + host: process.env.MAIL_SMTP, + port: 465, + secure: true, + auth: { + user: process.env.MAIL_USERNAME, + pass: process.env.MAIL_PASSWORD + }, + }); + + // send mail with defined transport object + let info = await transporter.sendMail({ + from: `signup@${process.env.WEB_ADDRESS}`, //WARNING: google overwrites this + to: email, + subject: 'Email Validation', + text: msg + }); + + if (info.accepted[0] != email) { + return 'validation email failed'; + } + + return null; +}; + module.exports = route; diff --git a/server/database/index.js b/server/database/index.js new file mode 100644 index 0000000..7b8c394 --- /dev/null +++ b/server/database/index.js @@ -0,0 +1,11 @@ +const Sequelize = require('sequelize'); + +const sequelize = new Sequelize(process.env.DB_DATABASE, process.env.DB_USERNAME, process.env.DB_PASSWORD, { + host: '127.0.0.1', + dialect: 'mariadb', + logging: false +}); + +sequelize.sync(); + +module.exports = sequelize; \ No newline at end of file diff --git a/server/database/models/accounts.js b/server/database/models/accounts.js new file mode 100644 index 0000000..6bb6257 --- /dev/null +++ b/server/database/models/accounts.js @@ -0,0 +1,36 @@ +const Sequelize = require('sequelize'); +const sequelize = require('..'); + +module.exports = sequelize.define('accounts', { + id: { + type: Sequelize.INTEGER(11), + allowNull: false, + autoIncrement: true, + primaryKey: true, + unique: true + }, + + privilege: { + type: Sequelize.ENUM, + values: ['administrator', 'moderator', 'alpha', 'beta', 'gamma', 'normal'], + defaultValue: 'normal' + }, + + email: { + type: 'varchar(320)', + unique: true + }, + + username: { + type: 'varchar(320)', + unique: true + }, + + hash: 'varchar(100)', //for passwords + + expiry: { + type: 'DATETIME', + allowNull: true, + defaultValue: null + } +}); diff --git a/server/database/models/banned-emails.js b/server/database/models/banned-emails.js new file mode 100644 index 0000000..451f841 --- /dev/null +++ b/server/database/models/banned-emails.js @@ -0,0 +1,25 @@ +const Sequelize = require('sequelize'); +const sequelize = require('..'); + +module.exports = sequelize.define('bannedEmails', { + id: { + type: Sequelize.INTEGER(11), + allowNull: false, + autoIncrement: true, + primaryKey: true, + unique: true + }, + + email: { + type: 'varchar(320)', + unique: true + }, + + reason: Sequelize.TEXT, + + expiry: { + type: 'DATETIME', + allowNull: true, + defaultValue: null + } +}); diff --git a/server/database/models/index.js b/server/database/models/index.js new file mode 100644 index 0000000..e68130e --- /dev/null +++ b/server/database/models/index.js @@ -0,0 +1,5 @@ +module.exports = { + bannedEmails: require('./banned-emails'), + accounts: require('./accounts'), + pendingSignups: require('./pending-signups') +} \ No newline at end of file diff --git a/server/database/models/pending-signups.js b/server/database/models/pending-signups.js new file mode 100644 index 0000000..1da186c --- /dev/null +++ b/server/database/models/pending-signups.js @@ -0,0 +1,18 @@ +const Sequelize = require('sequelize'); +const sequelize = require('..'); + +module.exports = sequelize.define('pendingSignups', { + email: { + type: 'varchar(320)', + unique: true + }, + + username: { + type: 'varchar(320)', + unique: true + }, + + hash: 'varchar(100)', //for passwords + + verify: Sequelize.INTEGER(11) +}); diff --git a/server/server.js b/server/server.js index bd15bfd..22ee859 100644 --- a/server/server.js +++ b/server/server.js @@ -12,6 +12,9 @@ const formidable = require('express-formidable'); app.use(formidable()); +//database connection +const database = require('./database'); + //account management app.use('/api/accounts', require('./accounts')); diff --git a/sql/change_database.sql b/sql/change_database.sql new file mode 100644 index 0000000..7be2397 --- /dev/null +++ b/sql/change_database.sql @@ -0,0 +1,2 @@ +#This file should be used for altering the database in production - make sure it works! + diff --git a/sql/create_database.sql b/sql/create_database.sql new file mode 100644 index 0000000..c851223 --- /dev/null +++ b/sql/create_database.sql @@ -0,0 +1,10 @@ +#This file only needs to be run once, during initial setup +#After this script, next run 'update_database.sql' + +#Create the actual database +CREATE DATABASE IF NOT EXISTS template; +USE template; + +#Create the database user +CREATE USER IF NOT EXISTS 'template'@'%' IDENTIFIED BY 'pikachu'; +GRANT ALL PRIVILEGES ON template.* TO 'template'@'%'; \ No newline at end of file diff --git a/sql/update_database.sql.old b/sql/update_database.sql.old new file mode 100644 index 0000000..6b249d7 --- /dev/null +++ b/sql/update_database.sql.old @@ -0,0 +1,52 @@ +# account system +CREATE TABLE IF NOT EXISTS pendingSignups ( + email VARCHAR(320) UNIQUE, + username VARCHAR(100) UNIQUE, + hash VARCHAR(100), + + verify INTEGER DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + privilege ENUM ('administrator', 'moderator', 'alpha', 'beta', 'normal') DEFAULT 'normal', + + email VARCHAR(320) UNIQUE, + username VARCHAR(100) UNIQUE, + hash VARCHAR(100), + + lastActivityTime TIMESTAMP DEFAULT '2021-01-01 00:00:00', + + deletionTime TIMESTAMP NULL DEFAULT NULL +); + +#CREATE TABLE IF NOT EXISTS sessions ( +# id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, +# td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), +# +# accountId INTEGER UNSIGNED, +# token INTEGER DEFAULT 0, +# +# CONSTRAINT FOREIGN KEY fk_sessions_accountId(accountId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE +#); + +CREATE TABLE IF NOT EXISTS passwordRecover ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + accountId INTEGER UNSIGNED UNIQUE, + token INTEGER DEFAULT 0, + + CONSTRAINT FOREIGN KEY fk_passwordRecover_accountId(accountId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS bannedEmails ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + email VARCHAR(320) UNIQUE, + reason TEXT, + expiry TIMESTAMP NULL DEFAULT NULL +); \ No newline at end of file