From 01f3b393ce4c9804d32d3d2a2742dc28e0b31041 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:03:52 +0200 Subject: [PATCH 001/127] Update app.js --- app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.js b/app.js index 4541e78..fa7d3d8 100644 --- a/app.js +++ b/app.js @@ -12,6 +12,10 @@ app.get('/', (req, res) => { // send a get request to root directory ('/' is thi }) }) +app.get('/ping', (req, res) => { + res.send('hello from heroku!'); +}) + app.listen(PORT, () => { // start server and listen on specified port console.log(`App is running on ${PORT}`) // confirm server is running and log port to the console }) \ No newline at end of file From e2a3214af68b4faa77bbf7807bbc5bdfa3a5dcd1 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:07:44 +0200 Subject: [PATCH 002/127] added db connection --- app.js | 10 + package-lock.json | 555 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 561 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index fa7d3d8..614b23d 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,16 @@ const express = require('express'); // import express module (simplifies routing const app = express(); // create an instance of the express module (app is the conventional variable name used) const fetch = require('node-fetch'); // import node-fetch (enables the fetch API to be used server-side) const PORT = process.env.PORT || 5000; // use either the host env var port (PORT) provided by Heroku or the local port (5000) on your machine +const bodyParser = require('body-parser'); +const Pool = require('pg').Pool + +//HEROKU DB CONNECTION +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: { + rejectUnauthorized: false + } +}); app.get('/', (req, res) => { // send a get request to root directory ('/' is this file (app.js)) fetch('https://www.boredapi.com/api/activity') // fetch activity from bored API - https://www.boredapi.com/about diff --git a/package-lock.json b/package-lock.json index c3961d6..23bd451 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,533 @@ { "name": "heroku-backend-final", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "heroku-backend-final", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "node-fetch": "^2.6.1" + }, + "devDependencies": {} + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/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==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + }, "dependencies": { "accepts": { "version": "1.3.7", @@ -236,9 +761,12 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } }, "on-finished": { "version": "2.3.0", @@ -351,6 +879,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -374,6 +907,20 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } } From e9e8195e738c259fadf5f1772e2ac85bd0411bec Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:09:07 +0200 Subject: [PATCH 003/127] Update app.js --- app.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 614b23d..9dbd069 100644 --- a/app.js +++ b/app.js @@ -6,12 +6,12 @@ const bodyParser = require('body-parser'); const Pool = require('pg').Pool //HEROKU DB CONNECTION -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: { - rejectUnauthorized: false - } -}); +// const pool = new Pool({ +// connectionString: process.env.DATABASE_URL, +// ssl: { +// rejectUnauthorized: false +// } +// }); app.get('/', (req, res) => { // send a get request to root directory ('/' is this file (app.js)) fetch('https://www.boredapi.com/api/activity') // fetch activity from bored API - https://www.boredapi.com/about From bf6adfc0decc09f8dbf5a1c630acd0206ca49876 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:10:55 +0200 Subject: [PATCH 004/127] Update app.js --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 9dbd069..bd10f8a 100644 --- a/app.js +++ b/app.js @@ -2,8 +2,8 @@ const express = require('express'); // import express module (simplifies routing const app = express(); // create an instance of the express module (app is the conventional variable name used) const fetch = require('node-fetch'); // import node-fetch (enables the fetch API to be used server-side) const PORT = process.env.PORT || 5000; // use either the host env var port (PORT) provided by Heroku or the local port (5000) on your machine -const bodyParser = require('body-parser'); -const Pool = require('pg').Pool +// const bodyParser = require('body-parser'); +// const Pool = require('pg').Pool //HEROKU DB CONNECTION // const pool = new Pool({ From 8408eef91ab38f936f082646fafa71e0b030367a Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:13:51 +0200 Subject: [PATCH 005/127] some installs might help??? --- app.js | 6 +- package-lock.json | 753 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 5 +- 3 files changed, 721 insertions(+), 43 deletions(-) diff --git a/app.js b/app.js index bd10f8a..bdd45d1 100644 --- a/app.js +++ b/app.js @@ -2,8 +2,10 @@ const express = require('express'); // import express module (simplifies routing const app = express(); // create an instance of the express module (app is the conventional variable name used) const fetch = require('node-fetch'); // import node-fetch (enables the fetch API to be used server-side) const PORT = process.env.PORT || 5000; // use either the host env var port (PORT) provided by Heroku or the local port (5000) on your machine -// const bodyParser = require('body-parser'); -// const Pool = require('pg').Pool + +//these seem to fuck up things for heroku +const bodyParser = require('body-parser'); +const Pool = require('pg').Pool //HEROKU DB CONNECTION // const pool = new Pool({ diff --git a/package-lock.json b/package-lock.json index 23bd451..46d93bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,11 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "body-parser": "^1.20.1", "express": "^4.17.1", - "node-fetch": "^2.6.1" - }, - "devDependencies": {} + "node-fetch": "^2.6.1", + "pg": "^8.8.0" + } }, "node_modules/accepts": { "version": "1.3.7", @@ -32,33 +33,139 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, + "node_modules/body-parser/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/body-parser/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -178,6 +285,48 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -211,6 +360,46 @@ "node": ">= 0.6" } }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -333,6 +522,14 @@ } } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -344,6 +541,11 @@ "node": ">= 0.8" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -357,6 +559,115 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -386,12 +697,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -399,6 +710,55 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/raw-body/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -456,6 +816,27 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -526,6 +907,14 @@ "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } } }, "dependencies": { @@ -544,26 +933,102 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + } } }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } }, "content-disposition": { "version": "0.5.3", @@ -661,6 +1126,41 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + } } }, "finalhandler": { @@ -687,6 +1187,34 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -768,6 +1296,11 @@ "whatwg-url": "^5.0.0" } }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -776,6 +1309,11 @@ "ee-first": "1.1.1" } }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -786,6 +1324,84 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "requires": {} + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -806,14 +1422,53 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + } } }, "safe-buffer": { @@ -869,6 +1524,21 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -921,6 +1591,11 @@ "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } } diff --git a/package.json b/package.json index a8c8110..7be09cb 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,10 @@ "author": "", "license": "ISC", "dependencies": { + "body-parser": "^1.20.1", "express": "^4.17.1", - "node-fetch": "^2.6.1" + "node-fetch": "^2.6.1", + "pg": "^8.8.0" }, - "devDependencies": {}, "description": "" } From 8b7e6772d3c3e0ac961931dea829b4c979268dd3 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:14:49 +0200 Subject: [PATCH 006/127] Update app.js --- app.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index bdd45d1..10751ae 100644 --- a/app.js +++ b/app.js @@ -8,12 +8,12 @@ const bodyParser = require('body-parser'); const Pool = require('pg').Pool //HEROKU DB CONNECTION -// const pool = new Pool({ -// connectionString: process.env.DATABASE_URL, -// ssl: { -// rejectUnauthorized: false -// } -// }); +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: { + rejectUnauthorized: false + } +}); app.get('/', (req, res) => { // send a get request to root directory ('/' is this file (app.js)) fetch('https://www.boredapi.com/api/activity') // fetch activity from bored API - https://www.boredapi.com/about From 496641b0154aee266724da3e09cd7f28892a953d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:16:35 +0200 Subject: [PATCH 007/127] Update app.js --- app.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app.js b/app.js index 10751ae..0fa564b 100644 --- a/app.js +++ b/app.js @@ -16,12 +16,7 @@ const pool = new Pool({ }); app.get('/', (req, res) => { // send a get request to root directory ('/' is this file (app.js)) - fetch('https://www.boredapi.com/api/activity') // fetch activity from bored API - https://www.boredapi.com/about - .then(res => res.json()) // return a promise containing the response - .then(json => res.send(`

Today's Activity: ${json.activity}!

`)) // extract the JSON body content from the response (specifically the activity value) and sends it to the client - .catch(function(err){ // catch any errors - console.log(err); // log errors to the console - }) + res.send(`

App is running :)

`); }) app.get('/ping', (req, res) => { From bd1d8e54d2fd65ae0d8837a23d6b081fcb516602 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:23:20 +0200 Subject: [PATCH 008/127] db initialization functions --- app.js | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/app.js b/app.js index 0fa564b..15b1e29 100644 --- a/app.js +++ b/app.js @@ -23,6 +23,93 @@ app.get('/ping', (req, res) => { res.send('hello from heroku!'); }) +app.get('/initialize', async (req, res) => { + const query = await pool.query( + ` + CREATE TABLE "users" ( + "id" SERIAL PRIMARY KEY, + "name" varchar(20) UNIQUE NOT NULL, + "premium" boolean DEFAULT false, + "room_keys" int DEFAULT 0 + ); + + ALTER TABLE users + ADD CHECK (LENGTH(name) >= 4); + + ALTER TABLE users + ADD CHECK (room_keys >= 0); + + CREATE TABLE "rooms" ( + "id" SERIAL PRIMARY KEY, + "title" varchar(30) UNIQUE NOT NULL, + "description" varchar(300) NOT NULL, + "creator_id" int NOT NULL, + "next_player_id" int, + "turn_end" timestamp, + "full" boolean DEFAULT false, + "finished" boolean DEFAULT false + ); + + ALTER TABLE rooms + ADD CHECK (LENGTH(title) >= 3); + + ALTER TABLE rooms + ADD CHECK (LENGTH(description) >= 3); + + CREATE TABLE "scenarios" ( + "id" SERIAL PRIMARY KEY, + "number" int NOT NULL, + "text" varchar NOT NULL, + "creator_id" int NOT NULL, + "room_id" int NOT NULL + ); + + ALTER TABLE scenarios + ADD CHECK (number >= 0); + + ALTER TABLE scenarios + ADD CHECK (LENGTH(text) >= 3); + + CREATE TABLE "rooms_users" ( + "id" SERIAL PRIMARY KEY, + "room_id" int NOT NULL, + "user_id" int NOT NULL, + "active" boolean NOT NULL DEFAULT true, + "queue_number" int NOT NULL + ); + + ALTER TABLE rooms_users + ADD CHECK (queue_number >= 0); + + ALTER TABLE "rooms" ADD FOREIGN KEY ("creator_id") REFERENCES "users" ("id"); + ALTER TABLE "rooms" ADD FOREIGN KEY ("next_player_id") REFERENCES "users" ("id"); + ALTER TABLE "scenarios" ADD FOREIGN KEY ("creator_id") REFERENCES "users" ("id"); + ALTER TABLE "scenarios" ADD FOREIGN KEY ("room_id") REFERENCES "rooms" ("id"); + ALTER TABLE "rooms_users" ADD FOREIGN KEY ("room_id") REFERENCES "rooms" ("id"); + ALTER TABLE "rooms_users" ADD FOREIGN KEY ("user_id") REFERENCES "users" ("id"); + ` + ); + + res.send(query); +}); + +app.get('/users', async (req, res) => { + const query = await pool.query('SELECT * FROM users'); + res.json(query.rows); +}); + +app.post('/users', async (req, res) => { + pool.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { + if (error) { + throw error + } + res.status(201).send({ + message: 'Successfully added a person to the db!', + person: results.rows[0] + }); + }) +}); + app.listen(PORT, () => { // start server and listen on specified port console.log(`App is running on ${PORT}`) // confirm server is running and log port to the console }) \ No newline at end of file From 62aac1461b22bdd13eb9568e0f8a38ddcd5ae3de Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:33:52 +0200 Subject: [PATCH 009/127] more test routes --- app.js | 116 ++++++++++++++++----------------------------------------- 1 file changed, 33 insertions(+), 83 deletions(-) diff --git a/app.js b/app.js index 15b1e29..7bb7163 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,7 @@ -const express = require('express'); // import express module (simplifies routing/requests, among other things) -const app = express(); // create an instance of the express module (app is the conventional variable name used) -const fetch = require('node-fetch'); // import node-fetch (enables the fetch API to be used server-side) -const PORT = process.env.PORT || 5000; // use either the host env var port (PORT) provided by Heroku or the local port (5000) on your machine - -//these seem to fuck up things for heroku +const express = require('express'); +const app = express(); +const fetch = require('node-fetch'); +const PORT = process.env.PORT || 5000; const bodyParser = require('body-parser'); const Pool = require('pg').Pool @@ -15,89 +13,40 @@ const pool = new Pool({ } }); -app.get('/', (req, res) => { // send a get request to root directory ('/' is this file (app.js)) +//TEST CONNECTION +app.get('/', (req, res) => { res.send(`

App is running :)

`); }) -app.get('/ping', (req, res) => { - res.send('hello from heroku!'); -}) - -app.get('/initialize', async (req, res) => { - const query = await pool.query( - ` - CREATE TABLE "users" ( - "id" SERIAL PRIMARY KEY, - "name" varchar(20) UNIQUE NOT NULL, - "premium" boolean DEFAULT false, - "room_keys" int DEFAULT 0 - ); - - ALTER TABLE users - ADD CHECK (LENGTH(name) >= 4); - - ALTER TABLE users - ADD CHECK (room_keys >= 0); - - CREATE TABLE "rooms" ( - "id" SERIAL PRIMARY KEY, - "title" varchar(30) UNIQUE NOT NULL, - "description" varchar(300) NOT NULL, - "creator_id" int NOT NULL, - "next_player_id" int, - "turn_end" timestamp, - "full" boolean DEFAULT false, - "finished" boolean DEFAULT false - ); - - ALTER TABLE rooms - ADD CHECK (LENGTH(title) >= 3); - - ALTER TABLE rooms - ADD CHECK (LENGTH(description) >= 3); - - CREATE TABLE "scenarios" ( - "id" SERIAL PRIMARY KEY, - "number" int NOT NULL, - "text" varchar NOT NULL, - "creator_id" int NOT NULL, - "room_id" int NOT NULL - ); - - ALTER TABLE scenarios - ADD CHECK (number >= 0); - - ALTER TABLE scenarios - ADD CHECK (LENGTH(text) >= 3); - - CREATE TABLE "rooms_users" ( - "id" SERIAL PRIMARY KEY, - "room_id" int NOT NULL, - "user_id" int NOT NULL, - "active" boolean NOT NULL DEFAULT true, - "queue_number" int NOT NULL - ); - - ALTER TABLE rooms_users - ADD CHECK (queue_number >= 0); - - ALTER TABLE "rooms" ADD FOREIGN KEY ("creator_id") REFERENCES "users" ("id"); - ALTER TABLE "rooms" ADD FOREIGN KEY ("next_player_id") REFERENCES "users" ("id"); - ALTER TABLE "scenarios" ADD FOREIGN KEY ("creator_id") REFERENCES "users" ("id"); - ALTER TABLE "scenarios" ADD FOREIGN KEY ("room_id") REFERENCES "rooms" ("id"); - ALTER TABLE "rooms_users" ADD FOREIGN KEY ("room_id") REFERENCES "rooms" ("id"); - ALTER TABLE "rooms_users" ADD FOREIGN KEY ("user_id") REFERENCES "users" ("id"); - ` - ); - - res.send(query); -}); - +//GET ALL USERS app.get('/users', async (req, res) => { const query = await pool.query('SELECT * FROM users'); res.json(query.rows); }); +//GET ALL ROOMS +app.get('/rooms', async (req, res) => { + const query = await pool.query('SELECT * FROM rooms'); +}); + +//CREATE A ROOM +app.post('/rooms', async (req, res) => { + pool.query( + 'INSERT INTO rooms (title, description, creator_id) VALUES ($1, $2, $3) RETURNING *', + [req.query.name, req.query.description, req.query.creatorId], + (error, results) => { + if (error) { + throw error + } + res.status(201).send({ + message: 'Successfully added a room to the db!', + person: results.rows[0] + }); + } + ) +}); + +//CREATE A NEW USER app.post('/users', async (req, res) => { pool.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { if (error) { @@ -110,6 +59,7 @@ app.post('/users', async (req, res) => { }) }); -app.listen(PORT, () => { // start server and listen on specified port - console.log(`App is running on ${PORT}`) // confirm server is running and log port to the console +//START SERVER +app.listen(PORT, () => { + console.log(`App is running on ${PORT}`) }) \ No newline at end of file From 7235dfdfb09b2b636b99cf3f564ede63ba386553 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 26 Oct 2022 17:37:58 +0200 Subject: [PATCH 010/127] Update app.js --- app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app.js b/app.js index 7bb7163..9a77618 100644 --- a/app.js +++ b/app.js @@ -27,6 +27,7 @@ app.get('/users', async (req, res) => { //GET ALL ROOMS app.get('/rooms', async (req, res) => { const query = await pool.query('SELECT * FROM rooms'); + res.json(query.rows) }); //CREATE A ROOM From 151baf3e98d59905dae992a8ce3e96300cd3828e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 27 Oct 2022 08:22:56 +0200 Subject: [PATCH 011/127] Create dev branch --- app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.js b/app.js index 9a77618..f455db4 100644 --- a/app.js +++ b/app.js @@ -5,6 +5,8 @@ const PORT = process.env.PORT || 5000; const bodyParser = require('body-parser'); const Pool = require('pg').Pool +//ENTERING THE DEV BRANCH!!! + //HEROKU DB CONNECTION const pool = new Pool({ connectionString: process.env.DATABASE_URL, From d02d5ab64d44391a66706cabeaadc8ab593b128b Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 27 Oct 2022 09:27:01 +0200 Subject: [PATCH 012/127] some file structure --- app.js | 61 ++++----------------------------------- routers/dbConnect.js | 27 +++++++++++++++++ routers/roomRouter.js | 30 +++++++++++++++++++ routers/scenarioRouter.js | 9 ++++++ routers/userRouter.js | 24 +++++++++++++++ 5 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 routers/dbConnect.js create mode 100644 routers/roomRouter.js create mode 100644 routers/scenarioRouter.js create mode 100644 routers/userRouter.js diff --git a/app.js b/app.js index f455db4..dd5364f 100644 --- a/app.js +++ b/app.js @@ -1,68 +1,19 @@ const express = require('express'); const app = express(); -const fetch = require('node-fetch'); const PORT = process.env.PORT || 5000; -const bodyParser = require('body-parser'); -const Pool = require('pg').Pool -//ENTERING THE DEV BRANCH!!! +const roomRouter = require('./routers/roomRouter'); +const userRouter = require('./routers/userRouter'); +const scenarioRouter = require('./routers/scenarioRouter'); -//HEROKU DB CONNECTION -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: { - rejectUnauthorized: false - } -}); +app.use('/room', roomRouter); +app.use('/user', userRouter); +app.use('/scenario', scenarioRouter); -//TEST CONNECTION app.get('/', (req, res) => { res.send(`

App is running :)

`); }) -//GET ALL USERS -app.get('/users', async (req, res) => { - const query = await pool.query('SELECT * FROM users'); - res.json(query.rows); -}); - -//GET ALL ROOMS -app.get('/rooms', async (req, res) => { - const query = await pool.query('SELECT * FROM rooms'); - res.json(query.rows) -}); - -//CREATE A ROOM -app.post('/rooms', async (req, res) => { - pool.query( - 'INSERT INTO rooms (title, description, creator_id) VALUES ($1, $2, $3) RETURNING *', - [req.query.name, req.query.description, req.query.creatorId], - (error, results) => { - if (error) { - throw error - } - res.status(201).send({ - message: 'Successfully added a room to the db!', - person: results.rows[0] - }); - } - ) -}); - -//CREATE A NEW USER -app.post('/users', async (req, res) => { - pool.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { - if (error) { - throw error - } - res.status(201).send({ - message: 'Successfully added a person to the db!', - person: results.rows[0] - }); - }) -}); - -//START SERVER app.listen(PORT, () => { console.log(`App is running on ${PORT}`) }) \ No newline at end of file diff --git a/routers/dbConnect.js b/routers/dbConnect.js new file mode 100644 index 0000000..7ab0dd5 --- /dev/null +++ b/routers/dbConnect.js @@ -0,0 +1,27 @@ +const Pool = require('pg').Pool + +const pool = ( + + process.env.DATABASE_URL ? + + new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: { + rejectUnauthorized: false + } + }) + + : + + new Pool({ + user: 'postgres', + host: 'localhost', + database: 'unwritten', + password: 'postgres', + port: 5432, + }) + +) + + +module.exports = pool; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js new file mode 100644 index 0000000..4269384 --- /dev/null +++ b/routers/roomRouter.js @@ -0,0 +1,30 @@ +const express = require('express'); +const roomRouter = express.Router(); +const db = require('./dbConnect.js') + +roomRouter.get('/', async (req, res, next) => { + + // const query = await pool.query('SELECT * FROM rooms'); + // res.json(query.rows) + + res.send('here are all the rooms :)'); +}); + +module.exports = roomRouter; + +// //CREATE A ROOM +// app.post('/rooms', async (req, res) => { +// pool.query( +// 'INSERT INTO rooms (title, description, creator_id) VALUES ($1, $2, $3) RETURNING *', +// [req.query.name, req.query.description, req.query.creatorId], +// (error, results) => { +// if (error) { +// throw error +// } +// res.status(201).send({ +// message: 'Successfully added a room to the db!', +// person: results.rows[0] +// }); +// } +// ) +// }); \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js new file mode 100644 index 0000000..5c5bfdf --- /dev/null +++ b/routers/scenarioRouter.js @@ -0,0 +1,9 @@ +const express = require('express'); +const scenarioRouter = express.Router(); +const db = require('./dbConnect.js') + +scenarioRouter.get('/', async (req, res) => { + res.send('you tried to get all scenarios! lol! when would you even need that? you little shit. Dont make these requests.') +}); + +module.exports = scenarioRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js new file mode 100644 index 0000000..0358123 --- /dev/null +++ b/routers/userRouter.js @@ -0,0 +1,24 @@ +const express = require('express'); +const userRouter = express.Router(); +const db = require('./dbConnect.js') + +userRouter.get('/', async (req, res) => { + // const query = await db.query('SELECT * FROM users'); + // res.json(query.rows); + res.send('getting all the users!') +}); + +module.exports = userRouter; + +// //CREATE A NEW USER +// app.post('/users', async (req, res) => { +// pool.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { +// if (error) { +// throw error +// } +// res.status(201).send({ +// message: 'Successfully added a person to the db!', +// person: results.rows[0] +// }); +// }) +// }); \ No newline at end of file From 4255890f59129dcb5099f6859bc61889dd094304 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 27 Oct 2022 11:39:33 +0200 Subject: [PATCH 013/127] connect to local db if not in heroku --- routers/dbConnect.js | 4 +++- routers/userRouter.js | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/routers/dbConnect.js b/routers/dbConnect.js index 7ab0dd5..355dd5e 100644 --- a/routers/dbConnect.js +++ b/routers/dbConnect.js @@ -4,6 +4,7 @@ const pool = ( process.env.DATABASE_URL ? + //heroku new Pool({ connectionString: process.env.DATABASE_URL, ssl: { @@ -12,7 +13,8 @@ const pool = ( }) : - + + //local new Pool({ user: 'postgres', host: 'localhost', diff --git a/routers/userRouter.js b/routers/userRouter.js index 0358123..cfb8ca0 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -3,9 +3,9 @@ const userRouter = express.Router(); const db = require('./dbConnect.js') userRouter.get('/', async (req, res) => { - // const query = await db.query('SELECT * FROM users'); - // res.json(query.rows); - res.send('getting all the users!') + const query = await db.query('SELECT * FROM users'); + res.json(query.rows); + //res.send('getting all the users!') }); module.exports = userRouter; From 5a1a94ba0a41513ac89660632db3774105c17ff9 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 27 Oct 2022 15:31:09 +0200 Subject: [PATCH 014/127] dotENV added --- .gitignore | 4 ++- app.js | 12 +++++++ package-lock.json | 89 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 91dfed8..7a8675d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store -node_modules \ No newline at end of file +node_modules +.env +.env.test \ No newline at end of file diff --git a/app.js b/app.js index dd5364f..a085a82 100644 --- a/app.js +++ b/app.js @@ -1,19 +1,31 @@ +//import libs const express = require('express'); const app = express(); const PORT = process.env.PORT || 5000; +console.log(process.env.STATUS); +console.log(process.env.PORT); + +//import middleware +const morgan = require('morgan'); +app.use(morgan('tiny')); + +//import routers const roomRouter = require('./routers/roomRouter'); const userRouter = require('./routers/userRouter'); const scenarioRouter = require('./routers/scenarioRouter'); +//mount routers app.use('/room', roomRouter); app.use('/user', userRouter); app.use('/scenario', scenarioRouter); +//test rout app.get('/', (req, res) => { res.send(`

App is running :)

`); }) +//start server app.listen(PORT, () => { console.log(`App is running on ${PORT}`) }) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 46d93bd..1e517f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "ISC", "dependencies": { "body-parser": "^1.20.1", + "dotenv": "^16.0.3", "express": "^4.17.1", + "morgan": "^1.10.0", "node-fetch": "^2.6.1", "pg": "^8.8.0" } @@ -32,6 +34,17 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -219,6 +232,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -490,6 +511,29 @@ "node": ">= 0.6" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -541,6 +585,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -932,6 +984,14 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1071,6 +1131,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1278,6 +1343,25 @@ "mime-db": "1.44.0" } }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1309,6 +1393,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", diff --git a/package.json b/package.json index 7be09cb..2d95fe8 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "license": "ISC", "dependencies": { "body-parser": "^1.20.1", + "dotenv": "^16.0.3", "express": "^4.17.1", + "morgan": "^1.10.0", "node-fetch": "^2.6.1", "pg": "^8.8.0" }, From 0130846dd448698ee355ad8b58b29f70fb366afa Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 27 Oct 2022 16:17:40 +0200 Subject: [PATCH 015/127] user paths done --- app.js | 14 ++++++---- package-lock.json | 22 ++++++++++++++++ package.json | 1 + routers/userRouter.js | 59 +++++++++++++++++++++++++++++++------------ 4 files changed, 75 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index a085a82..5a327bf 100644 --- a/app.js +++ b/app.js @@ -2,13 +2,17 @@ const express = require('express'); const app = express(); const PORT = process.env.PORT || 5000; +require('dotenv').config() -console.log(process.env.STATUS); -console.log(process.env.PORT); - -//import middleware +//logging middleware const morgan = require('morgan'); -app.use(morgan('tiny')); +app.use(morgan('tiny')); + +//error handling for dev environment +if (process.env.NODE_ENV === 'development'){ + const errorhandler = require('errorhandler'); + app.use(errorhandler()); +} //import routers const roomRouter = require('./routers/roomRouter'); diff --git a/package-lock.json b/package-lock.json index 1e517f1..ad05fb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "body-parser": "^1.20.1", "dotenv": "^16.0.3", + "errorhandler": "^1.5.1", "express": "^4.17.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", @@ -253,6 +254,18 @@ "node": ">= 0.8" } }, + "node_modules/errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "dependencies": { + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1146,6 +1159,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "requires": { + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", diff --git a/package.json b/package.json index 2d95fe8..d2aaf4f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dependencies": { "body-parser": "^1.20.1", "dotenv": "^16.0.3", + "errorhandler": "^1.5.1", "express": "^4.17.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", diff --git a/routers/userRouter.js b/routers/userRouter.js index cfb8ca0..db87739 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -2,23 +2,50 @@ const express = require('express'); const userRouter = express.Router(); const db = require('./dbConnect.js') -userRouter.get('/', async (req, res) => { - const query = await db.query('SELECT * FROM users'); - res.json(query.rows); - //res.send('getting all the users!') +userRouter.get('/', async (req, res) => { + + const query = await db.query('SELECT * FROM users WHERE id=8'); //change to logged user when session is implemented + + if(!query.rows){ + res.status(400).send('Unable to get users. Query returned nothing'); + } + else if(query.rows.length < 1){ + res.status(400).send('Couldnt not find any user with that id'); + } + else if(query.rows.length > 2){ + res.status(400).send('Query returned multiple users'); + } + else{ + res.json(query.rows[0]); + } +}); + +userRouter.post('/', async (req, res) => { + + if(!req.query.name){ + res.status(400).send('cant create user. No name was provided'); + return; + } + + if(req.query.name.length < 4){ + res.status(400).send('cant create user. name must be at least 4 characters long'); + return; + } + + if(req.query.name.length > 20){ + res.status(400).send('cant create user. name must be maximum 20 characters long'); + return + } + + db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { + if (error) { + res.status(400).send(error.detail); + return; + } + res.status(201).send('User created successfully!'); + }) + }); module.exports = userRouter; -// //CREATE A NEW USER -// app.post('/users', async (req, res) => { -// pool.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { -// if (error) { -// throw error -// } -// res.status(201).send({ -// message: 'Successfully added a person to the db!', -// person: results.rows[0] -// }); -// }) -// }); \ No newline at end of file From 377e2d6fad1cce4c45685d27ec71d265b3f3ac03 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 28 Oct 2022 12:45:12 +0200 Subject: [PATCH 016/127] Added create room route --- fakeData/testUser.js | 8 +++ routers/roomRouter.js | 121 ++++++++++++++++++++++++++++++++++-------- routers/userRouter.js | 3 +- 3 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 fakeData/testUser.js diff --git a/fakeData/testUser.js b/fakeData/testUser.js new file mode 100644 index 0000000..921491c --- /dev/null +++ b/fakeData/testUser.js @@ -0,0 +1,8 @@ +const user = { + id: 18, + name: 'smogg', + premium: true, + room_keys: 3 +} + +module.exports = user; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 4269384..9348742 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -1,30 +1,109 @@ const express = require('express'); const roomRouter = express.Router(); const db = require('./dbConnect.js') +const user = require('../fakeData/testUser'); roomRouter.get('/', async (req, res, next) => { + const query = await db.query('SELECT * FROM rooms'); + res.json(query.rows) +}); - // const query = await pool.query('SELECT * FROM rooms'); - // res.json(query.rows) - - res.send('here are all the rooms :)'); +roomRouter.get('/available', async (req, res, next) => { + const query = await db.query(` + SELECT + rooms.id AS room_id, + rooms.title AS title, + rooms.description AS description, + users.name AS user_name, + (rooms.creator_id = rooms_users.user_id) AS creator + FROM rooms + JOIN rooms_users + ON rooms.id = rooms_users.room_id + JOIN users + ON rooms_users.user_id = users.id + WHERE + rooms.full = false + AND finished = false + AND creator_id != 8 + AND users.id != 8 + + + -- also join with scenario table to count the number of scenarios + -- salad hunt should not appear below! since the user is in it. How to remove that one? must filter rooms that has a corresponding row with our name in rooms_users + `) + console.log(query); + res.json(query.rows) }); -module.exports = roomRouter; +roomRouter.post('/', async (req, res) => { + + //ERROR CHECKS + if(!req.query.title){ + res.status(400).send('cant create room. Please provide a title'); + return; + } + if(req.query.title.length <= 3){ + res.status(400).send('cant create room. Title must be at least 3 chars long'); + return; + } + if(req.query.title.length > 20){ + res.status(400).send('cant create room. Title can me maximum 20 characters long'); + return; + } + if(!req.query.description){ + res.status(400).send('cant create room. Please provide a description'); + return; + } + if(req.query.description.length <= 3){ + res.status(400).send('cant create room. Title must be at least 3 chars long'); + return; + } + if(req.query.description.length > 200){ + res.status(400).send('cant create room. Description can be at max 200 characters'); + return; + } + if(!req.query.scenario){ + res.status(400).send('cant create room. Please provide a starting scenario'); + return; + } + if(req.query.scenario.length <= 19){ + res.status(400).send('cant create room. starting scenario must be at least 20 characters'); + return; + } + if(req.query.scenario.length > 500){ + res.status(400).send('cant create room. Starting scenario can be at max 500 characters'); + return; + } + + //TRY ADD TO DATABASE + try { + await db.query('BEGIN'); + await db.query( + 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', + [user.id] + ); + const newRoomRes = await db.query( + 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', + [req.query.title, req.query.description, user.id] + ); + const newRoomId = newRoomRes.rows[0].id; + await db.query( + 'INSERT INTO rooms_users(room_id, user_id, queue_number) VALUES($1, $2, $3)', + [newRoomId, user.id, 0] + ); + await db.query( + 'INSERT INTO scenarios(number, text, creator_id, room_id) VALUES($1, $2, $3, $4)', + [0, req.query.scenario, user.id, newRoomId] + ); + await db.query('COMMIT'); + res.status(200).send('new room added with ID ' + newRoomId); + } + catch (e) { + db.query('ROLLBACK'); + console.error('FAILED TO ADD NEW ROOM: ' + e.message); + res.status(400).send('FAILED TO ADD NEW ROOM: ' + e.message) + } + +}); -// //CREATE A ROOM -// app.post('/rooms', async (req, res) => { -// pool.query( -// 'INSERT INTO rooms (title, description, creator_id) VALUES ($1, $2, $3) RETURNING *', -// [req.query.name, req.query.description, req.query.creatorId], -// (error, results) => { -// if (error) { -// throw error -// } -// res.status(201).send({ -// message: 'Successfully added a room to the db!', -// person: results.rows[0] -// }); -// } -// ) -// }); \ No newline at end of file +module.exports = roomRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index db87739..c308085 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,10 +1,11 @@ const express = require('express'); const userRouter = express.Router(); const db = require('./dbConnect.js') +const user = require('../fakeData/testUser'); //remove in live v. userRouter.get('/', async (req, res) => { - const query = await db.query('SELECT * FROM users WHERE id=8'); //change to logged user when session is implemented + const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented if(!query.rows){ res.status(400).send('Unable to get users. Query returned nothing'); From 968c74f1a792897b19c886382f3dd70369dbf611 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 28 Oct 2022 15:05:27 +0200 Subject: [PATCH 017/127] Join room route --- fakeData/testUser.js | 8 ++--- routers/roomRouter.js | 75 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 921491c..7a64a54 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,8 +1,8 @@ const user = { - id: 18, - name: 'smogg', - premium: true, - room_keys: 3 + id: 10, + name: 'flandre', + premium: false, + room_keys: 0 } module.exports = user; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 9348742..9d8f353 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -38,39 +38,39 @@ roomRouter.get('/available', async (req, res, next) => { roomRouter.post('/', async (req, res) => { //ERROR CHECKS - if(!req.query.title){ + if (!req.query.title) { res.status(400).send('cant create room. Please provide a title'); return; } - if(req.query.title.length <= 3){ + if (req.query.title.length <= 3) { res.status(400).send('cant create room. Title must be at least 3 chars long'); return; } - if(req.query.title.length > 20){ + if (req.query.title.length > 20) { res.status(400).send('cant create room. Title can me maximum 20 characters long'); return; } - if(!req.query.description){ + if (!req.query.description) { res.status(400).send('cant create room. Please provide a description'); return; } - if(req.query.description.length <= 3){ + if (req.query.description.length <= 3) { res.status(400).send('cant create room. Title must be at least 3 chars long'); return; } - if(req.query.description.length > 200){ + if (req.query.description.length > 200) { res.status(400).send('cant create room. Description can be at max 200 characters'); return; } - if(!req.query.scenario){ + if (!req.query.scenario) { res.status(400).send('cant create room. Please provide a starting scenario'); return; } - if(req.query.scenario.length <= 19){ + if (req.query.scenario.length <= 19) { res.status(400).send('cant create room. starting scenario must be at least 20 characters'); return; } - if(req.query.scenario.length > 500){ + if (req.query.scenario.length > 500) { res.status(400).send('cant create room. Starting scenario can be at max 500 characters'); return; } @@ -106,4 +106,61 @@ roomRouter.post('/', async (req, res) => { }); +roomRouter.post('/join', async (req, res) => { + + try { + + if(!req.query.room_id) throw new Error('Please provide a room_id!'); + + await db.query('BEGIN'); + + //make sure room is not full or finished + const roomQuery = await db.query('SELECT * FROM rooms WHERE id=$1', [req.query.room_id]); + if(roomQuery.rows.length == 0) throw new Error('There is no room with that id'); + if (roomQuery.rows[0].full) throw new Error('Room is full'); + if (roomQuery.rows[0].finished) throw new Error('Story has already been finished'); + + //add players to rooms_users + const playerQuery = await db.query( + 'SELECT * FROM rooms_users WHERE room_id = $1', + [req.query.room_id] + ); + await db.query( + 'INSERT INTO rooms_users (room_id, user_id, queue_number) VALUES ($1, $2, $3)', + [req.query.room_id, user.id, playerQuery.rows.length] + ); + + //alter the rooms table + //add a turn_end timestamp to room + await db.query( + `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, //deadline 2 days from now + [req.query.room_id] + ); + + //make room full if full + if(playerQuery.rows.length == 3){ + await db.query( + 'UPDATE rooms SET "full"=true WHERE id=$1', + [req.query.room_id] + ); + } + + //set next player + await db.query( + 'UPDATE rooms SET next_player_id=$1 WHERE id=$2', + [user.id, req.query.room_id] + ); + + //commit + await db.query('COMMIT'); + res.status(200).send('Successfully joined the room!'); + } + catch (e) { + db.query('ROLLBACK'); + console.error('FAILED TO JOIN ROOM: ' + e.message); + res.status(400).send('FAILED TO JOIN ROOM: ' + e.message) + } + +}); + module.exports = roomRouter; \ No newline at end of file From a926543c304ce14820c1ac117e786115cd064e17 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 28 Oct 2022 15:36:53 +0200 Subject: [PATCH 018/127] progress on add scenario route --- routers/scenarioRouter.js | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 5c5bfdf..1b00286 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,9 +1,54 @@ const express = require('express'); const scenarioRouter = express.Router(); -const db = require('./dbConnect.js') +const db = require('./dbConnect.js'); +const user = require('../fakeData/testUser'); + +scenarioRouter.post('/', async (req,res) => { + + try { + + //check: room_id provided¨ + if(!req.query.room_id) throw new Error('No room_id provided'); + + //checK: scenario text provided + if(!req.query.text) throw new Error('No text provided'); + + //check: scenario text is long enough + if(req.query.text.length < 3) throw new Error('text must be at least 3 characters long'); + + //check: player has enough characters + const playerQuery = await db.query( //might not be necessary, since we will query this table any way and can return the player + 'SELECT * FROM rooms_users WHERE user_id = $1', + [user.id] + ) + if(playerQuery.rows[0].char_count < req.query.text.length) throw new Error('player does not have enough characters left'); + + //check: its the players turn + const roomQuery = await db.query( + 'SELECT * FROM rooms WHERE id=$1', + [req.query.room_id] + ); + if(roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); + + //check: room is not finished + if(roomQuery.rows[0].finished) throw new Error('the story has already been ended'); + + //check: deadline has not passed + if(roomQuery.rows[0].turn_end < new Date()){ + //++ update who's turn it is + throw new Error('turn has already passed'); + } + + //insert into scenarios + //set next player id (get from rooms_users) + //update turn end + //sets finished if full scenario count + //update player character count + + } catch (error) { + + } -scenarioRouter.get('/', async (req, res) => { - res.send('you tried to get all scenarios! lol! when would you even need that? you little shit. Dont make these requests.') }); module.exports = scenarioRouter; \ No newline at end of file From 871256a634eb704fcb64bd955d3f95df34c7be1f Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 28 Oct 2022 15:50:27 +0200 Subject: [PATCH 019/127] upload scenario route progress --- routers/scenarioRouter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 1b00286..982657a 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -35,13 +35,12 @@ scenarioRouter.post('/', async (req,res) => { //check: deadline has not passed if(roomQuery.rows[0].turn_end < new Date()){ - //++ update who's turn it is + //update player turn throw new Error('turn has already passed'); } //insert into scenarios - //set next player id (get from rooms_users) - //update turn end + //update player turn //sets finished if full scenario count //update player character count @@ -51,4 +50,5 @@ scenarioRouter.post('/', async (req,res) => { }); + module.exports = scenarioRouter; \ No newline at end of file From 96d97a80bf6c9ed5c93aab54f621354d40845e68 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 31 Oct 2022 11:09:28 +0100 Subject: [PATCH 020/127] scenario posting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit routen funkar, fastär jävligt shit. Stökig kod och troligtvis öppen för en massa buggar. Men en bra början att jobba vidare på --- fakeData/testUser.js | 6 +-- routers/scenarioRouter.js | 104 ++++++++++++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 7a64a54..5ae29f7 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,8 +1,8 @@ const user = { - id: 10, + id: 9, name: 'flandre', - premium: false, - room_keys: 0 + premium: true, + room_keys: 9 } module.exports = user; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 982657a..5166887 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -3,49 +3,113 @@ const scenarioRouter = express.Router(); const db = require('./dbConnect.js'); const user = require('../fakeData/testUser'); -scenarioRouter.post('/', async (req,res) => { +scenarioRouter.post('/', async (req, res) => { try { - - //check: room_id provided¨ - if(!req.query.room_id) throw new Error('No room_id provided'); + //CHECK QUERY PARAMS + //check: room_id provided + if (!req.query.room_id) throw new Error('No room_id provided'); //checK: scenario text provided - if(!req.query.text) throw new Error('No text provided'); + if (!req.query.text) throw new Error('No text provided'); //check: scenario text is long enough - if(req.query.text.length < 3) throw new Error('text must be at least 3 characters long'); + if (req.query.text.length < 3) throw new Error('text must be at least 3 characters long'); - //check: player has enough characters + //start transaction + await db.query('BEGIN'); + + //CHECKS IN DB const playerQuery = await db.query( //might not be necessary, since we will query this table any way and can return the player - 'SELECT * FROM rooms_users WHERE user_id = $1', - [user.id] - ) - if(playerQuery.rows[0].char_count < req.query.text.length) throw new Error('player does not have enough characters left'); + 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY queue_number', + [req.query.room_id] + ); + + //check: player has enough characters + playerQuery.rows.forEach(playerRow => { + if (playerRow.user_id == user.id && playerRow.char_count < req.query.text.length) { + throw new Error('player does not have enough characters left') + }; + }) //check: its the players turn const roomQuery = await db.query( 'SELECT * FROM rooms WHERE id=$1', [req.query.room_id] ); - if(roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); + if (roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); //check: room is not finished - if(roomQuery.rows[0].finished) throw new Error('the story has already been ended'); + if (roomQuery.rows[0].finished) throw new Error('the story has already been ended'); //check: deadline has not passed - if(roomQuery.rows[0].turn_end < new Date()){ - //update player turn + if (roomQuery.rows[0].turn_end < new Date()) { + //++update player turn throw new Error('turn has already passed'); } - + + //MAKE THE TRANSACTION //insert into scenarios + const scenarioQuery = await db.query( + 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES ((SELECT MAX(number_in_room) FROM scenarios WHERE room_id = $3)+1, $1, $2, $3) RETURNING *', + [req.query.text, user.id, req.query.room_id] + ); + if (scenarioQuery.rows[0].number_in_room > 39) { + throw Error('Scenario limit reached! Must create ending'); + } //update player turn - //sets finished if full scenario count + //get the list of players for this room, then calc which one is after the player + let i = 0; + playerQuery.rows.forEach((player, j) => { + if (player.user_id != user.id) return + if (j == (playerQuery.rows.length-1)) return; + i = j+1; + }) + const nextPlayerId = playerQuery.rows[i].user_id; + + // let playerQueueNumber; + // playerQuery.rows.forEach(playerRow => { + // if (playerRow.user_id == user.id) { + // playerQueueNumber = playerRow.queue_number; + // } + // }) + // let nextPlayerId; + // let nextPlayerNumber = playerQueueNumber; + // let firstPlayerId; + // let firstPlayerNumber = playerQueueNumber; + // playerQuery.rows.forEach(row => { + // if (row.user_id == user.id) return; + // if (row.queue_number > playerQueueNumber && row.queue_number < nextPlayerNumber) { + // nextPlayerNumber = row.queue_number; + // nextPlayerId = row.user_id; + // } + // if (row.queue_number < firstPlayerNumber) { + // firstPlayerNumber = row.queue_number; + // firstPlayerId = row.user_id; + // } + // }) + // if (nextPlayerId == user.id) { + // nextPlayerId = firstPlayerId; + // } + if (!nextPlayerId) throw new Error('next player ID is null'); + + //set that player as the next ons + await db.query('UPDATE rooms SET next_player_id = $1 WHERE id = $2', [nextPlayerId, req.query.room_id]); + //update player character count - - } catch (error) { - + await db.query( + 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', + [req.query.text.length, user.id, req.query.room_id] + ); + + //end transaction + await db.query('COMMIT'); + res.status(200).send('new scenario added with id: ' + scenarioQuery.rows[0].id); + } + catch (error) { + db.query('ROLLBACK'); + console.error('FAILED TO ADD NEW SCENARIO: ' + error.message); + res.status(400).send('FAILED TO ADD NEW SCENARIO: ' + error.message) } }); From 3eeb2bf6a09e6f299d42227ac84ae12ab8749610 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 31 Oct 2022 14:39:07 +0100 Subject: [PATCH 021/127] scenario posting accounts for ends --- fakeData/testUser.js | 6 +- routers/scenarioRouter.js | 130 ++++++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 59 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 5ae29f7..39bed3a 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,8 +1,8 @@ const user = { id: 9, - name: 'flandre', - premium: true, - room_keys: 9 + // name: 'flandre', + // premium: true, + // room_keys: 9 } module.exports = user; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 5166887..8b6d9e3 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -5,29 +5,27 @@ const user = require('../fakeData/testUser'); scenarioRouter.post('/', async (req, res) => { - try { - //CHECK QUERY PARAMS - //check: room_id provided - if (!req.query.room_id) throw new Error('No room_id provided'); + const roomId = req.query.room_id; + const scenario = req.query.text; + const isEnd = (req.query.end); - //checK: scenario text provided - if (!req.query.text) throw new Error('No text provided'); + console.log('end is: ', isEnd); - //check: scenario text is long enough - if (req.query.text.length < 3) throw new Error('text must be at least 3 characters long'); + try { - //start transaction - await db.query('BEGIN'); + //CHECKS + if (!roomId) throw new Error('No room_id provided'); + if (!scenario) throw new Error('No text provided'); + if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); - //CHECKS IN DB - const playerQuery = await db.query( //might not be necessary, since we will query this table any way and can return the player + const playerQuery = await db.query( 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY queue_number', - [req.query.room_id] + [roomId] ); //check: player has enough characters playerQuery.rows.forEach(playerRow => { - if (playerRow.user_id == user.id && playerRow.char_count < req.query.text.length) { + if (playerRow.user_id == user.id && playerRow.char_count < scenario.length) { throw new Error('player does not have enough characters left') }; }) @@ -35,7 +33,7 @@ scenarioRouter.post('/', async (req, res) => { //check: its the players turn const roomQuery = await db.query( 'SELECT * FROM rooms WHERE id=$1', - [req.query.room_id] + [roomId] ); if (roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); @@ -48,71 +46,91 @@ scenarioRouter.post('/', async (req, res) => { throw new Error('turn has already passed'); } + //check: can end (if player wants to end) + // if (isEnd) { + // const scenarioCountQuery = await db.query('SELECT COUNT(*) FROM scenarios WHERE room_id=$1', [roomId]); + // if (scenarioCountQuery.rows[0].count < 29) { + // throw new Error('You cannot end the story yet! It needs at least 30 paragraphs'); + // } + // } + + //MAKE THE TRANSACTION + await db.query('BEGIN'); //insert into scenarios const scenarioQuery = await db.query( 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES ((SELECT MAX(number_in_room) FROM scenarios WHERE room_id = $3)+1, $1, $2, $3) RETURNING *', - [req.query.text, user.id, req.query.room_id] + [scenario, user.id, roomId] ); if (scenarioQuery.rows[0].number_in_room > 39) { throw Error('Scenario limit reached! Must create ending'); } - //update player turn - //get the list of players for this room, then calc which one is after the player + + //update player turn, deadline, and finished (if ending) let i = 0; playerQuery.rows.forEach((player, j) => { if (player.user_id != user.id) return - if (j == (playerQuery.rows.length-1)) return; - i = j+1; + if (j == (playerQuery.rows.length - 1)) return; + i = j + 1; }) const nextPlayerId = playerQuery.rows[i].user_id; - - // let playerQueueNumber; - // playerQuery.rows.forEach(playerRow => { - // if (playerRow.user_id == user.id) { - // playerQueueNumber = playerRow.queue_number; - // } - // }) - // let nextPlayerId; - // let nextPlayerNumber = playerQueueNumber; - // let firstPlayerId; - // let firstPlayerNumber = playerQueueNumber; - // playerQuery.rows.forEach(row => { - // if (row.user_id == user.id) return; - // if (row.queue_number > playerQueueNumber && row.queue_number < nextPlayerNumber) { - // nextPlayerNumber = row.queue_number; - // nextPlayerId = row.user_id; - // } - // if (row.queue_number < firstPlayerNumber) { - // firstPlayerNumber = row.queue_number; - // firstPlayerId = row.user_id; - // } - // }) - // if (nextPlayerId == user.id) { - // nextPlayerId = firstPlayerId; - // } - if (!nextPlayerId) throw new Error('next player ID is null'); - - //set that player as the next ons - await db.query('UPDATE rooms SET next_player_id = $1 WHERE id = $2', [nextPlayerId, req.query.room_id]); - - //update player character count + if (isEnd) nextPlayerId = null; await db.query( - 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', - [req.query.text.length, user.id, req.query.room_id] + `UPDATE rooms + SET + next_player_id = $1, + turn_end = $4, + finished = $3 + WHERE id = $2`, + [ + nextPlayerId, + roomId, + isEnd, + (isEnd ? null : new Date(Date.now() + 172800000)) + ] ); + //update player character count + if (!isEnd) { + await db.query( + 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', + [scenario.length, user.id, roomId] + ); + } + + if (isEnd) { + await db.query( + ` + UPDATE users + SET room_keys = room_keys + 1 + WHERE EXISTS( + SELECT FROM rooms_users + WHERE rooms_users.room_id = $1 + AND rooms_users.user_id = users.id + ); + `, + [roomId] + ) + } + //end transaction await db.query('COMMIT'); - res.status(200).send('new scenario added with id: ' + scenarioQuery.rows[0].id); + if (isEnd) { + res.status(200).send('new scenario added with id: ' + scenarioQuery.rows[0].id); + } + else { + res.status(200).send('you ended the story! And got a key!: ' + scenarioQuery.rows[0].id); + } } catch (error) { + db.query('ROLLBACK'); console.error('FAILED TO ADD NEW SCENARIO: ' + error.message); - res.status(400).send('FAILED TO ADD NEW SCENARIO: ' + error.message) + res.status(400).send('FAILED TO ADD NEW SCENARIO: ' + error.message); + } }); -module.exports = scenarioRouter; \ No newline at end of file +module.exports = scenarioRouter; \ No newline at end of file From e8da14a79287944d880009a54b42eb1264f1fb91 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 31 Oct 2022 16:43:14 +0100 Subject: [PATCH 022/127] get available rooms route --- fakeData/testUser.js | 2 +- routers/roomRouter.js | 122 ++++++++++++++++++++++++++------------ routers/scenarioRouter.js | 13 +--- 3 files changed, 87 insertions(+), 50 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 39bed3a..607f973 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 9, + id: 30, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 9d8f353..9bc49f3 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -9,30 +9,75 @@ roomRouter.get('/', async (req, res, next) => { }); roomRouter.get('/available', async (req, res, next) => { - const query = await db.query(` - SELECT - rooms.id AS room_id, - rooms.title AS title, - rooms.description AS description, - users.name AS user_name, - (rooms.creator_id = rooms_users.user_id) AS creator - FROM rooms - JOIN rooms_users - ON rooms.id = rooms_users.room_id - JOIN users - ON rooms_users.user_id = users.id - WHERE - rooms.full = false - AND finished = false - AND creator_id != 8 - AND users.id != 8 - - - -- also join with scenario table to count the number of scenarios - -- salad hunt should not appear below! since the user is in it. How to remove that one? must filter rooms that has a corresponding row with our name in rooms_users - `) - console.log(query); - res.json(query.rows) + + try { + + const query = await db.query( + ` + SELECT + rooms.id AS room_id, + rooms.title AS title, + rooms.description AS description, + users.name AS user_name, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + COUNT(scenarios.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms.id = rooms_users.room_id + JOIN users ON rooms_users.user_id = users.id + JOIN scenarios ON scenarios.room_id = rooms.id + WHERE + rooms.full = false + AND rooms.finished = false + AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) + GROUP BY (rooms_users.user_id, rooms.id, users.name) + ORDER BY room_id; + `, + [user.id] + ) + + + let newRooms = []; + let oldRooms = []; + + query.rows.forEach(room => { + if (room.scenario_count < 4) AddRoomToList(newRooms, room); + else AddRoomToList(oldRooms, room); + }) + + res.status(200).json({ new: newRooms, old: oldRooms }); + + } catch (error) { + + res.status(400).send('unable to get avaliable rooms: ', error.message); + + } + + + + function AddRoomToList(roomArray, roomToAdd) { + + if (roomArray.length >= 3) return; + + let roomAlreadyInArray = false; + + roomArray.forEach(roomToCheck => { + if (roomToCheck.room_id == roomToAdd.room_id) { + roomAlreadyInArray = true; + if (roomToAdd.user_name == roomToAdd.creator) return; + roomToCheck.writers.push(roomToAdd.user_name); + } + }) + + if (!roomAlreadyInArray) { + if (roomToAdd.creator != roomToAdd.user_name) { + roomToAdd.writers = [roomToAdd.user_name]; + } + else roomToAdd.writers = []; + delete roomToAdd.user_name; + roomArray.push(roomToAdd); + } + } + }); roomRouter.post('/', async (req, res) => { @@ -46,8 +91,8 @@ roomRouter.post('/', async (req, res) => { res.status(400).send('cant create room. Title must be at least 3 chars long'); return; } - if (req.query.title.length > 20) { - res.status(400).send('cant create room. Title can me maximum 20 characters long'); + if (req.query.title.length > 50) { + res.status(400).send('cant create room. Title can me maximum 50 characters long'); return; } if (!req.query.description) { @@ -92,7 +137,7 @@ roomRouter.post('/', async (req, res) => { [newRoomId, user.id, 0] ); await db.query( - 'INSERT INTO scenarios(number, text, creator_id, room_id) VALUES($1, $2, $3, $4)', + 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES($1, $2, $3, $4)', [0, req.query.scenario, user.id, newRoomId] ); await db.query('COMMIT'); @@ -107,19 +152,19 @@ roomRouter.post('/', async (req, res) => { }); roomRouter.post('/join', async (req, res) => { - + try { - if(!req.query.room_id) throw new Error('Please provide a room_id!'); + if (!req.query.room_id) throw new Error('Please provide a room_id!'); await db.query('BEGIN'); - + //make sure room is not full or finished const roomQuery = await db.query('SELECT * FROM rooms WHERE id=$1', [req.query.room_id]); - if(roomQuery.rows.length == 0) throw new Error('There is no room with that id'); + if (roomQuery.rows.length == 0) throw new Error('There is no room with that id'); if (roomQuery.rows[0].full) throw new Error('Room is full'); if (roomQuery.rows[0].finished) throw new Error('Story has already been finished'); - + //add players to rooms_users const playerQuery = await db.query( 'SELECT * FROM rooms_users WHERE room_id = $1', @@ -134,23 +179,23 @@ roomRouter.post('/join', async (req, res) => { //add a turn_end timestamp to room await db.query( `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, //deadline 2 days from now - [req.query.room_id] + [req.query.room_id] ); - + //make room full if full - if(playerQuery.rows.length == 3){ + if (playerQuery.rows.length == 3) { await db.query( 'UPDATE rooms SET "full"=true WHERE id=$1', [req.query.room_id] ); } - + //set next player await db.query( 'UPDATE rooms SET next_player_id=$1 WHERE id=$2', [user.id, req.query.room_id] ); - + //commit await db.query('COMMIT'); res.status(200).send('Successfully joined the room!'); @@ -163,4 +208,5 @@ roomRouter.post('/join', async (req, res) => { }); -module.exports = roomRouter; \ No newline at end of file +module.exports = roomRouter; + diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 8b6d9e3..58c018a 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -7,7 +7,7 @@ scenarioRouter.post('/', async (req, res) => { const roomId = req.query.room_id; const scenario = req.query.text; - const isEnd = (req.query.end); + const isEnd = (req.query.end == true); console.log('end is: ', isEnd); @@ -46,15 +46,6 @@ scenarioRouter.post('/', async (req, res) => { throw new Error('turn has already passed'); } - //check: can end (if player wants to end) - // if (isEnd) { - // const scenarioCountQuery = await db.query('SELECT COUNT(*) FROM scenarios WHERE room_id=$1', [roomId]); - // if (scenarioCountQuery.rows[0].count < 29) { - // throw new Error('You cannot end the story yet! It needs at least 30 paragraphs'); - // } - // } - - //MAKE THE TRANSACTION await db.query('BEGIN'); //insert into scenarios @@ -115,7 +106,7 @@ scenarioRouter.post('/', async (req, res) => { //end transaction await db.query('COMMIT'); - if (isEnd) { + if (!isEnd) { res.status(200).send('new scenario added with id: ' + scenarioQuery.rows[0].id); } else { From 94a976827608a5e5e5650365f53870a27bf5b553 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 09:36:08 +0100 Subject: [PATCH 023/127] get user room route --- fakeData/testUser.js | 2 +- routers/roomRouter.js | 78 +++++++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 607f973..f9b1ad8 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 30, + id: 3, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 9bc49f3..10eafa0 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -3,6 +3,8 @@ const roomRouter = express.Router(); const db = require('./dbConnect.js') const user = require('../fakeData/testUser'); +//GETTERS + roomRouter.get('/', async (req, res, next) => { const query = await db.query('SELECT * FROM rooms'); res.json(query.rows) @@ -44,7 +46,7 @@ roomRouter.get('/available', async (req, res, next) => { else AddRoomToList(oldRooms, room); }) - res.status(200).json({ new: newRooms, old: oldRooms }); + res.status(200).json({ new: newRooms.slice(0,3), old: oldRooms.slice(0,3) }); } catch (error) { @@ -52,34 +54,48 @@ roomRouter.get('/available', async (req, res, next) => { } +}); +roomRouter.get('/user', async (req, res, next) => { - function AddRoomToList(roomArray, roomToAdd) { + try { - if (roomArray.length >= 3) return; + const query = await db.query( + ` + SELECT + rooms.id AS room_id, + title, + description, + finished, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user_name, + (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count, + case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn + FROM rooms + JOIN rooms_users ON rooms_users.room_id = rooms.id + WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1); + `, + [user.id] + ) - let roomAlreadyInArray = false; + let rooms = []; - roomArray.forEach(roomToCheck => { - if (roomToCheck.room_id == roomToAdd.room_id) { - roomAlreadyInArray = true; - if (roomToAdd.user_name == roomToAdd.creator) return; - roomToCheck.writers.push(roomToAdd.user_name); - } + query.rows.forEach(room => { + AddRoomToList(rooms, room); }) - if (!roomAlreadyInArray) { - if (roomToAdd.creator != roomToAdd.user_name) { - roomToAdd.writers = [roomToAdd.user_name]; - } - else roomToAdd.writers = []; - delete roomToAdd.user_name; - roomArray.push(roomToAdd); - } + res.status(200).json(rooms); + + } catch (error) { + + res.status(400).send('unable to get your rooms: ' + error.message); + } }); +//SETTERS + roomRouter.post('/', async (req, res) => { //ERROR CHECKS @@ -208,5 +224,31 @@ roomRouter.post('/join', async (req, res) => { }); +//EXPORT + module.exports = roomRouter; +//FUNCTIONS + +function AddRoomToList(roomArray, roomToAdd) { + + let roomAlreadyInArray = false; + + roomArray.forEach(roomToCheck => { + if (roomToCheck.room_id == roomToAdd.room_id) { + roomAlreadyInArray = true; + if (roomToAdd.user_name == roomToAdd.creator) return; + roomToCheck.writers.push(roomToAdd.user_name); + } + }) + + if (!roomAlreadyInArray) { + if (roomToAdd.creator != roomToAdd.user_name) { + roomToAdd.writers = [roomToAdd.user_name]; + } + else roomToAdd.writers = []; + delete roomToAdd.user_name; + roomArray.push(roomToAdd); + } +} + From 110b47cd34cf48b50901f76b1d3f77183336d252 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 09:47:19 +0100 Subject: [PATCH 024/127] get archive route --- fakeData/testUser.js | 2 +- routers/roomRouter.js | 57 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index f9b1ad8..a64e81f 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 3, + id: 18, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 10eafa0..90bedce 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -17,10 +17,10 @@ roomRouter.get('/available', async (req, res, next) => { const query = await db.query( ` SELECT - rooms.id AS room_id, + rooms.id, rooms.title AS title, rooms.description AS description, - users.name AS user_name, + users.name AS user, (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, COUNT(scenarios.id) AS scenario_count FROM rooms @@ -32,7 +32,7 @@ roomRouter.get('/available', async (req, res, next) => { AND rooms.finished = false AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) GROUP BY (rooms_users.user_id, rooms.id, users.name) - ORDER BY room_id; + ORDER BY id; `, [user.id] ) @@ -63,12 +63,12 @@ roomRouter.get('/user', async (req, res, next) => { const query = await db.query( ` SELECT - rooms.id AS room_id, + rooms.id, title, description, finished, (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - (SELECT name FROM users WHERE id = rooms_users.user_id) AS user_name, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count, case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn FROM rooms @@ -94,6 +94,41 @@ roomRouter.get('/user', async (req, res, next) => { }); +roomRouter.get('/archive', async (req, res, next) => { + + try { + + const query = await db.query( + ` + SELECT + rooms.id, + title, + description, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, + (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms_users.room_id = rooms.id + WHERE rooms.finished = true + ` + ) + + let rooms = []; + + query.rows.forEach(room => { + AddRoomToList(rooms, room); + }) + + res.status(200).json(rooms); + + } catch (error) { + + res.status(400).send('unable to get your rooms: ' + error.message); + + } + +}); + //SETTERS roomRouter.post('/', async (req, res) => { @@ -235,19 +270,19 @@ function AddRoomToList(roomArray, roomToAdd) { let roomAlreadyInArray = false; roomArray.forEach(roomToCheck => { - if (roomToCheck.room_id == roomToAdd.room_id) { + if (roomToCheck.id == roomToAdd.id) { roomAlreadyInArray = true; - if (roomToAdd.user_name == roomToAdd.creator) return; - roomToCheck.writers.push(roomToAdd.user_name); + if (roomToAdd.user == roomToAdd.creator) return; + roomToCheck.writers.push(roomToAdd.user); } }) if (!roomAlreadyInArray) { - if (roomToAdd.creator != roomToAdd.user_name) { - roomToAdd.writers = [roomToAdd.user_name]; + if (roomToAdd.creator != roomToAdd.user) { + roomToAdd.writers = [roomToAdd.user]; } else roomToAdd.writers = []; - delete roomToAdd.user_name; + delete roomToAdd.user; roomArray.push(roomToAdd); } } From c7057fa15c7f81c6ddd87d581a5d99924bc25e88 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 13:53:01 +0100 Subject: [PATCH 025/127] Cleanup --- fakeData/testUser.js | 2 +- routers/roomRouter.js | 216 ++++++++++++++++---------------------- routers/scenarioRouter.js | 13 ++- 3 files changed, 103 insertions(+), 128 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index a64e81f..daf75c5 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 18, + id: 13, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 90bedce..a93eb79 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -12,120 +12,73 @@ roomRouter.get('/', async (req, res, next) => { roomRouter.get('/available', async (req, res, next) => { - try { - - const query = await db.query( - ` - SELECT - rooms.id, - rooms.title AS title, - rooms.description AS description, - users.name AS user, - (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - COUNT(scenarios.id) AS scenario_count - FROM rooms - JOIN rooms_users ON rooms.id = rooms_users.room_id - JOIN users ON rooms_users.user_id = users.id - JOIN scenarios ON scenarios.room_id = rooms.id - WHERE - rooms.full = false - AND rooms.finished = false - AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) - GROUP BY (rooms_users.user_id, rooms.id, users.name) - ORDER BY id; - `, - [user.id] - ) - - - let newRooms = []; - let oldRooms = []; - - query.rows.forEach(room => { - if (room.scenario_count < 4) AddRoomToList(newRooms, room); - else AddRoomToList(oldRooms, room); - }) - - res.status(200).json({ new: newRooms.slice(0,3), old: oldRooms.slice(0,3) }); - - } catch (error) { - - res.status(400).send('unable to get avaliable rooms: ', error.message); - - } + RetrieveRooms( + ` + SELECT + rooms.id, + rooms.title AS title, + rooms.description AS description, + users.name AS user, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + COUNT(scenarios.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms.id = rooms_users.room_id + JOIN users ON rooms_users.user_id = users.id + JOIN scenarios ON scenarios.room_id = rooms.id + WHERE + rooms.full = false + AND rooms.finished = false + AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) + GROUP BY (rooms_users.user_id, rooms.id, users.name) + ORDER BY id; + `, + [user.id], + res + ); }); roomRouter.get('/user', async (req, res, next) => { - try { - - const query = await db.query( - ` - SELECT - rooms.id, - title, - description, - finished, - (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, - (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count, - case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn - FROM rooms - JOIN rooms_users ON rooms_users.room_id = rooms.id - WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1); - `, - [user.id] - ) - - let rooms = []; - - query.rows.forEach(room => { - AddRoomToList(rooms, room); - }) - - res.status(200).json(rooms); - - } catch (error) { - - res.status(400).send('unable to get your rooms: ' + error.message); - - } + RetrieveRooms( + ` + SELECT + rooms.id, + title, + description, + finished, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, + (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count, + case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn + FROM rooms + JOIN rooms_users ON rooms_users.room_id = rooms.id + WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1); + `, + [user.id], + res + ); }); roomRouter.get('/archive', async (req, res, next) => { - try { - - const query = await db.query( - ` - SELECT - rooms.id, - title, - description, - (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, - (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count - FROM rooms - JOIN rooms_users ON rooms_users.room_id = rooms.id - WHERE rooms.finished = true - ` - ) - - let rooms = []; - - query.rows.forEach(room => { - AddRoomToList(rooms, room); - }) - - res.status(200).json(rooms); - - } catch (error) { - - res.status(400).send('unable to get your rooms: ' + error.message); - - } + await RetrieveRooms( + ` + SELECT + rooms.id, + title, + description, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, + (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms_users.room_id = rooms.id + WHERE rooms.finished = true + `, + [], + res + ); }); @@ -184,8 +137,8 @@ roomRouter.post('/', async (req, res) => { ); const newRoomId = newRoomRes.rows[0].id; await db.query( - 'INSERT INTO rooms_users(room_id, user_id, queue_number) VALUES($1, $2, $3)', - [newRoomId, user.id, 0] + 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', + [newRoomId, user.id] ); await db.query( 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES($1, $2, $3, $4)', @@ -222,8 +175,8 @@ roomRouter.post('/join', async (req, res) => { [req.query.room_id] ); await db.query( - 'INSERT INTO rooms_users (room_id, user_id, queue_number) VALUES ($1, $2, $3)', - [req.query.room_id, user.id, playerQuery.rows.length] + 'INSERT INTO rooms_users (room_id, user_id) VALUES ($1, $2)', + [req.query.room_id, user.id] ); //alter the rooms table @@ -265,25 +218,36 @@ module.exports = roomRouter; //FUNCTIONS -function AddRoomToList(roomArray, roomToAdd) { +async function RetrieveRooms(queryText, queryParams, res) { - let roomAlreadyInArray = false; - - roomArray.forEach(roomToCheck => { - if (roomToCheck.id == roomToAdd.id) { - roomAlreadyInArray = true; - if (roomToAdd.user == roomToAdd.creator) return; - roomToCheck.writers.push(roomToAdd.user); - } - }) + try { + const query = await db.query(queryText, queryParams); + let rooms = []; + query.rows.forEach(room => { - if (!roomAlreadyInArray) { - if (roomToAdd.creator != roomToAdd.user) { - roomToAdd.writers = [roomToAdd.user]; - } - else roomToAdd.writers = []; - delete roomToAdd.user; - roomArray.push(roomToAdd); + let roomAlreadyInArray = false; + + rooms.forEach(roomToCheck => { + if (roomToCheck.id == room.id) { + roomAlreadyInArray = true; + if (room.user == room.creator) return; + roomToCheck.writers.push(room.user); + } + }) + + if (!roomAlreadyInArray) { + if (room.creator != room.user) { + room.writers = [room.user]; + } + else room.writers = []; + delete room.user; + rooms.push(room); + } + }); + res.status(200).json(rooms); + } + catch (error) { + res.status(400).send('unable to get your rooms: ' + error.message); } -} +} \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 58c018a..2296cec 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -19,7 +19,7 @@ scenarioRouter.post('/', async (req, res) => { if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); const playerQuery = await db.query( - 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY queue_number', + 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY user_id', [roomId] ); @@ -104,6 +104,8 @@ scenarioRouter.post('/', async (req, res) => { ) } + if (isEnd || playerQuery.rowCount < 4) await ClearNextTurn(roomId); + //end transaction await db.query('COMMIT'); if (!isEnd) { @@ -123,5 +125,14 @@ scenarioRouter.post('/', async (req, res) => { }); +const ClearNextTurn = async roomId => { + await db.query( + `UPDATE rooms + SET turn_end = null, next_player_id = null + WHERE id = $1;`, + [roomId] + ) +} + module.exports = scenarioRouter; \ No newline at end of file From c914c3c283ed7e5466aacc99db6d600873430959 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 14:11:00 +0100 Subject: [PATCH 026/127] get room info route - back alive --- routers/roomRouter.js | 44 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index a93eb79..67002cb 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -5,9 +5,47 @@ const user = require('../fakeData/testUser'); //GETTERS -roomRouter.get('/', async (req, res, next) => { - const query = await db.query('SELECT * FROM rooms'); - res.json(query.rows) +roomRouter.get('/:id', async (req, res, next) => { + + try { + + const roomQuery = await db.query( + `SELECT title, description, creator_id, next_player_id, turn_end, finished + FROM rooms + WHERE id = $1`, + [req.params.id] + ); + + if(roomQuery.rowCount == 0) throw new Error('Could not find a room with the provided ID'); + + const playerQuery = await db.query( + `SELECT users.id, users.name, rooms_users.char_count + FROM rooms_users + JOIN users ON rooms_users.user_id = users.id + WHERE rooms_users.room_id = $1 + ORDER BY rooms_users.id`, + [req.params.id] + ); + + const scenarioQuery = await db.query( + `SELECT scenario, creator_id + FROM scenarios + WHERE room_id = $1 + ORDER BY id`, + [req.params.id] + ); + + const room = roomQuery.rows[0]; + room.players = playerQuery.rows; + room.scenarios = scenarioQuery.rows; + + res.status(200).send(room); + + } + catch (error) { + + } + }); roomRouter.get('/available', async (req, res, next) => { From fc4c1b5235fbba0a4ab1fae7c311899c5604530e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 15:04:44 +0100 Subject: [PATCH 027/127] refactoring route functions progress --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 143 ++++++++++++++++++++++++++++++++++++++ routers/roomRouter.js | 4 +- routers/scenarioRouter.js | 127 +++++---------------------------- 4 files changed, 165 insertions(+), 111 deletions(-) create mode 100644 routers/dbFunctions.js diff --git a/fakeData/testUser.js b/fakeData/testUser.js index daf75c5..3dff29b 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 13, + id: 22, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js new file mode 100644 index 0000000..7979459 --- /dev/null +++ b/routers/dbFunctions.js @@ -0,0 +1,143 @@ +const db = require('./dbConnect.js'); +const user = require('../fakeData/testUser'); + +async function GetRoomInfo(roomId) { + return await db.query( + 'SELECT * FROM rooms WHERE id=$1', + [roomId] + ); +} + +async function GetPlayersInRoom(roomId) { + return await db.query( + 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY user_id', + [roomId] + ); +} + +function MakeSureDeadlineHasNotPassed(roomQuery) { + if (roomQuery.rows[0].turn_end < new Date()) { + //++update player turn + throw new Error('turn has already passed'); + } +} + +function MakeSurePlayerHasEnoughChars(playerQuery, scenario) { + playerQuery.rows.forEach(playerRow => { + if (playerRow.user_id == user.id && playerRow.char_count < scenario.length) { + throw new Error('player does not have enough characters left'); + }; + }); +} + +async function MakeSureItsNotTheLastTurn(roomId) { + const scenariosQuery = await db.query( + `SELECT * + FROM scenarios + WHERE room_id = $1`, + [roomId] + ); + if (scenariosQuery.rowCount >= 39) + throw Error('Scenario limit reached! Must create ending'); +} + +function MakeSureItsNotFinished(roomQuery) { + if (roomQuery.rows[0].finished) + throw new Error('the story has already been ended'); +} + +function MakeSureItsPlayersTurn(roomQuery) { + if (roomQuery.rows[0].next_player_id != user.id) + throw new Error('its not the logged players turn'); +} + +async function UpdateRoomInfo(isEnd, isFull, roomId, playerQuery) { + await db.query( + `UPDATE rooms + SET + next_player_id = $1, + turn_end = $4, + finished = $3 + WHERE id = $2`, + [ + isEnd || isFull ? null : GetNextPlayerId(playerQuery, isEnd), + roomId, + isEnd, + isEnd || isFull ? null : new Date(Date.now() + 172800000) + ] + ); +} + +async function AddScenario(scenario, roomId) { + const scenarioQuery = await db.query( + 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES ($1, $2, $3) RETURNING *', + [scenario, user.id, roomId] + ); + return scenarioQuery.rows[0].id; +} + +async function BeginTransaction() { + await db.query('BEGIN'); +} + +function GetNextPlayerId(playerQuery, isEnd) { + let i = 0; + playerQuery.rows.forEach((player, j) => { + if (player.user_id != user.id) + return; + if (j == (playerQuery.rows.length - 1)) + return; + i = j + 1; + }); + const nextPlayerId = playerQuery.rows[i].user_id; + if (isEnd) + nextPlayerId = null; + return nextPlayerId; +} + +function Rollback() { + db.query('ROLLBACK'); +} + +async function UpdateCharCount(scenario, roomId) { + await db.query( + 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', + [scenario.length, user.id, roomId] + ); +} + +async function GiveKeyToEachPlayer(roomId) { + await db.query( + ` + UPDATE users + SET room_keys = room_keys + 1 + WHERE EXISTS( + SELECT FROM rooms_users + WHERE rooms_users.room_id = $1 + AND rooms_users.user_id = users.id + ); + `, + [roomId] + ); +} + +async function Commit() { + await db.query('COMMIT'); +} + +module.exports = { + GetRoomInfo, + GetPlayersInRoom, + MakeSureDeadlineHasNotPassed, + MakeSurePlayerHasEnoughChars, + MakeSureItsNotTheLastTurn, + MakeSureItsNotFinished, + MakeSureItsPlayersTurn, + UpdateRoomInfo, + AddScenario, + BeginTransaction, + Rollback, + UpdateCharCount, + GiveKeyToEachPlayer, + Commit +}; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 67002cb..97f2513 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -179,8 +179,8 @@ roomRouter.post('/', async (req, res) => { [newRoomId, user.id] ); await db.query( - 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES($1, $2, $3, $4)', - [0, req.query.scenario, user.id, newRoomId] + 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES($1, $2, $3)', + [req.query.scenario, user.id, newRoomId] ); await db.query('COMMIT'); res.status(200).send('new room added with ID ' + newRoomId); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 2296cec..f050e30 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,7 +1,6 @@ const express = require('express'); const scenarioRouter = express.Router(); -const db = require('./dbConnect.js'); -const user = require('../fakeData/testUser'); +const dbFunctions = require('./dbFunctions'); scenarioRouter.post('/', async (req, res) => { @@ -9,130 +8,42 @@ scenarioRouter.post('/', async (req, res) => { const scenario = req.query.text; const isEnd = (req.query.end == true); - console.log('end is: ', isEnd); - try { - //CHECKS if (!roomId) throw new Error('No room_id provided'); if (!scenario) throw new Error('No text provided'); if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); - const playerQuery = await db.query( - 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY user_id', - [roomId] - ); - - //check: player has enough characters - playerQuery.rows.forEach(playerRow => { - if (playerRow.user_id == user.id && playerRow.char_count < scenario.length) { - throw new Error('player does not have enough characters left') - }; - }) - - //check: its the players turn - const roomQuery = await db.query( - 'SELECT * FROM rooms WHERE id=$1', - [roomId] - ); - if (roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); - - //check: room is not finished - if (roomQuery.rows[0].finished) throw new Error('the story has already been ended'); + const playerQuery = await dbFunctions.GetPlayersInRoom(roomId); + const roomQuery = await dbFunctions.GetRoomInfo(roomId); - //check: deadline has not passed - if (roomQuery.rows[0].turn_end < new Date()) { - //++update player turn - throw new Error('turn has already passed'); - } + dbFunctions.MakeSurePlayerHasEnoughChars(playerQuery, scenario); + dbFunctions.MakeSureItsPlayersTurn(roomQuery); + dbFunctions.MakeSureItsNotFinished(roomQuery); + dbFunctions.MakeSureDeadlineHasNotPassed(roomQuery); + if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); - //MAKE THE TRANSACTION - await db.query('BEGIN'); - //insert into scenarios - const scenarioQuery = await db.query( - 'INSERT INTO scenarios(number_in_room, scenario, creator_id, room_id) VALUES ((SELECT MAX(number_in_room) FROM scenarios WHERE room_id = $3)+1, $1, $2, $3) RETURNING *', - [scenario, user.id, roomId] - ); - if (scenarioQuery.rows[0].number_in_room > 39) { - throw Error('Scenario limit reached! Must create ending'); - } + await dbFunctions.BeginTransaction(); - //update player turn, deadline, and finished (if ending) - let i = 0; - playerQuery.rows.forEach((player, j) => { - if (player.user_id != user.id) return - if (j == (playerQuery.rows.length - 1)) return; - i = j + 1; - }) - const nextPlayerId = playerQuery.rows[i].user_id; - if (isEnd) nextPlayerId = null; - await db.query( - `UPDATE rooms - SET - next_player_id = $1, - turn_end = $4, - finished = $3 - WHERE id = $2`, - [ - nextPlayerId, - roomId, - isEnd, - (isEnd ? null : new Date(Date.now() + 172800000)) - ] - ); + const scenarioId = await dbFunctions.AddScenario(scenario, roomId); + await dbFunctions.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); + if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); + else await dbFunctions.UpdateCharCount(scenario, roomId); - //update player character count - if (!isEnd) { - await db.query( - 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', - [scenario.length, user.id, roomId] - ); - } + await dbFunctions.Commit(); - if (isEnd) { - await db.query( - ` - UPDATE users - SET room_keys = room_keys + 1 - WHERE EXISTS( - SELECT FROM rooms_users - WHERE rooms_users.room_id = $1 - AND rooms_users.user_id = users.id - ); - `, - [roomId] - ) - } + if (!isEnd) res.status(200).send('new scenario added with id: ' + scenarioId); + else res.status(200).send('you ended the story! And got a key!: ' + scenarioId); - if (isEnd || playerQuery.rowCount < 4) await ClearNextTurn(roomId); - - //end transaction - await db.query('COMMIT'); - if (!isEnd) { - res.status(200).send('new scenario added with id: ' + scenarioQuery.rows[0].id); - } - else { - res.status(200).send('you ended the story! And got a key!: ' + scenarioQuery.rows[0].id); - } } catch (error) { - db.query('ROLLBACK'); - console.error('FAILED TO ADD NEW SCENARIO: ' + error.message); + dbFunctions.Rollback(); + console.error(error); res.status(400).send('FAILED TO ADD NEW SCENARIO: ' + error.message); } }); -const ClearNextTurn = async roomId => { - await db.query( - `UPDATE rooms - SET turn_end = null, next_player_id = null - WHERE id = $1;`, - [roomId] - ) -} - - -module.exports = scenarioRouter; \ No newline at end of file +module.exports = scenarioRouter; \ No newline at end of file From b4009bedba33a5bd6c2c9cc78bb3cfad658187ef Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 1 Nov 2022 15:22:32 +0100 Subject: [PATCH 028/127] more cleanups --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 15 ++++++- routers/scenarioRouter.js | 87 +++++++++++++++++++++------------------ 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 3dff29b..d7b9719 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 22, + id: 24, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js index 7979459..288e2bc 100644 --- a/routers/dbFunctions.js +++ b/routers/dbFunctions.js @@ -125,6 +125,18 @@ async function Commit() { await db.query('COMMIT'); } +async function TryTransaction(transaction, res) { + + try { + await transaction(); + } + catch (error) { + db.Rollback(); + console.error(error); + res.status(400).send('Transaction failed: ' + error.message); + } +} + module.exports = { GetRoomInfo, GetPlayersInRoom, @@ -139,5 +151,6 @@ module.exports = { Rollback, UpdateCharCount, GiveKeyToEachPlayer, - Commit + Commit, + TryTransaction }; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index f050e30..592fede 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,49 +1,54 @@ const express = require('express'); const scenarioRouter = express.Router(); -const dbFunctions = require('./dbFunctions'); +const db = require('./dbFunctions'); scenarioRouter.post('/', async (req, res) => { - const roomId = req.query.room_id; - const scenario = req.query.text; - const isEnd = (req.query.end == true); - - try { - - if (!roomId) throw new Error('No room_id provided'); - if (!scenario) throw new Error('No text provided'); - if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); - - const playerQuery = await dbFunctions.GetPlayersInRoom(roomId); - const roomQuery = await dbFunctions.GetRoomInfo(roomId); - - dbFunctions.MakeSurePlayerHasEnoughChars(playerQuery, scenario); - dbFunctions.MakeSureItsPlayersTurn(roomQuery); - dbFunctions.MakeSureItsNotFinished(roomQuery); - dbFunctions.MakeSureDeadlineHasNotPassed(roomQuery); - if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); - - await dbFunctions.BeginTransaction(); - - const scenarioId = await dbFunctions.AddScenario(scenario, roomId); - await dbFunctions.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); - if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); - else await dbFunctions.UpdateCharCount(scenario, roomId); - - await dbFunctions.Commit(); - - if (!isEnd) res.status(200).send('new scenario added with id: ' + scenarioId); - else res.status(200).send('you ended the story! And got a key!: ' + scenarioId); - - } - catch (error) { - - dbFunctions.Rollback(); - console.error(error); - res.status(400).send('FAILED TO ADD NEW SCENARIO: ' + error.message); - - } + db.TryTransaction( + async () => { await TryAddScenario(req.query.room_id, req.query.text, (req.query.end == true), res); }, + res + ) }); -module.exports = scenarioRouter; \ No newline at end of file +module.exports = scenarioRouter; + +async function TryAddScenario(roomId, scenario, isEnd, res) { + + //some initial checks + if (!roomId) + throw new Error('No room_id provided'); + if (!scenario) + throw new Error('No text provided'); + if (scenario.length < 3) + throw new Error('text must be at least 3 characters long'); + + //make queries + const playerQuery = await db.GetPlayersInRoom(roomId); + const roomQuery = await db.GetRoomInfo(roomId); + + //some db checks + db.MakeSurePlayerHasEnoughChars(playerQuery, scenario); + db.MakeSureItsPlayersTurn(roomQuery); + db.MakeSureItsNotFinished(roomQuery); + db.MakeSureDeadlineHasNotPassed(roomQuery); + if (!isEnd) + await db.MakeSureItsNotTheLastTurn(roomId); + + //carry out the transaction + await db.BeginTransaction(); + const scenarioId = await db.AddScenario(scenario, roomId); + await db.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); + if (isEnd) + await db.GiveKeyToEachPlayer(roomId); + else + await db.UpdateCharCount(scenario, roomId); + await db.Commit(); + + //sebd response + if (!isEnd) + res.status(200).send('new scenario added with id: ' + scenarioId); + else + res.status(200).send('you ended the story! And got a key!: ' + scenarioId); +} + From 8afb10af2c60145461e16de583c15b902b1cf464 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 2 Nov 2022 09:39:58 +0100 Subject: [PATCH 029/127] transaction func update --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 17 +++++++++++++---- routers/scenarioRouter.js | 19 +++++++++++-------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index d7b9719..3dff29b 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 24, + id: 22, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js index 288e2bc..b90ebbc 100644 --- a/routers/dbFunctions.js +++ b/routers/dbFunctions.js @@ -47,10 +47,16 @@ function MakeSureItsNotFinished(roomQuery) { } function MakeSureItsPlayersTurn(roomQuery) { - if (roomQuery.rows[0].next_player_id != user.id) + console.log(roomQuery.rows[0]); + if (!roomQuery.rows[0].next_player_id || roomQuery.rows[0].next_player_id != user.id) throw new Error('its not the logged players turn'); } +function MakeSureRoomExists(roomQuery) { + if (roomQuery.rowCount == 0) + throw new Error('No room found with the given id'); +} + async function UpdateRoomInfo(isEnd, isFull, roomId, playerQuery) { await db.query( `UPDATE rooms @@ -125,13 +131,15 @@ async function Commit() { await db.query('COMMIT'); } -async function TryTransaction(transaction, res) { +async function TryTransaction(Transaction, res) { try { - await transaction(); + await BeginTransaction(); + await Transaction(); + await Commit(); } catch (error) { - db.Rollback(); + Rollback(); console.error(error); res.status(400).send('Transaction failed: ' + error.message); } @@ -140,6 +148,7 @@ async function TryTransaction(transaction, res) { module.exports = { GetRoomInfo, GetPlayersInRoom, + MakeSureRoomExists, MakeSureDeadlineHasNotPassed, MakeSurePlayerHasEnoughChars, MakeSureItsNotTheLastTurn, diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 592fede..05e3b66 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -4,6 +4,12 @@ const db = require('./dbFunctions'); scenarioRouter.post('/', async (req, res) => { + // req.Transaction = async () => { + // await TryAddScenario( + // req.query.room_id, req.query.text, (req.query.end == true), res + // ) + // } + db.TryTransaction( async () => { await TryAddScenario(req.query.room_id, req.query.text, (req.query.end == true), res); }, res @@ -28,6 +34,7 @@ async function TryAddScenario(roomId, scenario, isEnd, res) { const roomQuery = await db.GetRoomInfo(roomId); //some db checks + db.MakeSureRoomExists(roomQuery); db.MakeSurePlayerHasEnoughChars(playerQuery, scenario); db.MakeSureItsPlayersTurn(roomQuery); db.MakeSureItsNotFinished(roomQuery); @@ -36,19 +43,15 @@ async function TryAddScenario(roomId, scenario, isEnd, res) { await db.MakeSureItsNotTheLastTurn(roomId); //carry out the transaction - await db.BeginTransaction(); + const scenarioId = await db.AddScenario(scenario, roomId); await db.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); - if (isEnd) - await db.GiveKeyToEachPlayer(roomId); - else - await db.UpdateCharCount(scenario, roomId); - await db.Commit(); + if (isEnd) await db.GiveKeyToEachPlayer(roomId); + else await db.UpdateCharCount(scenario, roomId); - //sebd response + //send response if (!isEnd) res.status(200).send('new scenario added with id: ' + scenarioId); else res.status(200).send('you ended the story! And got a key!: ' + scenarioId); } - From 9688102d58fe43190120a6034987e467efc898c9 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 2 Nov 2022 10:57:15 +0100 Subject: [PATCH 030/127] room router cleanup --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 6 +- routers/roomRouter.js | 132 +++++++++++++------------------------- routers/scenarioRouter.js | 99 ++++++++++++++-------------- routers/userRouter.js | 68 ++++++++++---------- 5 files changed, 131 insertions(+), 176 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 3dff29b..a64e81f 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 22, + id: 18, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js index b90ebbc..5405222 100644 --- a/routers/dbFunctions.js +++ b/routers/dbFunctions.js @@ -131,18 +131,20 @@ async function Commit() { await db.query('COMMIT'); } -async function TryTransaction(Transaction, res) { +async function TryTransaction(req, res, next) { try { await BeginTransaction(); - await Transaction(); + await req.Transaction(); //attach the query function you want to user await Commit(); + res.send(req.responseMessage); //attach a response message to send on a successfull transaction } catch (error) { Rollback(); console.error(error); res.status(400).send('Transaction failed: ' + error.message); } + } module.exports = { diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 97f2513..6a842e5 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -1,14 +1,14 @@ const express = require('express'); const roomRouter = express.Router(); const db = require('./dbConnect.js') +const dbFunctions = require('./dbFunctions') const user = require('../fakeData/testUser'); -//GETTERS - -roomRouter.get('/:id', async (req, res, next) => { +//GETTER FUNCTIONS +const GetRoomData = async (req, res, next) => { try { - + const roomQuery = await db.query( `SELECT title, description, creator_id, next_player_id, turn_end, finished FROM rooms @@ -16,7 +16,7 @@ roomRouter.get('/:id', async (req, res, next) => { [req.params.id] ); - if(roomQuery.rowCount == 0) throw new Error('Could not find a room with the provided ID'); + if (roomQuery.rowCount == 0) throw new Error('Could not find a room with the provided ID'); const playerQuery = await db.query( `SELECT users.id, users.name, rooms_users.char_count @@ -41,14 +41,14 @@ roomRouter.get('/:id', async (req, res, next) => { res.status(200).send(room); - } + } catch (error) { - + console.error(error); + res.status(400).send('Failed to get room: ' + error.message); } -}); - -roomRouter.get('/available', async (req, res, next) => { +} +const GetAvaliableRooms = async (req, res, next) => { RetrieveRooms( ` @@ -74,9 +74,8 @@ roomRouter.get('/available', async (req, res, next) => { res ); -}); - -roomRouter.get('/user', async (req, res, next) => { +} +const GetUserRooms = async (req, res, next) => { RetrieveRooms( ` @@ -97,11 +96,10 @@ roomRouter.get('/user', async (req, res, next) => { res ); -}); - -roomRouter.get('/archive', async (req, res, next) => { +} +const GetArchive = async (req, res) => { - await RetrieveRooms( + RetrieveRooms( ` SELECT rooms.id, @@ -118,53 +116,24 @@ roomRouter.get('/archive', async (req, res, next) => { res ); -}); +} -//SETTERS +//POST TRANSACTION FUNCTIONS +const AttachAddRoomTransaction = async (req, res, next) => { -roomRouter.post('/', async (req, res) => { + req.Transaction = async () => { + //ERROR CHECKS + if (!req.query.title) throw new Error('Please provide a title'); + if (req.query.title.length <= 3) throw new Error('Title must be at least 3 chars long'); + if (req.query.title.length > 50) throw new Error('Title can me maximum 50 characters long'); + if (!req.query.description) throw new Error('Please provide a description'); + if (req.query.description.length <= 3) throw new Error('Description must be at least 3 chars long'); + if (req.query.description.length > 200) throw new Error('Description can be at max 200 characters'); + if (!req.query.scenario) throw new Error('Please provide a starting scenario'); + if (req.query.scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); + if (req.query.scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); - //ERROR CHECKS - if (!req.query.title) { - res.status(400).send('cant create room. Please provide a title'); - return; - } - if (req.query.title.length <= 3) { - res.status(400).send('cant create room. Title must be at least 3 chars long'); - return; - } - if (req.query.title.length > 50) { - res.status(400).send('cant create room. Title can me maximum 50 characters long'); - return; - } - if (!req.query.description) { - res.status(400).send('cant create room. Please provide a description'); - return; - } - if (req.query.description.length <= 3) { - res.status(400).send('cant create room. Title must be at least 3 chars long'); - return; - } - if (req.query.description.length > 200) { - res.status(400).send('cant create room. Description can be at max 200 characters'); - return; - } - if (!req.query.scenario) { - res.status(400).send('cant create room. Please provide a starting scenario'); - return; - } - if (req.query.scenario.length <= 19) { - res.status(400).send('cant create room. starting scenario must be at least 20 characters'); - return; - } - if (req.query.scenario.length > 500) { - res.status(400).send('cant create room. Starting scenario can be at max 500 characters'); - return; - } - - //TRY ADD TO DATABASE - try { - await db.query('BEGIN'); + //TRY ADD TO DATABASE await db.query( 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', [user.id] @@ -182,25 +151,16 @@ roomRouter.post('/', async (req, res) => { 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES($1, $2, $3)', [req.query.scenario, user.id, newRoomId] ); - await db.query('COMMIT'); - res.status(200).send('new room added with ID ' + newRoomId); + req.responseMessage = 'new room added with ID ' + newRoomId; } - catch (e) { - db.query('ROLLBACK'); - console.error('FAILED TO ADD NEW ROOM: ' + e.message); - res.status(400).send('FAILED TO ADD NEW ROOM: ' + e.message) - } - -}); -roomRouter.post('/join', async (req, res) => { - - try { + next(); +} +const AttachJoinRoomTransaction = async (req, res, next) => { + req.Transaction = async () => { if (!req.query.room_id) throw new Error('Please provide a room_id!'); - await db.query('BEGIN'); - //make sure room is not full or finished const roomQuery = await db.query('SELECT * FROM rooms WHERE id=$1', [req.query.room_id]); if (roomQuery.rows.length == 0) throw new Error('There is no room with that id'); @@ -238,24 +198,24 @@ roomRouter.post('/join', async (req, res) => { [user.id, req.query.room_id] ); - //commit - await db.query('COMMIT'); - res.status(200).send('Successfully joined the room!'); - } - catch (e) { - db.query('ROLLBACK'); - console.error('FAILED TO JOIN ROOM: ' + e.message); - res.status(400).send('FAILED TO JOIN ROOM: ' + e.message) + req.responseMessage = 'Successfully joined the room!'; } -}); + next(); +} -//EXPORT +//MOUNT ROUTes +roomRouter.get('/data/:id', GetRoomData); +roomRouter.get('/available', GetAvaliableRooms); +roomRouter.get('/user', GetUserRooms); +roomRouter.get('/archive', GetArchive); +roomRouter.post('/', AttachAddRoomTransaction, dbFunctions.TryTransaction); +roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); +//EXPORT module.exports = roomRouter; //FUNCTIONS - async function RetrieveRooms(queryText, queryParams, res) { try { diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 05e3b66..6bf93f8 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -2,56 +2,51 @@ const express = require('express'); const scenarioRouter = express.Router(); const db = require('./dbFunctions'); -scenarioRouter.post('/', async (req, res) => { - - // req.Transaction = async () => { - // await TryAddScenario( - // req.query.room_id, req.query.text, (req.query.end == true), res - // ) - // } - - db.TryTransaction( - async () => { await TryAddScenario(req.query.room_id, req.query.text, (req.query.end == true), res); }, - res - ) - -}); - -module.exports = scenarioRouter; - -async function TryAddScenario(roomId, scenario, isEnd, res) { - - //some initial checks - if (!roomId) - throw new Error('No room_id provided'); - if (!scenario) - throw new Error('No text provided'); - if (scenario.length < 3) - throw new Error('text must be at least 3 characters long'); - - //make queries - const playerQuery = await db.GetPlayersInRoom(roomId); - const roomQuery = await db.GetRoomInfo(roomId); - - //some db checks - db.MakeSureRoomExists(roomQuery); - db.MakeSurePlayerHasEnoughChars(playerQuery, scenario); - db.MakeSureItsPlayersTurn(roomQuery); - db.MakeSureItsNotFinished(roomQuery); - db.MakeSureDeadlineHasNotPassed(roomQuery); - if (!isEnd) - await db.MakeSureItsNotTheLastTurn(roomId); - - //carry out the transaction - - const scenarioId = await db.AddScenario(scenario, roomId); - await db.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); - if (isEnd) await db.GiveKeyToEachPlayer(roomId); - else await db.UpdateCharCount(scenario, roomId); - - //send response - if (!isEnd) - res.status(200).send('new scenario added with id: ' + scenarioId); - else - res.status(200).send('you ended the story! And got a key!: ' + scenarioId); +const AttachAddScenarioTransaction = async (req, res, next) => { + + req.Transaction = async () => { + + const roomId = req.query.room_id; + const scenario = req.query.text; + const isEnd = (req.query.end == true); + + if (!roomId) + throw new Error('No room_id provided'); + if (!scenario) + throw new Error('No text provided'); + if (scenario.length < 3) + throw new Error('text must be at least 3 characters long'); + + //make queries + const playerQuery = await db.GetPlayersInRoom(roomId); + const roomQuery = await db.GetRoomInfo(roomId); + + //some db checks + db.MakeSureRoomExists(roomQuery); + db.MakeSurePlayerHasEnoughChars(playerQuery, scenario); + db.MakeSureItsPlayersTurn(roomQuery); + db.MakeSureItsNotFinished(roomQuery); + db.MakeSureDeadlineHasNotPassed(roomQuery); + if (!isEnd) + await db.MakeSureItsNotTheLastTurn(roomId); + + //carry out the transaction + const scenarioId = await db.AddScenario(scenario, roomId); + await db.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); + if (isEnd) await db.GiveKeyToEachPlayer(roomId); + else await db.UpdateCharCount(scenario, roomId); + + //send response + if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId + // res.status(200).send('new scenario added with id: ' + scenarioId); + else + //res.status(200).send('you ended the story! And got a key!: ' + scenarioId); + req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + } + + next(); } + +scenarioRouter.post('/', AttachAddScenarioTransaction, db.TryTransaction); + +module.exports = scenarioRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index c308085..2d3971b 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -3,50 +3,48 @@ const userRouter = express.Router(); const db = require('./dbConnect.js') const user = require('../fakeData/testUser'); //remove in live v. -userRouter.get('/', async (req, res) => { +const AddNewUser = async (req, res, next) => { - const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented - - if(!query.rows){ - res.status(400).send('Unable to get users. Query returned nothing'); - } - else if(query.rows.length < 1){ - res.status(400).send('Couldnt not find any user with that id'); - } - else if(query.rows.length > 2){ - res.status(400).send('Query returned multiple users'); - } - else{ - res.json(query.rows[0]); - } -}); + try { -userRouter.post('/', async (req, res) => { + if (!req.query.name) throw new Error('No name provided') + if (req.query.name.length < 4) throw new Error('Name must be at least 4 characters') + if (req.query.name.length > 20) throw new Error('Name must be max 20 characters') - if(!req.query.name){ - res.status(400).send('cant create user. No name was provided'); - return; - } + await db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name]); + + res.status(201).send('User created successfully!'); - if(req.query.name.length < 4){ - res.status(400).send('cant create user. name must be at least 4 characters long'); - return; } + catch (error) { + + res.status(400).send('Cant create user: ' + error.message); - if(req.query.name.length > 20){ - res.status(400).send('cant create user. name must be maximum 20 characters long'); - return } - db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name], (error, results) => { - if (error) { - res.status(400).send(error.detail); - return; - } - res.status(201).send('User created successfully!'); - }) +} + +const GetUserData = async (req, res, next) => { + + try { + + const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented + + if (!query.rows) throw new Error('Query returned nothing'); + if (query.rowCount < 1) throw new Error('Found no user with that id'); + if (query.rows.length > 2) throw new Error('Query returned multiple users'); + + res.json(query.rows[0]); + + } + catch (error) { + res.status(400).send('Unable to get user. ' + error.message); + } + +} -}); +userRouter.get('/', GetUserData); +userRouter.post('/', AddNewUser); module.exports = userRouter; From 4c024a5615c09b6599bb78bc93a2650834390c92 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 2 Nov 2022 11:56:02 +0100 Subject: [PATCH 031/127] cleanup --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 64 ++++++++++----- routers/roomRouter.js | 164 +++++++++++++++++--------------------- routers/scenarioRouter.js | 33 +++----- 4 files changed, 133 insertions(+), 130 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index a64e81f..3137949 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 18, + id: 34, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js index 5405222..4047fc5 100644 --- a/routers/dbFunctions.js +++ b/routers/dbFunctions.js @@ -2,29 +2,53 @@ const db = require('./dbConnect.js'); const user = require('../fakeData/testUser'); async function GetRoomInfo(roomId) { - return await db.query( + const roomQuery = await db.query( 'SELECT * FROM rooms WHERE id=$1', [roomId] ); + + MakeSureRoomExists(roomQuery); + + return roomQuery.rows[0]; } async function GetPlayersInRoom(roomId) { - return await db.query( - 'SELECT * FROM rooms_users WHERE room_id = $1 ORDER BY user_id', + const query = await db.query( + `SELECT users.id, users.name, rooms_users.char_count, active + FROM rooms_users + JOIN users ON rooms_users.user_id = users.id + WHERE rooms_users.room_id = $1 + ORDER BY rooms_users.id`, + [roomId] + ); + + return query.rows; +} + +const GetScenariosInRoom = async (roomId) => { + + const scenarioQuery = await db.query( + `SELECT scenario, creator_id + FROM scenarios + WHERE room_id = $1 + ORDER BY id`, [roomId] ); + + return scenarioQuery.rows; + } -function MakeSureDeadlineHasNotPassed(roomQuery) { - if (roomQuery.rows[0].turn_end < new Date()) { +function MakeSureDeadlineHasNotPassed(room) { + if (room.turn_end < new Date()) { //++update player turn throw new Error('turn has already passed'); } } -function MakeSurePlayerHasEnoughChars(playerQuery, scenario) { - playerQuery.rows.forEach(playerRow => { - if (playerRow.user_id == user.id && playerRow.char_count < scenario.length) { +function MakeSurePlayerHasEnoughChars(players, scenario) { + players.forEach(player => { + if (player.user_id == user.id && player.char_count < scenario.length) { throw new Error('player does not have enough characters left'); }; }); @@ -41,14 +65,13 @@ async function MakeSureItsNotTheLastTurn(roomId) { throw Error('Scenario limit reached! Must create ending'); } -function MakeSureItsNotFinished(roomQuery) { - if (roomQuery.rows[0].finished) +function MakeSureItsNotFinished(room) { + if (room.finished) throw new Error('the story has already been ended'); } -function MakeSureItsPlayersTurn(roomQuery) { - console.log(roomQuery.rows[0]); - if (!roomQuery.rows[0].next_player_id || roomQuery.rows[0].next_player_id != user.id) +function MakeSureItsPlayersTurn(room) { + if (!room.next_player_id || room.next_player_id != user.id) throw new Error('its not the logged players turn'); } @@ -57,7 +80,7 @@ function MakeSureRoomExists(roomQuery) { throw new Error('No room found with the given id'); } -async function UpdateRoomInfo(isEnd, isFull, roomId, playerQuery) { +async function UpdateRoomInfo(isEnd, isFull, roomId, players) { await db.query( `UPDATE rooms SET @@ -66,7 +89,7 @@ async function UpdateRoomInfo(isEnd, isFull, roomId, playerQuery) { finished = $3 WHERE id = $2`, [ - isEnd || isFull ? null : GetNextPlayerId(playerQuery, isEnd), + isEnd || isFull ? null : GetNextPlayerId(players, isEnd), roomId, isEnd, isEnd || isFull ? null : new Date(Date.now() + 172800000) @@ -86,16 +109,16 @@ async function BeginTransaction() { await db.query('BEGIN'); } -function GetNextPlayerId(playerQuery, isEnd) { +function GetNextPlayerId(players, isEnd) { let i = 0; - playerQuery.rows.forEach((player, j) => { + players.forEach((player, j) => { if (player.user_id != user.id) return; - if (j == (playerQuery.rows.length - 1)) + if (j == (players.length - 1)) return; i = j + 1; }); - const nextPlayerId = playerQuery.rows[i].user_id; + const nextPlayerId = players[i].user_id; if (isEnd) nextPlayerId = null; return nextPlayerId; @@ -144,12 +167,13 @@ async function TryTransaction(req, res, next) { console.error(error); res.status(400).send('Transaction failed: ' + error.message); } - + } module.exports = { GetRoomInfo, GetPlayersInRoom, + GetScenariosInRoom, MakeSureRoomExists, MakeSureDeadlineHasNotPassed, MakeSurePlayerHasEnoughChars, diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 6a842e5..e59f525 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -1,46 +1,17 @@ const express = require('express'); const roomRouter = express.Router(); -const db = require('./dbConnect.js') -const dbFunctions = require('./dbFunctions') +const db = require('./dbConnect.js'); +const dbFunctions = require('./dbFunctions'); const user = require('../fakeData/testUser'); //GETTER FUNCTIONS const GetRoomData = async (req, res, next) => { try { - - const roomQuery = await db.query( - `SELECT title, description, creator_id, next_player_id, turn_end, finished - FROM rooms - WHERE id = $1`, - [req.params.id] - ); - - if (roomQuery.rowCount == 0) throw new Error('Could not find a room with the provided ID'); - - const playerQuery = await db.query( - `SELECT users.id, users.name, rooms_users.char_count - FROM rooms_users - JOIN users ON rooms_users.user_id = users.id - WHERE rooms_users.room_id = $1 - ORDER BY rooms_users.id`, - [req.params.id] - ); - - const scenarioQuery = await db.query( - `SELECT scenario, creator_id - FROM scenarios - WHERE room_id = $1 - ORDER BY id`, - [req.params.id] - ); - - const room = roomQuery.rows[0]; - room.players = playerQuery.rows; - room.scenarios = scenarioQuery.rows; - + const room = await dbFunctions.GetRoomInfo(req.params.id) + room.players = await dbFunctions.GetPlayersInRoom(req.params.id); + room.scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); res.status(200).send(room); - } catch (error) { console.error(error); @@ -48,11 +19,10 @@ const GetRoomData = async (req, res, next) => { } } -const GetAvaliableRooms = async (req, res, next) => { +const AttachAvailableRoomsQuery = async (req, res, next) => { - RetrieveRooms( - ` - SELECT + req.roomQuery = ( + `SELECT rooms.id, rooms.title AS title, rooms.description AS description, @@ -68,18 +38,16 @@ const GetAvaliableRooms = async (req, res, next) => { AND rooms.finished = false AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) GROUP BY (rooms_users.user_id, rooms.id, users.name) - ORDER BY id; - `, - [user.id], - res + ORDER BY id;` ); + req.roomQueryParams = [user.id]; + next(); } -const GetUserRooms = async (req, res, next) => { +const AttachUserRoomsQuery = async (req, res, next) => { - RetrieveRooms( - ` - SELECT + req.roomQuery = ( + `SELECT rooms.id, title, description, @@ -90,18 +58,16 @@ const GetUserRooms = async (req, res, next) => { case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn FROM rooms JOIN rooms_users ON rooms_users.room_id = rooms.id - WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1); - `, - [user.id], - res + WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1);` ); + req.roomQueryParams = [user.id]; + next(); } -const GetArchive = async (req, res) => { +const AttachArchiveQuery = async (req, res, next) => { - RetrieveRooms( - ` - SELECT + req.roomQuery = ( + `SELECT rooms.id, title, description, @@ -110,11 +76,10 @@ const GetArchive = async (req, res) => { (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count FROM rooms JOIN rooms_users ON rooms_users.room_id = rooms.id - WHERE rooms.finished = true - `, - [], - res + WHERE rooms.finished = true` ); + req.roomQueryParams = []; + next(); } @@ -122,35 +87,27 @@ const GetArchive = async (req, res) => { const AttachAddRoomTransaction = async (req, res, next) => { req.Transaction = async () => { + + const title = req.query.title; + const description = req.query.description; + const scenario = req.query.scenario; + //ERROR CHECKS - if (!req.query.title) throw new Error('Please provide a title'); - if (req.query.title.length <= 3) throw new Error('Title must be at least 3 chars long'); - if (req.query.title.length > 50) throw new Error('Title can me maximum 50 characters long'); - if (!req.query.description) throw new Error('Please provide a description'); - if (req.query.description.length <= 3) throw new Error('Description must be at least 3 chars long'); - if (req.query.description.length > 200) throw new Error('Description can be at max 200 characters'); - if (!req.query.scenario) throw new Error('Please provide a starting scenario'); - if (req.query.scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); - if (req.query.scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); + if (!title) throw new Error('Please provide a title'); + if (title.length <= 3) throw new Error('Title must be at least 3 chars long'); + if (title.length > 50) throw new Error('Title can me maximum 50 characters long'); + + if (!description) throw new Error('Please provide a description'); + if (description.length <= 3) throw new Error('Description must be at least 3 chars long'); + if (description.length > 200) throw new Error('Description can be at max 200 characters'); + + if (!scenario) throw new Error('Please provide a starting scenario'); + if (scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); + if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); //TRY ADD TO DATABASE - await db.query( - 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', - [user.id] - ); - const newRoomRes = await db.query( - 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', - [req.query.title, req.query.description, user.id] - ); - const newRoomId = newRoomRes.rows[0].id; - await db.query( - 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', - [newRoomId, user.id] - ); - await db.query( - 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES($1, $2, $3)', - [req.query.scenario, user.id, newRoomId] - ); + await RemoveKeyFromLoggedUser(); + const newRoomId = await CreateNewRoom(req.query.title, req.query.description, req.query.scenario, user.id); req.responseMessage = 'new room added with ID ' + newRoomId; } @@ -206,20 +163,49 @@ const AttachJoinRoomTransaction = async (req, res, next) => { //MOUNT ROUTes roomRouter.get('/data/:id', GetRoomData); -roomRouter.get('/available', GetAvaliableRooms); -roomRouter.get('/user', GetUserRooms); -roomRouter.get('/archive', GetArchive); +roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); +roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); +roomRouter.get('/archive', AttachArchiveQuery, RetrieveRooms); roomRouter.post('/', AttachAddRoomTransaction, dbFunctions.TryTransaction); roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); //EXPORT module.exports = roomRouter; +async function CreateNewRoom(title, description, scenario, creator_id) { + const roomId = await AddRoom(title, description, creator_id); + await AddUserToRoom(roomId, creator_id); + await dbFunctions.AddScenario(scenario, roomId) + return roomId; +} + +async function AddRoom(title, description, creator_id) { + const query = await db.query( + 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', + [title, description, creator_id] + ); + return query.rows[0].id; +} + +async function AddUserToRoom(roomId, user_id) { + await db.query( + 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', + [roomId, user_id] + ); +} + +async function RemoveKeyFromLoggedUser() { + await db.query( + 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', + [user.id] + ); +} + //FUNCTIONS -async function RetrieveRooms(queryText, queryParams, res) { +async function RetrieveRooms(req, res) { try { - const query = await db.query(queryText, queryParams); + const query = await db.query(req.roomQuery, req.roomQueryParams); let rooms = []; query.rows.forEach(room => { diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 6bf93f8..b42ea4b 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -10,38 +10,31 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const scenario = req.query.text; const isEnd = (req.query.end == true); - if (!roomId) - throw new Error('No room_id provided'); - if (!scenario) - throw new Error('No text provided'); - if (scenario.length < 3) - throw new Error('text must be at least 3 characters long'); + //initial error checks + if (!roomId) throw new Error('No room_id provided'); + if (!scenario) throw new Error('No text provided'); + if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); //make queries - const playerQuery = await db.GetPlayersInRoom(roomId); - const roomQuery = await db.GetRoomInfo(roomId); + const players = await db.GetPlayersInRoom(roomId); + const room = await db.GetRoomInfo(roomId); //some db checks - db.MakeSureRoomExists(roomQuery); - db.MakeSurePlayerHasEnoughChars(playerQuery, scenario); - db.MakeSureItsPlayersTurn(roomQuery); - db.MakeSureItsNotFinished(roomQuery); - db.MakeSureDeadlineHasNotPassed(roomQuery); - if (!isEnd) - await db.MakeSureItsNotTheLastTurn(roomId); + db.MakeSurePlayerHasEnoughChars(players, scenario); + db.MakeSureItsPlayersTurn(room); + db.MakeSureItsNotFinished(room); + db.MakeSureDeadlineHasNotPassed(room); + if (!isEnd) await db.MakeSureItsNotTheLastTurn(roomId); //carry out the transaction const scenarioId = await db.AddScenario(scenario, roomId); - await db.UpdateRoomInfo(isEnd, roomQuery.rows[0].full, roomId, playerQuery); + await db.UpdateRoomInfo(isEnd, room.full, roomId, players); if (isEnd) await db.GiveKeyToEachPlayer(roomId); else await db.UpdateCharCount(scenario, roomId); //send response if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId - // res.status(200).send('new scenario added with id: ' + scenarioId); - else - //res.status(200).send('you ended the story! And got a key!: ' + scenarioId); - req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; } next(); From 4bd91300a09ab2577dd950d4af8f3c49f4249dc9 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 2 Nov 2022 14:42:02 +0100 Subject: [PATCH 032/127] clean --- fakeData/testUser.js | 2 +- routers/dbFunctions.js | 136 ++++++++++++++++++++++++++++++-------- routers/roomRouter.js | 90 ++++++------------------- routers/scenarioRouter.js | 26 ++++---- routers/userRouter.js | 27 +++----- 5 files changed, 152 insertions(+), 129 deletions(-) diff --git a/fakeData/testUser.js b/fakeData/testUser.js index 3137949..0937c7e 100644 --- a/fakeData/testUser.js +++ b/fakeData/testUser.js @@ -1,5 +1,5 @@ const user = { - id: 34, + id: 10, // name: 'flandre', // premium: true, // room_keys: 9 diff --git a/routers/dbFunctions.js b/routers/dbFunctions.js index 4047fc5..3798957 100644 --- a/routers/dbFunctions.js +++ b/routers/dbFunctions.js @@ -1,6 +1,7 @@ const db = require('./dbConnect.js'); const user = require('../fakeData/testUser'); +//GETTERS async function GetRoomInfo(roomId) { const roomQuery = await db.query( 'SELECT * FROM rooms WHERE id=$1', @@ -39,6 +40,30 @@ const GetScenariosInRoom = async (roomId) => { } +function GetNextPlayerId(players) { + let i = 0; + players.forEach((player, j) => { + if (player.id != user.id) return; + if (j == (players.length - 1)) return; + i = j + 1; + }); + const nextPlayerId = players[i].id; + return nextPlayerId; +} + +async function GetLoggedUserInfo() { + const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented + + if (!query.rows) + throw new Error('Query returned nothing'); + if (query.rowCount < 1) + throw new Error('Found no user with that id'); + if (query.rows.length > 2) + throw new Error('Query returned multiple users'); + return query.rows[0]; +} + +//CHECKS function MakeSureDeadlineHasNotPassed(room) { if (room.turn_end < new Date()) { //++update player turn @@ -80,6 +105,11 @@ function MakeSureRoomExists(roomQuery) { throw new Error('No room found with the given id'); } +//SETTERS +async function CreateUser(name) { + await db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [name]); +} + async function UpdateRoomInfo(isEnd, isFull, roomId, players) { await db.query( `UPDATE rooms @@ -89,10 +119,10 @@ async function UpdateRoomInfo(isEnd, isFull, roomId, players) { finished = $3 WHERE id = $2`, [ - isEnd || isFull ? null : GetNextPlayerId(players, isEnd), + (isEnd || !isFull) ? null : GetNextPlayerId(players, isEnd), roomId, isEnd, - isEnd || isFull ? null : new Date(Date.now() + 172800000) + (isEnd || !isFull) ? null : new Date(Date.now() + 172800000) ] ); } @@ -105,29 +135,6 @@ async function AddScenario(scenario, roomId) { return scenarioQuery.rows[0].id; } -async function BeginTransaction() { - await db.query('BEGIN'); -} - -function GetNextPlayerId(players, isEnd) { - let i = 0; - players.forEach((player, j) => { - if (player.user_id != user.id) - return; - if (j == (players.length - 1)) - return; - i = j + 1; - }); - const nextPlayerId = players[i].user_id; - if (isEnd) - nextPlayerId = null; - return nextPlayerId; -} - -function Rollback() { - db.query('ROLLBACK'); -} - async function UpdateCharCount(scenario, roomId) { await db.query( 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', @@ -150,6 +157,74 @@ async function GiveKeyToEachPlayer(roomId) { ); } +async function SetNextPlayerInRoom(roomId, userId) { + await db.query( + 'UPDATE rooms SET next_player_id=$1 WHERE id=$2', + [userId, roomId] + ); +} + +async function UpdateRoomFullStatus(roomId) { + await db.query( + `UPDATE rooms + SET "full"=((SELECT COUNT(*) FROM rooms_users WHERE rooms_users.room_id = rooms.id) >= 4) + WHERE id = $1`, + [roomId] + ); +} + +async function ResetRoomTurnEnd(roomId) { + await db.query( + `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, + [roomId] + ); +} + +async function AddUserToRoom(roomId, userId) { + await db.query( + 'INSERT INTO rooms_users (room_id, user_id) VALUES ($1, $2)', + [roomId, userId] + ); +} + +async function CreateNewRoom(title, description, scenario, creator_id) { + const roomId = await AddRoom(title, description, creator_id); + await AddUserToRoom(roomId, creator_id); + await AddScenario(scenario, roomId) + return roomId; +} + +async function AddRoom(title, description, creator_id) { + const query = await db.query( + 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', + [title, description, creator_id] + ); + return query.rows[0].id; +} + +async function AddUserToRoom(roomId, user_id) { + await db.query( + 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', + [roomId, user_id] + ); +} + +async function RemoveKeyFromLoggedUser() { + await db.query( + 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', + [user.id] + ); +} + +//TRANSACTIONS +async function BeginTransaction() { + await db.query('BEGIN'); +} + +function Rollback() { + db.query('ROLLBACK'); +} + async function Commit() { await db.query('COMMIT'); } @@ -170,6 +245,7 @@ async function TryTransaction(req, res, next) { } +//EXPORT module.exports = { GetRoomInfo, GetPlayersInRoom, @@ -187,5 +263,13 @@ module.exports = { UpdateCharCount, GiveKeyToEachPlayer, Commit, - TryTransaction + TryTransaction, + RemoveKeyFromLoggedUser, + CreateNewRoom, + ResetRoomTurnEnd, + UpdateRoomFullStatus, + SetNextPlayerInRoom, + AddUserToRoom, + CreateUser, + GetLoggedUserInfo }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index e59f525..e11fafa 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -96,18 +96,18 @@ const AttachAddRoomTransaction = async (req, res, next) => { if (!title) throw new Error('Please provide a title'); if (title.length <= 3) throw new Error('Title must be at least 3 chars long'); if (title.length > 50) throw new Error('Title can me maximum 50 characters long'); - + if (!description) throw new Error('Please provide a description'); if (description.length <= 3) throw new Error('Description must be at least 3 chars long'); if (description.length > 200) throw new Error('Description can be at max 200 characters'); - + if (!scenario) throw new Error('Please provide a starting scenario'); if (scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); //TRY ADD TO DATABASE - await RemoveKeyFromLoggedUser(); - const newRoomId = await CreateNewRoom(req.query.title, req.query.description, req.query.scenario, user.id); + await dbFunctions.RemoveKeyFromLoggedUser(); + const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, user.id); req.responseMessage = 'new room added with ID ' + newRoomId; } @@ -116,45 +116,22 @@ const AttachAddRoomTransaction = async (req, res, next) => { const AttachJoinRoomTransaction = async (req, res, next) => { req.Transaction = async () => { - if (!req.query.room_id) throw new Error('Please provide a room_id!'); - - //make sure room is not full or finished - const roomQuery = await db.query('SELECT * FROM rooms WHERE id=$1', [req.query.room_id]); - if (roomQuery.rows.length == 0) throw new Error('There is no room with that id'); - if (roomQuery.rows[0].full) throw new Error('Room is full'); - if (roomQuery.rows[0].finished) throw new Error('Story has already been finished'); - - //add players to rooms_users - const playerQuery = await db.query( - 'SELECT * FROM rooms_users WHERE room_id = $1', - [req.query.room_id] - ); - await db.query( - 'INSERT INTO rooms_users (room_id, user_id) VALUES ($1, $2)', - [req.query.room_id, user.id] - ); - - //alter the rooms table - //add a turn_end timestamp to room - await db.query( - `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, //deadline 2 days from now - [req.query.room_id] - ); - - //make room full if full - if (playerQuery.rows.length == 3) { - await db.query( - 'UPDATE rooms SET "full"=true WHERE id=$1', - [req.query.room_id] - ); - } - - //set next player - await db.query( - 'UPDATE rooms SET next_player_id=$1 WHERE id=$2', - [user.id, req.query.room_id] - ); + const roomId = req.query.room_id; + if (!roomId) throw new Error('Please provide a room_id!'); + + //Checks + const room = await dbFunctions.GetRoomInfo(roomId); + if (room.full) throw new Error('Room is full'); + if (room.finished) throw new Error('Story has already been finished'); + + //update + await dbFunctions.AddUserToRoom(roomId, user.id); + await dbFunctions.ResetRoomTurnEnd(roomId); + await dbFunctions.UpdateRoomFullStatus(roomId); + await dbFunctions.SetNextPlayerInRoom(roomId, user.id); + + //response req.responseMessage = 'Successfully joined the room!'; } @@ -172,35 +149,6 @@ roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); //EXPORT module.exports = roomRouter; -async function CreateNewRoom(title, description, scenario, creator_id) { - const roomId = await AddRoom(title, description, creator_id); - await AddUserToRoom(roomId, creator_id); - await dbFunctions.AddScenario(scenario, roomId) - return roomId; -} - -async function AddRoom(title, description, creator_id) { - const query = await db.query( - 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', - [title, description, creator_id] - ); - return query.rows[0].id; -} - -async function AddUserToRoom(roomId, user_id) { - await db.query( - 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', - [roomId, user_id] - ); -} - -async function RemoveKeyFromLoggedUser() { - await db.query( - 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', - [user.id] - ); -} - //FUNCTIONS async function RetrieveRooms(req, res) { diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index b42ea4b..06094bf 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,6 +1,6 @@ const express = require('express'); const scenarioRouter = express.Router(); -const db = require('./dbFunctions'); +const dbFunctions = require('./dbFunctions'); const AttachAddScenarioTransaction = async (req, res, next) => { @@ -16,21 +16,21 @@ const AttachAddScenarioTransaction = async (req, res, next) => { if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); //make queries - const players = await db.GetPlayersInRoom(roomId); - const room = await db.GetRoomInfo(roomId); + const players = await dbFunctions.GetPlayersInRoom(roomId); + const room = await dbFunctions.GetRoomInfo(roomId); //some db checks - db.MakeSurePlayerHasEnoughChars(players, scenario); - db.MakeSureItsPlayersTurn(room); - db.MakeSureItsNotFinished(room); - db.MakeSureDeadlineHasNotPassed(room); - if (!isEnd) await db.MakeSureItsNotTheLastTurn(roomId); + dbFunctions.MakeSurePlayerHasEnoughChars(players, scenario); + dbFunctions.MakeSureItsPlayersTurn(room); + dbFunctions.MakeSureItsNotFinished(room); + dbFunctions.MakeSureDeadlineHasNotPassed(room); + if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); //carry out the transaction - const scenarioId = await db.AddScenario(scenario, roomId); - await db.UpdateRoomInfo(isEnd, room.full, roomId, players); - if (isEnd) await db.GiveKeyToEachPlayer(roomId); - else await db.UpdateCharCount(scenario, roomId); + const scenarioId = await dbFunctions.AddScenario(scenario, roomId); + await dbFunctions.UpdateRoomInfo(isEnd, room.full, roomId, players); + if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); + else await dbFunctions.UpdateCharCount(scenario, roomId); //send response if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId @@ -40,6 +40,6 @@ const AttachAddScenarioTransaction = async (req, res, next) => { next(); } -scenarioRouter.post('/', AttachAddScenarioTransaction, db.TryTransaction); +scenarioRouter.post('/', AttachAddScenarioTransaction, dbFunctions.TryTransaction); module.exports = scenarioRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 2d3971b..9a50cb9 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,17 +1,16 @@ const express = require('express'); const userRouter = express.Router(); -const db = require('./dbConnect.js') -const user = require('../fakeData/testUser'); //remove in live v. const AddNewUser = async (req, res, next) => { try { - if (!req.query.name) throw new Error('No name provided') - if (req.query.name.length < 4) throw new Error('Name must be at least 4 characters') - if (req.query.name.length > 20) throw new Error('Name must be max 20 characters') + const name = req.query.name; - await db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [req.query.name]); + if (!name) throw new Error('No name provided') + if (name.length < 4) throw new Error('Name must be at least 4 characters') + if (name.length > 20) throw new Error('Name must be max 20 characters') + await CreateUser(name); res.status(201).send('User created successfully!'); @@ -24,18 +23,11 @@ const AddNewUser = async (req, res, next) => { } -const GetUserData = async (req, res, next) => { +const GetUserInfo = async (req, res, next) => { try { - - const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented - - if (!query.rows) throw new Error('Query returned nothing'); - if (query.rowCount < 1) throw new Error('Found no user with that id'); - if (query.rows.length > 2) throw new Error('Query returned multiple users'); - - res.json(query.rows[0]); - + const userInfo = await GetLoggedUserInfo(); + res.json(userInfo); } catch (error) { res.status(400).send('Unable to get user. ' + error.message); @@ -43,8 +35,7 @@ const GetUserData = async (req, res, next) => { } -userRouter.get('/', GetUserData); +userRouter.get('/', GetUserInfo); userRouter.post('/', AddNewUser); module.exports = userRouter; - From dd0565c803b22fbccf054e9b56702be8d30d7c1c Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 2 Nov 2022 15:38:33 +0100 Subject: [PATCH 033/127] Update userRouter.js --- routers/userRouter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routers/userRouter.js b/routers/userRouter.js index 9a50cb9..e95dfd8 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,5 +1,6 @@ const express = require('express'); const userRouter = express.Router(); +const dbFunctions = require('./dbFunctions'); const AddNewUser = async (req, res, next) => { @@ -10,7 +11,7 @@ const AddNewUser = async (req, res, next) => { if (!name) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') - await CreateUser(name); + await dbFunctions.CreateUser(name); res.status(201).send('User created successfully!'); @@ -26,7 +27,7 @@ const AddNewUser = async (req, res, next) => { const GetUserInfo = async (req, res, next) => { try { - const userInfo = await GetLoggedUserInfo(); + const userInfo = await dbFunctions.GetLoggedUserInfo(); res.json(userInfo); } catch (error) { From bc87f86987802697cdd64fabf410eeacabdeee17 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 4 Nov 2022 14:06:52 +0100 Subject: [PATCH 034/127] query return data fixes --- routers/roomRouter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index e11fafa..b7f781b 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -55,7 +55,7 @@ const AttachUserRoomsQuery = async (req, res, next) => { (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count, - case when rooms.next_player_id = $1 then 'TRUE' else 'FALSE' end as users_turn + case when rooms.next_player_id = $1 then true else false end as users_turn FROM rooms JOIN rooms_users ON rooms_users.room_id = rooms.id WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1);` @@ -84,7 +84,7 @@ const AttachArchiveQuery = async (req, res, next) => { } //POST TRANSACTION FUNCTIONS -const AttachAddRoomTransaction = async (req, res, next) => { +const AttachCreateRoomTransaction = async (req, res, next) => { req.Transaction = async () => { @@ -108,7 +108,7 @@ const AttachAddRoomTransaction = async (req, res, next) => { //TRY ADD TO DATABASE await dbFunctions.RemoveKeyFromLoggedUser(); const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, user.id); - req.responseMessage = 'new room added with ID ' + newRoomId; + req.responseMessage = {success: true, message: 'new room added!', roomId: newRoomId}; } next(); @@ -143,7 +143,7 @@ roomRouter.get('/data/:id', GetRoomData); roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); roomRouter.get('/archive', AttachArchiveQuery, RetrieveRooms); -roomRouter.post('/', AttachAddRoomTransaction, dbFunctions.TryTransaction); +roomRouter.post('/', AttachCreateRoomTransaction, dbFunctions.TryTransaction); roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); //EXPORT From 71c1f9081a162a92eb876d058a5d147d27bd9b61 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 8 Nov 2022 15:23:26 +0100 Subject: [PATCH 035/127] login routes and jwt implemented --- {routers => database}/dbConnect.js | 0 {routers => database}/dbFunctions.js | 64 +- fakeData/testUser.js | 8 - middleware/authentication.js | 51 ++ package-lock.json | 1212 ++++++++++++++++++++++++++ package.json | 4 + routers/roomRouter.js | 17 +- routers/scenarioRouter.js | 15 +- routers/userRouter.js | 25 +- 9 files changed, 1345 insertions(+), 51 deletions(-) rename {routers => database}/dbConnect.js (100%) rename {routers => database}/dbFunctions.js (79%) delete mode 100644 fakeData/testUser.js create mode 100644 middleware/authentication.js diff --git a/routers/dbConnect.js b/database/dbConnect.js similarity index 100% rename from routers/dbConnect.js rename to database/dbConnect.js diff --git a/routers/dbFunctions.js b/database/dbFunctions.js similarity index 79% rename from routers/dbFunctions.js rename to database/dbFunctions.js index 3798957..7a52f06 100644 --- a/routers/dbFunctions.js +++ b/database/dbFunctions.js @@ -1,5 +1,28 @@ const db = require('./dbConnect.js'); -const user = require('../fakeData/testUser'); +const jwt = require('jsonwebtoken'); + +//AUTH +async function CreateUser(name, email, password) { + await db.query( + 'INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING *', + [name, email, password] + ); + //auto login on success? +} + +async function Login(email, password) { + const query = await db.query( + 'SELECT * FROM users WHERE email = $1', [email] + ); + if (!query.rows[0]) throw new Error('user with that email does not exist'); + else if (password != query.rows[0].password) throw new Error('wrong password'); + else { + return query.rows[0]; + }; + + +} + //GETTERS async function GetRoomInfo(roomId) { @@ -40,10 +63,10 @@ const GetScenariosInRoom = async (roomId) => { } -function GetNextPlayerId(players) { +function GetNextPlayerId(players, userId) { let i = 0; players.forEach((player, j) => { - if (player.id != user.id) return; + if (player.id != userId) return; if (j == (players.length - 1)) return; i = j + 1; }); @@ -51,8 +74,8 @@ function GetNextPlayerId(players) { return nextPlayerId; } -async function GetLoggedUserInfo() { - const query = await db.query('SELECT * FROM users WHERE id=' + user.id); //change to logged user when session is implemented +async function GetUserInfo(id) { + const query = await db.query('SELECT * FROM users WHERE id=' + id); //change to logged user when session is implemented if (!query.rows) throw new Error('Query returned nothing'); @@ -71,9 +94,9 @@ function MakeSureDeadlineHasNotPassed(room) { } } -function MakeSurePlayerHasEnoughChars(players, scenario) { +function MakeSurePlayerHasEnoughChars(players, scenario, userId) { players.forEach(player => { - if (player.user_id == user.id && player.char_count < scenario.length) { + if (player.user_id == userId && player.char_count < scenario.length) { throw new Error('player does not have enough characters left'); }; }); @@ -95,8 +118,8 @@ function MakeSureItsNotFinished(room) { throw new Error('the story has already been ended'); } -function MakeSureItsPlayersTurn(room) { - if (!room.next_player_id || room.next_player_id != user.id) +function MakeSureItsPlayersTurn(room, userId) { + if (!room.next_player_id || room.next_player_id != userId) throw new Error('its not the logged players turn'); } @@ -106,11 +129,7 @@ function MakeSureRoomExists(roomQuery) { } //SETTERS -async function CreateUser(name) { - await db.query('INSERT INTO users (name) VALUES ($1) RETURNING *', [name]); -} - -async function UpdateRoomInfo(isEnd, isFull, roomId, players) { +async function UpdateRoomInfo(isEnd, isFull, roomId, players, userId) { await db.query( `UPDATE rooms SET @@ -119,7 +138,7 @@ async function UpdateRoomInfo(isEnd, isFull, roomId, players) { finished = $3 WHERE id = $2`, [ - (isEnd || !isFull) ? null : GetNextPlayerId(players, isEnd), + (isEnd || !isFull) ? null : GetNextPlayerId(players, userId), roomId, isEnd, (isEnd || !isFull) ? null : new Date(Date.now() + 172800000) @@ -127,18 +146,18 @@ async function UpdateRoomInfo(isEnd, isFull, roomId, players) { ); } -async function AddScenario(scenario, roomId) { +async function AddScenario(scenario, roomId, userId) { const scenarioQuery = await db.query( 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES ($1, $2, $3) RETURNING *', - [scenario, user.id, roomId] + [scenario, userId, roomId] ); return scenarioQuery.rows[0].id; } -async function UpdateCharCount(scenario, roomId) { +async function UpdateCharCount(scenario, roomId, userId) { await db.query( 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', - [scenario.length, user.id, roomId] + [scenario.length, userId, roomId] ); } @@ -209,10 +228,10 @@ async function AddUserToRoom(roomId, user_id) { ); } -async function RemoveKeyFromLoggedUser() { +async function RemoveKeyFromLoggedUser(userId) { await db.query( 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', - [user.id] + [userId] ); } @@ -271,5 +290,6 @@ module.exports = { SetNextPlayerInRoom, AddUserToRoom, CreateUser, - GetLoggedUserInfo + GetLoggedUserInfo: GetUserInfo, + Login }; \ No newline at end of file diff --git a/fakeData/testUser.js b/fakeData/testUser.js deleted file mode 100644 index 0937c7e..0000000 --- a/fakeData/testUser.js +++ /dev/null @@ -1,8 +0,0 @@ -const user = { - id: 10, - // name: 'flandre', - // premium: true, - // room_keys: 9 -} - -module.exports = user; \ No newline at end of file diff --git a/middleware/authentication.js b/middleware/authentication.js new file mode 100644 index 0000000..a0579a2 --- /dev/null +++ b/middleware/authentication.js @@ -0,0 +1,51 @@ +const jwt = require('jsonwebtoken'); +const dbFunctions = require('../database/dbFunctions'); + +const isAuth = async (req, res, next) => { + + const authToken = req.headers['authorization']; + + if (typeof authToken == 'undefined' || !authToken) { + res.status(403).send('no authorization header provided'); + return; + } + + jwt.verify(authToken, process.env.JWT_SECRET, (err, data) => { + if (err) { + res.status(403).send('invalid auth token'); + } + else { + req.user = data; + next(); + } + }) + +} + +const Login = async (req, res, next) => { + try { + + const email = req.query.email; + const password = req.query.password; + + if (!email) throw new Error('no email provided'); + if (!password) throw new Error('no password provided'); + + const user = await dbFunctions.Login(email, password); + + const token = jwt.sign( + user, + process.env.JWT_SECRET, + {expiresIn: process.env.JWT_EXPIRES_IN} + ); + + res.status(201).send({ message: 'logged in as ' + user.name, token: token }); + + } catch (error) { + + res.status(400).send('Cant login: ' + error.message); + + } +} + +module.exports = { isAuth, Login }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ad05fb8..fc2c995 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,57 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "bcrypt": "^5.1.0", "body-parser": "^1.20.1", "dotenv": "^16.0.3", "errorhandler": "^1.5.1", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", + "passport": "^0.6.0", + "passport-local": "^1.0.0", "pg": "^8.8.0" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -30,11 +72,73 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -46,6 +150,19 @@ "node": ">= 0.8" } }, + "node_modules/bcrypt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", + "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.10", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -152,6 +269,20 @@ "node": ">=0.6" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/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": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -180,6 +311,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -220,6 +377,11 @@ "ms": "2.0.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -233,6 +395,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -241,11 +411,24 @@ "node": ">=12" } }, + "node_modules/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==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -394,11 +577,46 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -412,6 +630,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -434,6 +671,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -449,6 +691,39 @@ "node": ">= 0.6" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -460,6 +735,15 @@ "node": ">=0.10.0" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -473,6 +757,127 @@ "node": ">= 0.10" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "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": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -524,6 +929,51 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -560,6 +1010,11 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -579,6 +1034,39 @@ } } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -606,6 +1094,14 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -619,11 +1115,60 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/pg": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", @@ -824,6 +1369,33 @@ "node": ">=0.6" } }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -834,6 +1406,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -876,6 +1456,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -894,6 +1479,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", @@ -910,6 +1500,73 @@ "node": ">= 0.6" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -943,6 +1600,11 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -973,6 +1635,19 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -980,9 +1655,45 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -992,11 +1703,58 @@ "negotiator": "0.6.2" } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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==" + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -1005,6 +1763,15 @@ "safe-buffer": "5.1.2" } }, + "bcrypt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", + "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.10", + "node-addon-api": "^5.0.0" + } + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1084,6 +1851,20 @@ } } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -1103,6 +1884,26 @@ "get-intrinsic": "^1.0.2" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1134,6 +1935,11 @@ "ms": "2.0.0" } }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1144,16 +1950,34 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, "dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, + "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" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1274,11 +2098,40 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -1289,6 +2142,19 @@ "has-symbols": "^1.0.3" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1302,6 +2168,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -1314,6 +2185,30 @@ "toidentifier": "1.0.0" } }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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==" + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1322,6 +2217,15 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -1332,6 +2236,112 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "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.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "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" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "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": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1365,6 +2375,36 @@ "mime-db": "1.44.0" } }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, "morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -1394,6 +2434,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -1402,6 +2447,30 @@ "whatwg-url": "^5.0.0" } }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -1420,6 +2489,14 @@ "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": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -1430,11 +2507,44 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "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": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, "path-to-regexp": { "version": "0.1.7", "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": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "pg": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", @@ -1582,6 +2692,24 @@ } } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1592,6 +2720,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -1630,6 +2763,11 @@ "send": "0.17.1" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -1645,6 +2783,11 @@ "object-inspect": "^1.9.0" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", @@ -1655,6 +2798,52 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1679,6 +2868,11 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1703,10 +2897,28 @@ "webidl-conversions": "^3.0.0" } }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index d2aaf4f..3120b11 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,16 @@ "author": "", "license": "ISC", "dependencies": { + "bcrypt": "^5.1.0", "body-parser": "^1.20.1", "dotenv": "^16.0.3", "errorhandler": "^1.5.1", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", + "passport": "^0.6.0", + "passport-local": "^1.0.0", "pg": "^8.8.0" }, "description": "" diff --git a/routers/roomRouter.js b/routers/roomRouter.js index b7f781b..2651c63 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -1,8 +1,8 @@ const express = require('express'); const roomRouter = express.Router(); -const db = require('./dbConnect.js'); -const dbFunctions = require('./dbFunctions'); -const user = require('../fakeData/testUser'); +const db = require('../database/dbConnect.js'); +const dbFunctions = require('../database/dbFunctions'); +const {isAuth} = require('../middleware/authentication'); //GETTER FUNCTIONS const GetRoomData = async (req, res, next) => { @@ -40,7 +40,7 @@ const AttachAvailableRoomsQuery = async (req, res, next) => { GROUP BY (rooms_users.user_id, rooms.id, users.name) ORDER BY id;` ); - req.roomQueryParams = [user.id]; + req.roomQueryParams = [req.user.id]; next(); } @@ -60,7 +60,7 @@ const AttachUserRoomsQuery = async (req, res, next) => { JOIN rooms_users ON rooms_users.room_id = rooms.id WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1);` ); - req.roomQueryParams = [user.id]; + req.roomQueryParams = [req.user.id]; next(); } @@ -106,7 +106,7 @@ const AttachCreateRoomTransaction = async (req, res, next) => { if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); //TRY ADD TO DATABASE - await dbFunctions.RemoveKeyFromLoggedUser(); + await dbFunctions.RemoveKeyFromLoggedUser(req.user.id); const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, user.id); req.responseMessage = {success: true, message: 'new room added!', roomId: newRoomId}; } @@ -126,10 +126,10 @@ const AttachJoinRoomTransaction = async (req, res, next) => { if (room.finished) throw new Error('Story has already been finished'); //update - await dbFunctions.AddUserToRoom(roomId, user.id); + await dbFunctions.AddUserToRoom(roomId, req.user.id); await dbFunctions.ResetRoomTurnEnd(roomId); await dbFunctions.UpdateRoomFullStatus(roomId); - await dbFunctions.SetNextPlayerInRoom(roomId, user.id); + await dbFunctions.SetNextPlayerInRoom(roomId, req.user.id); //response req.responseMessage = 'Successfully joined the room!'; @@ -139,6 +139,7 @@ const AttachJoinRoomTransaction = async (req, res, next) => { } //MOUNT ROUTes +roomRouter.use(isAuth); roomRouter.get('/data/:id', GetRoomData); roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 06094bf..2f6ad7e 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,6 +1,7 @@ const express = require('express'); const scenarioRouter = express.Router(); -const dbFunctions = require('./dbFunctions'); +const dbFunctions = require('../database/dbFunctions'); +const {isAuth} = require('../middleware/authentication'); const AttachAddScenarioTransaction = async (req, res, next) => { @@ -9,6 +10,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const roomId = req.query.room_id; const scenario = req.query.text; const isEnd = (req.query.end == true); + const userId = req.user.id; //initial error checks if (!roomId) throw new Error('No room_id provided'); @@ -20,17 +22,17 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const room = await dbFunctions.GetRoomInfo(roomId); //some db checks - dbFunctions.MakeSurePlayerHasEnoughChars(players, scenario); - dbFunctions.MakeSureItsPlayersTurn(room); + dbFunctions.MakeSurePlayerHasEnoughChars(players, scenario, userId); + dbFunctions.MakeSureItsPlayersTurn(room, userId); dbFunctions.MakeSureItsNotFinished(room); dbFunctions.MakeSureDeadlineHasNotPassed(room); if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); //carry out the transaction - const scenarioId = await dbFunctions.AddScenario(scenario, roomId); - await dbFunctions.UpdateRoomInfo(isEnd, room.full, roomId, players); + const scenarioId = await dbFunctions.AddScenario(scenario, roomId, userId); + await dbFunctions.UpdateRoomInfo(isEnd, room.full, roomId, players, userId); if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); - else await dbFunctions.UpdateCharCount(scenario, roomId); + else await dbFunctions.UpdateCharCount(scenario, roomId, userId); //send response if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId @@ -40,6 +42,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { next(); } +scenarioRouter.use(isAuth); scenarioRouter.post('/', AttachAddScenarioTransaction, dbFunctions.TryTransaction); module.exports = scenarioRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index e95dfd8..41e55ed 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,17 +1,23 @@ const express = require('express'); const userRouter = express.Router(); -const dbFunctions = require('./dbFunctions'); +const dbFunctions = require('../database/dbFunctions'); +const {isAuth, Login} = require('../middleware/authentication'); const AddNewUser = async (req, res, next) => { try { const name = req.query.name; + const email = req.query.email; + const password = req.query.password; if (!name) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') - await dbFunctions.CreateUser(name); + if (!email) throw new Error('No email provided') + if (!password) throw new Error('No password provided') + + await dbFunctions.CreateUser(name, email, password); res.status(201).send('User created successfully!'); @@ -27,16 +33,21 @@ const AddNewUser = async (req, res, next) => { const GetUserInfo = async (req, res, next) => { try { - const userInfo = await dbFunctions.GetLoggedUserInfo(); - res.json(userInfo); + res.json({ + id: req.user.id, + name: req.user.name, + room_keys: req.user.room_keys, + email: req.user.email + }); } catch (error) { res.status(400).send('Unable to get user. ' + error.message); } - + } -userRouter.get('/', GetUserInfo); -userRouter.post('/', AddNewUser); +userRouter.get('/', isAuth, GetUserInfo); +userRouter.post('/create', AddNewUser); +userRouter.post('/login', Login); module.exports = userRouter; From b04313982cd3233f583b473ae008f2ba4b14acb3 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 8 Nov 2022 15:47:32 +0100 Subject: [PATCH 036/127] password hashin --- database/dbFunctions.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 7a52f06..9a50c4f 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -1,28 +1,30 @@ const db = require('./dbConnect.js'); -const jwt = require('jsonwebtoken'); +const bcrypt = require('bcrypt'); //AUTH async function CreateUser(name, email, password) { + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + await db.query( 'INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING *', - [name, email, password] + [name, email, hash] ); //auto login on success? } - async function Login(email, password) { + const query = await db.query( 'SELECT * FROM users WHERE email = $1', [email] ); - if (!query.rows[0]) throw new Error('user with that email does not exist'); - else if (password != query.rows[0].password) throw new Error('wrong password'); - else { - return query.rows[0]; - }; + if (!query.rows[0]) throw new Error('user with that email does not exist') + if (!await bcrypt.compare(password, query.rows[0].password)) throw new Error('wrong password. hash is: '+ hash); -} + return query.rows[0]; +} //GETTERS async function GetRoomInfo(roomId) { From 33c7728fc8483fa88f0d89885c0b74d23d76916d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 8 Nov 2022 16:21:20 +0100 Subject: [PATCH 037/127] auto login --- app.js | 4 ++-- routers/userRouter.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 5a327bf..d0b2967 100644 --- a/app.js +++ b/app.js @@ -25,8 +25,8 @@ app.use('/user', userRouter); app.use('/scenario', scenarioRouter); //test rout -app.get('/', (req, res) => { - res.send(`

App is running :)

`); +app.get('/', async (req, res) => { + res.send(`unwritten server is running on ${PORT} :)`); }) //start server diff --git a/routers/userRouter.js b/routers/userRouter.js index 41e55ed..08c13fb 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -19,7 +19,8 @@ const AddNewUser = async (req, res, next) => { await dbFunctions.CreateUser(name, email, password); - res.status(201).send('User created successfully!'); + next(); + //res.status(201).send('Created user and logged in'); } catch (error) { @@ -47,7 +48,7 @@ const GetUserInfo = async (req, res, next) => { } userRouter.get('/', isAuth, GetUserInfo); -userRouter.post('/create', AddNewUser); +userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); -module.exports = userRouter; +module.exports = userRouter; \ No newline at end of file From daa3c17ac974c1df11871d0baae42eaab0f0ac15 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 8 Nov 2022 16:39:12 +0100 Subject: [PATCH 038/127] hotfix on create room path --- routers/roomRouter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 2651c63..f75abbe 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -107,7 +107,7 @@ const AttachCreateRoomTransaction = async (req, res, next) => { //TRY ADD TO DATABASE await dbFunctions.RemoveKeyFromLoggedUser(req.user.id); - const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, user.id); + const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, req.user.id); req.responseMessage = {success: true, message: 'new room added!', roomId: newRoomId}; } From e2aad6ba80eb442f738f55c571cd05a0a7bcd968 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 8 Nov 2022 16:44:55 +0100 Subject: [PATCH 039/127] another hotfix --- database/dbFunctions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 9a50c4f..4cd4086 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -211,7 +211,7 @@ async function AddUserToRoom(roomId, userId) { async function CreateNewRoom(title, description, scenario, creator_id) { const roomId = await AddRoom(title, description, creator_id); await AddUserToRoom(roomId, creator_id); - await AddScenario(scenario, roomId) + await AddScenario(scenario, roomId, creator_id) return roomId; } From a343fc62ec23359feee5eb24510cff98dcde90a4 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 13:42:07 +0100 Subject: [PATCH 040/127] backend route fix for token requests --- database/dbFunctions.js | 16 +++++++--------- middleware/authentication.js | 4 ++-- routers/roomRouter.js | 12 ++++++------ routers/userRouter.js | 10 ++++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 4cd4086..ffa7c70 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -20,7 +20,7 @@ async function Login(email, password) { ); if (!query.rows[0]) throw new Error('user with that email does not exist') - if (!await bcrypt.compare(password, query.rows[0].password)) throw new Error('wrong password. hash is: '+ hash); + if (!await bcrypt.compare(password, query.rows[0].password)) throw new Error('wrong password. hash is: ' + hash); return query.rows[0]; @@ -76,15 +76,13 @@ function GetNextPlayerId(players, userId) { return nextPlayerId; } -async function GetUserInfo(id) { +async function GetLoggedUserInfo(id) { const query = await db.query('SELECT * FROM users WHERE id=' + id); //change to logged user when session is implemented - if (!query.rows) - throw new Error('Query returned nothing'); - if (query.rowCount < 1) - throw new Error('Found no user with that id'); - if (query.rows.length > 2) - throw new Error('Query returned multiple users'); + if (!query.rows) throw new Error('Query returned nothing'); + if (query.rowCount < 1) throw new Error('Found no user with that id'); + if (query.rows.length > 2) throw new Error('Query returned multiple users'); + return query.rows[0]; } @@ -292,6 +290,6 @@ module.exports = { SetNextPlayerInRoom, AddUserToRoom, CreateUser, - GetLoggedUserInfo: GetUserInfo, + GetLoggedUserInfo, Login }; \ No newline at end of file diff --git a/middleware/authentication.js b/middleware/authentication.js index a0579a2..f567e4a 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -15,7 +15,7 @@ const isAuth = async (req, res, next) => { res.status(403).send('invalid auth token'); } else { - req.user = data; + req.userId = data.userId; next(); } }) @@ -34,7 +34,7 @@ const Login = async (req, res, next) => { const user = await dbFunctions.Login(email, password); const token = jwt.sign( - user, + {userId: user.id}, process.env.JWT_SECRET, {expiresIn: process.env.JWT_EXPIRES_IN} ); diff --git a/routers/roomRouter.js b/routers/roomRouter.js index f75abbe..4062bbd 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -40,7 +40,7 @@ const AttachAvailableRoomsQuery = async (req, res, next) => { GROUP BY (rooms_users.user_id, rooms.id, users.name) ORDER BY id;` ); - req.roomQueryParams = [req.user.id]; + req.roomQueryParams = [req.userId]; next(); } @@ -60,7 +60,7 @@ const AttachUserRoomsQuery = async (req, res, next) => { JOIN rooms_users ON rooms_users.room_id = rooms.id WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1);` ); - req.roomQueryParams = [req.user.id]; + req.roomQueryParams = [req.userId]; next(); } @@ -106,8 +106,8 @@ const AttachCreateRoomTransaction = async (req, res, next) => { if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); //TRY ADD TO DATABASE - await dbFunctions.RemoveKeyFromLoggedUser(req.user.id); - const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, req.user.id); + await dbFunctions.RemoveKeyFromLoggedUser(req.userId); + const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, req.userId); req.responseMessage = {success: true, message: 'new room added!', roomId: newRoomId}; } @@ -126,10 +126,10 @@ const AttachJoinRoomTransaction = async (req, res, next) => { if (room.finished) throw new Error('Story has already been finished'); //update - await dbFunctions.AddUserToRoom(roomId, req.user.id); + await dbFunctions.AddUserToRoom(roomId, req.userId); await dbFunctions.ResetRoomTurnEnd(roomId); await dbFunctions.UpdateRoomFullStatus(roomId); - await dbFunctions.SetNextPlayerInRoom(roomId, req.user.id); + await dbFunctions.SetNextPlayerInRoom(roomId, req.userId); //response req.responseMessage = 'Successfully joined the room!'; diff --git a/routers/userRouter.js b/routers/userRouter.js index 08c13fb..0f4155d 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -34,11 +34,13 @@ const AddNewUser = async (req, res, next) => { const GetUserInfo = async (req, res, next) => { try { + const user = await dbFunctions.GetLoggedUserInfo(req.userId); res.json({ - id: req.user.id, - name: req.user.name, - room_keys: req.user.room_keys, - email: req.user.email + id: user.id, + name: user.name, + room_keys: user.room_keys, + email: user.email, + premium: user.premium }); } catch (error) { From 3c80968101e34e76ce379a65ad37313076288b4a Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 14:00:38 +0100 Subject: [PATCH 041/127] bugfix --- routers/scenarioRouter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 2f6ad7e..43b7088 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -10,7 +10,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const roomId = req.query.room_id; const scenario = req.query.text; const isEnd = (req.query.end == true); - const userId = req.user.id; + const userId = req.userId; //initial error checks if (!roomId) throw new Error('No room_id provided'); From 5dfd0953a500543302a3a5cd70c074ca5a823fca Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 14:03:08 +0100 Subject: [PATCH 042/127] better error responses from server --- database/dbFunctions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index ffa7c70..6a3081d 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -259,7 +259,7 @@ async function TryTransaction(req, res, next) { catch (error) { Rollback(); console.error(error); - res.status(400).send('Transaction failed: ' + error.message); + res.status(400).send({ok: false, message: error.message}); } } From bdf65332819ed4efaf66be80205d7383d00a1e78 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 14:04:45 +0100 Subject: [PATCH 043/127] Update scenarioRouter.js --- routers/scenarioRouter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 43b7088..c0c25bc 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -16,6 +16,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { if (!roomId) throw new Error('No room_id provided'); if (!scenario) throw new Error('No text provided'); if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); + if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries const players = await dbFunctions.GetPlayersInRoom(roomId); From fe964a86b35b85947af96dc38a7a6682360eeee1 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 14:25:13 +0100 Subject: [PATCH 044/127] error handling on create user --- database/dbFunctions.js | 8 +++++++- routers/userRouter.js | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 6a3081d..f37df8e 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -4,6 +4,12 @@ const bcrypt = require('bcrypt'); //AUTH async function CreateUser(name, email, password) { + const checkEmail = await db.query('SELECT * FROM users WHERE email = $1', [email]); + if (checkEmail.rowCount > 0) throw new Error('email already in use'); + + const checkName = await db.query('SELECT * FROM users WHERE name = $1', [name]); + if (checkName.rowCount > 0) throw new Error('display name already taken'); + const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(password, salt); @@ -11,7 +17,7 @@ async function CreateUser(name, email, password) { 'INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING *', [name, email, hash] ); - //auto login on success? + } async function Login(email, password) { diff --git a/routers/userRouter.js b/routers/userRouter.js index 0f4155d..fae40db 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -11,21 +11,21 @@ const AddNewUser = async (req, res, next) => { const email = req.query.email; const password = req.query.password; + if (!email) throw new Error('No email provided') + if (!password) throw new Error('No password provided') + if (password.length < 6) throw new Error('Password must be at least 6 characters') if (!name) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') - if (!email) throw new Error('No email provided') - if (!password) throw new Error('No password provided') await dbFunctions.CreateUser(name, email, password); next(); - //res.status(201).send('Created user and logged in'); } catch (error) { - res.status(400).send('Cant create user: ' + error.message); + res.status(400).send({ok: false, message: error.message}); } From 54f58fb3c625f2383d93fa97e7addca152037b25 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 10 Nov 2022 14:42:31 +0100 Subject: [PATCH 045/127] improved backend checks create user --- routers/userRouter.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/routers/userRouter.js b/routers/userRouter.js index fae40db..c6a1b07 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -11,10 +11,14 @@ const AddNewUser = async (req, res, next) => { const email = req.query.email; const password = req.query.password; - if (!email) throw new Error('No email provided') - if (!password) throw new Error('No password provided') + const exists = text =>{ + return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); + } + + if (!exists(email)) throw new Error('No email provided') + if (!exists(password)) throw new Error('No password provided') if (password.length < 6) throw new Error('Password must be at least 6 characters') - if (!name) throw new Error('No name provided') + if (!exists(name)) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') From 254689b7122d99d6dbcca529111cb1c56f8e3233 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 11 Nov 2022 16:52:05 +0100 Subject: [PATCH 046/127] push notifications added requires you to give the backend a token on signup. --- app.js | 10 +++++ database/dbFunctions.js | 39 +++++++++++++----- middleware/authentication.js | 1 + notifications/notifications.js | 47 +++++++++++++++++++++ package-lock.json | 75 ++++++++++++++++++++++++++++++++++ package.json | 1 + routers/scenarioRouter.js | 27 +++++++----- routers/userRouter.js | 24 ++++++----- 8 files changed, 193 insertions(+), 31 deletions(-) create mode 100644 notifications/notifications.js diff --git a/app.js b/app.js index d0b2967..3a538b0 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,16 @@ const app = express(); const PORT = process.env.PORT || 5000; require('dotenv').config() +//test pushing +// const {GetPushToken} = require('./database/dbFunctions'); +// const {SendTurnNotification} = require('./notifications/notifications'); +// const TestPush = async () => { +// const pushToken = await GetPushToken(13); +// SendTurnNotification(pushToken, 52, 'Lambda 5'); +// } +// TestPush(); + + //logging middleware const morgan = require('morgan'); app.use(morgan('tiny')); diff --git a/database/dbFunctions.js b/database/dbFunctions.js index ffa7c70..5ac8a6b 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -2,17 +2,18 @@ const db = require('./dbConnect.js'); const bcrypt = require('bcrypt'); //AUTH -async function CreateUser(name, email, password) { +async function CreateUser(name, email, password, pushToken) { const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(password, salt); await db.query( - 'INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING *', - [name, email, hash] + 'INSERT INTO users (name, email, password, expo_push_token) VALUES ($1, $2, $3, $4) RETURNING *', + [name, email, hash, pushToken] ); - //auto login on success? + } + async function Login(email, password) { const query = await db.query( @@ -86,6 +87,12 @@ async function GetLoggedUserInfo(id) { return query.rows[0]; } +async function GetPushToken(userId) { + const query = await db.query('SELECT expo_push_token FROM users WHERE id = $1', [userId]); + if (query.rowCount != 0) return query.rows[0].expo_push_token; + else return null; +} + //CHECKS function MakeSureDeadlineHasNotPassed(room) { if (room.turn_end < new Date()) { @@ -119,8 +126,17 @@ function MakeSureItsNotFinished(room) { } function MakeSureItsPlayersTurn(room, userId) { - if (!room.next_player_id || room.next_player_id != userId) - throw new Error('its not the logged players turn'); + + if (!room.next_player_id) { + throw new Error(`there is no next player!`); + } + + if (!room.next_player_id || room.next_player_id != userId) { + throw new Error(`its not the logged players turn. + poster id was ${userId}, + but its actually user with id ${room.nextPlayerId} who is next`); + } + } function MakeSureRoomExists(roomQuery) { @@ -129,7 +145,8 @@ function MakeSureRoomExists(roomQuery) { } //SETTERS -async function UpdateRoomInfo(isEnd, isFull, roomId, players, userId) { +async function UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd) { + await db.query( `UPDATE rooms SET @@ -138,10 +155,10 @@ async function UpdateRoomInfo(isEnd, isFull, roomId, players, userId) { finished = $3 WHERE id = $2`, [ - (isEnd || !isFull) ? null : GetNextPlayerId(players, userId), + nextPlayerId, roomId, isEnd, - (isEnd || !isFull) ? null : new Date(Date.now() + 172800000) + turnEnd ] ); } @@ -291,5 +308,7 @@ module.exports = { AddUserToRoom, CreateUser, GetLoggedUserInfo, - Login + Login, + GetPushToken, + GetNextPlayerId }; \ No newline at end of file diff --git a/middleware/authentication.js b/middleware/authentication.js index f567e4a..83c6ffe 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -16,6 +16,7 @@ const isAuth = async (req, res, next) => { } else { req.userId = data.userId; + console.log('attaching user id: ', req.userId); next(); } }) diff --git a/notifications/notifications.js b/notifications/notifications.js new file mode 100644 index 0000000..e43b64c --- /dev/null +++ b/notifications/notifications.js @@ -0,0 +1,47 @@ +const { Expo } = require('expo-server-sdk'); + +let expo = new Expo(); + +const SendPushNotification = async (pushToken, title, body, data) => { + + if (!Expo.isExpoPushToken(pushToken)) { + console.error(`Push token ${pushToken} is not a valid Expo push token`); + return; + } + + const message = { + to: pushToken, + sound: 'default', + title: title, + body: body, + data: data, + } + + const chunks = expo.chunkPushNotifications([message]); + const chunk = chunks[0]; + + try { + const ticketChunk = await expo.sendPushNotificationsAsync(chunk); + console.log('sent notification!'); + console.log(ticketChunk); + } catch (error) { + console.error(error); + } + +} + +const SendTurnNotification = (pushToken, roomId, storyTitle) => { + + SendPushNotification( + pushToken, + 'Your turn!', + storyTitle + ' was updated. You are the next player in line to write!', + { + type: 'turn', + roomId: roomId + } + ); + +} + +module.exports = { SendTurnNotification }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fc2c995..39cdba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "body-parser": "^1.20.1", "dotenv": "^16.0.3", "errorhandler": "^1.5.1", + "expo-server-sdk": "^3.7.0", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", @@ -437,6 +438,11 @@ "node": ">= 0.8" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, "node_modules/errorhandler": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", @@ -462,6 +468,16 @@ "node": ">= 0.6" } }, + "node_modules/expo-server-sdk": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-3.7.0.tgz", + "integrity": "sha512-SMZuBiIWejAdMMIOTjGQlprcwvSyLfeUQlooyGB5q6GvZ8zHjp+if8Q4k7xczUBTqIqTzs5IvTZnTiqA9Oe9WA==", + "dependencies": { + "node-fetch": "^2.6.0", + "promise-limit": "^2.7.0", + "promise-retry": "^2.0.1" + } + }, "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -1278,6 +1294,23 @@ "node": ">=0.10.0" } }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1382,6 +1415,14 @@ "node": ">= 6" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "engines": { + "node": ">= 4" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1983,6 +2024,11 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, "errorhandler": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", @@ -2002,6 +2048,16 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "expo-server-sdk": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-3.7.0.tgz", + "integrity": "sha512-SMZuBiIWejAdMMIOTjGQlprcwvSyLfeUQlooyGB5q6GvZ8zHjp+if8Q4k7xczUBTqIqTzs5IvTZnTiqA9Oe9WA==", + "requires": { + "node-fetch": "^2.6.0", + "promise-limit": "^2.7.0", + "promise-retry": "^2.0.1" + } + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -2623,6 +2679,20 @@ "xtend": "^4.0.0" } }, + "promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==" + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -2702,6 +2772,11 @@ "util-deprecate": "^1.0.1" } }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", diff --git a/package.json b/package.json index 3120b11..d1a4934 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "body-parser": "^1.20.1", "dotenv": "^16.0.3", "errorhandler": "^1.5.1", + "expo-server-sdk": "^3.7.0", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 43b7088..aaa3a78 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -2,37 +2,44 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const {isAuth} = require('../middleware/authentication'); +const {SendTurnNotification} = require('../notifications/notifications') const AttachAddScenarioTransaction = async (req, res, next) => { req.Transaction = async () => { - const roomId = req.query.room_id; - const scenario = req.query.text; - const isEnd = (req.query.end == true); const userId = req.userId; + const {roomId, text} = req.query + const isEnd = (req.query.end == true); //initial error checks - if (!roomId) throw new Error('No room_id provided'); - if (!scenario) throw new Error('No text provided'); - if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); + if (!roomId) throw new Error('No roomId provided'); + if (!text) throw new Error('No text provided'); + if (text.length < 3) throw new Error('text must be at least 3 characters long'); //make queries const players = await dbFunctions.GetPlayersInRoom(roomId); const room = await dbFunctions.GetRoomInfo(roomId); //some db checks - dbFunctions.MakeSurePlayerHasEnoughChars(players, scenario, userId); + dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); dbFunctions.MakeSureItsPlayersTurn(room, userId); dbFunctions.MakeSureItsNotFinished(room); dbFunctions.MakeSureDeadlineHasNotPassed(room); if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); //carry out the transaction - const scenarioId = await dbFunctions.AddScenario(scenario, roomId, userId); - await dbFunctions.UpdateRoomInfo(isEnd, room.full, roomId, players, userId); + const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); + + const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); + const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); + await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); - else await dbFunctions.UpdateCharCount(scenario, roomId, userId); + else { + await dbFunctions.UpdateCharCount(text, roomId, userId); + const pushToken = await dbFunctions.GetPushToken(nextPlayerId); + SendTurnNotification(pushToken, roomId, room.title) + } //send response if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId diff --git a/routers/userRouter.js b/routers/userRouter.js index 0f4155d..ced65c7 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -7,17 +7,19 @@ const AddNewUser = async (req, res, next) => { try { - const name = req.query.name; - const email = req.query.email; - const password = req.query.password; - - if (!name) throw new Error('No name provided') - if (name.length < 4) throw new Error('Name must be at least 4 characters') - if (name.length > 20) throw new Error('Name must be max 20 characters') - if (!email) throw new Error('No email provided') - if (!password) throw new Error('No password provided') - - await dbFunctions.CreateUser(name, email, password); + // const name = req.query.name; + // const email = req.query.email; + // const password = req.query.password; + // const pushToken = req.query.pushToken; + const {name, email, password, pushToken} = req.query; + + if (!name) throw new Error('No name provided'); + if (name.length < 4) throw new Error('Name must be at least 4 characters'); + if (name.length > 20) throw new Error('Name must be max 20 characters'); + if (!email) throw new Error('No email provided'); + if (!password) throw new Error('No password provided'); + + await dbFunctions.CreateUser(name, email, password, pushToken); next(); //res.status(201).send('Created user and logged in'); From af80f930176a3b865f0e4280dace659f0b97b88c Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 16 Nov 2022 15:51:05 -0300 Subject: [PATCH 047/127] bugfix --- routers/scenarioRouter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 8de4288..3f196a3 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -14,8 +14,8 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //initial error checks if (!roomId) throw new Error('No room_id provided'); - if (!scenario) throw new Error('No text provided'); - if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); + if (!text) throw new Error('No text provided'); + if (text.length < 3) throw new Error('text must be at least 3 characters long'); if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries From 4319c3092309e0b2a1c445c597bc9f861f7d79df Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 16 Nov 2022 18:31:46 -0300 Subject: [PATCH 048/127] push notification fix --- database/dbFunctions.js | 4 +--- notifications/notifications.js | 11 ++++++----- routers/scenarioRouter.js | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index ff3dfaa..fc91a7e 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -138,9 +138,7 @@ function MakeSureItsPlayersTurn(room, userId) { } if (!room.next_player_id || room.next_player_id != userId) { - throw new Error(`its not the logged players turn. - poster id was ${userId}, - but its actually user with id ${room.nextPlayerId} who is next`); + throw new Error(`its not the logged players turn. poster id was ${userId}, but its actually user with id ${room.next_player_id} who is next`); } } diff --git a/notifications/notifications.js b/notifications/notifications.js index e43b64c..ab24c97 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -2,6 +2,7 @@ const { Expo } = require('expo-server-sdk'); let expo = new Expo(); + const SendPushNotification = async (pushToken, title, body, data) => { if (!Expo.isExpoPushToken(pushToken)) { @@ -30,15 +31,15 @@ const SendPushNotification = async (pushToken, title, body, data) => { } -const SendTurnNotification = (pushToken, roomId, storyTitle) => { - +const SendTurnNotification = (roomId, storyTitle, user) => { SendPushNotification( - pushToken, - 'Your turn!', + user.expo_push_token, + `Your turn, ${user.name}!`, storyTitle + ' was updated. You are the next player in line to write!', { type: 'turn', - roomId: roomId + roomId: roomId, + userId: user.id } ); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 3f196a3..19daf98 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -38,8 +38,8 @@ const AttachAddScenarioTransaction = async (req, res, next) => { if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); else { await dbFunctions.UpdateCharCount(text, roomId, userId); - const pushToken = await dbFunctions.GetPushToken(nextPlayerId); - SendTurnNotification(pushToken, roomId, room.title) + const nextPlayer = await dbFunctions.GetLoggedUserInfo(nextPlayerId); + SendTurnNotification(roomId, room.title, nextPlayer); } //send response From 148e6b51da018ddbb7d907e1b21c5b3806517a46 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 17 Nov 2022 15:49:12 -0300 Subject: [PATCH 049/127] updated available rooms query --- routers/roomRouter.js | 166 ++++++++++++++++++++++++++++++------------ 1 file changed, 120 insertions(+), 46 deletions(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 4062bbd..2a98c5c 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -2,7 +2,7 @@ const express = require('express'); const roomRouter = express.Router(); const db = require('../database/dbConnect.js'); const dbFunctions = require('../database/dbFunctions'); -const {isAuth} = require('../middleware/authentication'); +const { isAuth } = require('../middleware/authentication'); //GETTER FUNCTIONS const GetRoomData = async (req, res, next) => { @@ -21,27 +21,83 @@ const GetRoomData = async (req, res, next) => { } const AttachAvailableRoomsQuery = async (req, res, next) => { - req.roomQuery = ( - `SELECT - rooms.id, - rooms.title AS title, - rooms.description AS description, - users.name AS user, - (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - COUNT(scenarios.id) AS scenario_count - FROM rooms - JOIN rooms_users ON rooms.id = rooms_users.room_id - JOIN users ON rooms_users.user_id = users.id - JOIN scenarios ON scenarios.room_id = rooms.id - WHERE - rooms.full = false - AND rooms.finished = false - AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) - GROUP BY (rooms_users.user_id, rooms.id, users.name) - ORDER BY id;` - ); - req.roomQueryParams = [req.userId]; - next(); + try { + + const availableRooms = await GetRoomsDb( + `SELECT + rooms.id, + rooms.title AS title, + rooms.description AS description, + users.name AS user, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + COUNT(scenarios.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms.id = rooms_users.room_id + JOIN users ON rooms_users.user_id = users.id + JOIN scenarios ON scenarios.room_id = rooms.id + WHERE + rooms.full = false + AND rooms.finished = false + AND rooms.next_player_id IS NULL + AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) + GROUP BY (rooms_users.user_id, rooms.id, users.name) + ORDER BY id`, + [req.userId] + ) + + const newRooms = availableRooms.filter(room => + (room.scenario_count < 4) + ); + const oldRooms = availableRooms.filter(room => room.scenario_count >= 4); + + if (newRooms.length > 3) newRooms.splice(3, newRooms.length - 3); + if (oldRooms.length > 3) oldRooms.splice(3, oldRooms.length - 3); + + res.status(200).json({ new: newRooms, old: oldRooms }); + + } catch (error) { + + res.status(400).send('unable to get available rooms: ' + error.message); + + } + + //old code + // req.roomQuery = ( + // `SELECT + // rooms.id, + // rooms.title AS title, + // rooms.description AS description, + // users.name AS user, + // (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + // COUNT(scenarios.id) AS scenario_count + // FROM rooms + // JOIN rooms_users ON rooms.id = rooms_users.room_id + // JOIN users ON rooms_users.user_id = users.id + // JOIN scenarios ON scenarios.room_id = rooms.id + // WHERE + // rooms.full = false + // AND rooms.finished = false + // AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) + // GROUP BY (rooms_users.user_id, rooms.id, users.name) + // ORDER BY id;` + // ); + // /* + // this should be limited to 3 new rooms and 3 old rooms! + // cannot do double queries here + // can do in many ways + // seems reasonable that the backend should return new and old in separate objects + // this means we also have to change the front-end to accomodate for this + // alternatively we split into 2 + // but no, right now we will always query together. + // However - we COULD make 2 queries here + // Or take the array of retrieved rooms and filter them into 2 array + // and limit that array + // but idk, kina like the double query idea. + // seems we might have to custom make this function since it will be a special case + // Alternatively we could + // */ + // req.roomQueryParams = [req.userId]; + // next(); } const AttachUserRoomsQuery = async (req, res, next) => { @@ -108,7 +164,7 @@ const AttachCreateRoomTransaction = async (req, res, next) => { //TRY ADD TO DATABASE await dbFunctions.RemoveKeyFromLoggedUser(req.userId); const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, req.userId); - req.responseMessage = {success: true, message: 'new room added!', roomId: newRoomId}; + req.responseMessage = { success: true, message: 'new room added!', roomId: newRoomId }; } next(); @@ -155,32 +211,50 @@ async function RetrieveRooms(req, res) { try { const query = await db.query(req.roomQuery, req.roomQueryParams); - let rooms = []; - query.rows.forEach(room => { - - let roomAlreadyInArray = false; - - rooms.forEach(roomToCheck => { - if (roomToCheck.id == room.id) { - roomAlreadyInArray = true; - if (room.user == room.creator) return; - roomToCheck.writers.push(room.user); - } - }) - - if (!roomAlreadyInArray) { - if (room.creator != room.user) { - room.writers = [room.user]; - } - else room.writers = []; - delete room.user; - rooms.push(room); - } - }); + const rooms = ParseRoomsForFrontend(query.rows); res.status(200).json(rooms); } catch (error) { res.status(400).send('unable to get your rooms: ' + error.message); } -} \ No newline at end of file +} + +async function GetRoomsDb(roomQuery, roomQueryParams) { + + const query = await db.query(roomQuery, roomQueryParams); + const rooms = ParseRoomsForFrontend(query.rows); + return rooms; + +} + +function ParseRoomsForFrontend(unparsedRooms) { + //I have like... no idea what this function does + //I think it organizes the room so that users, writers, creators, players etc + //are put in the way expected by frontend + let rooms = []; + unparsedRooms.forEach(room => { + + let roomAlreadyInArray = false; + + rooms.forEach(roomToCheck => { + if (roomToCheck.id == room.id) { + roomAlreadyInArray = true; + if (room.user == room.creator) + return; + roomToCheck.writers.push(room.user); + } + }); + + if (!roomAlreadyInArray) { + if (room.creator != room.user) { + room.writers = [room.user]; + } + else + room.writers = []; + delete room.user; + rooms.push(room); + } + }); + return rooms; +} From 5066526a2e0aee980f8a4e9a76dd01bd56bfadca Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 17 Nov 2022 17:33:17 -0300 Subject: [PATCH 050/127] turn change on deadline --- database/dbFunctions.js | 31 +++++++- package-lock.json | 158 ++++++++++++++++++++++++++++++++++++++ package.json | 1 + routers/roomRouter.js | 3 +- routers/scenarioRouter.js | 26 +++++-- 5 files changed, 211 insertions(+), 8 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index ff3dfaa..bb3adcd 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -258,6 +258,32 @@ async function RemoveKeyFromLoggedUser(userId) { ); } +async function CheckDeadline(roomId) { + + console.log('checking dealine'); + + //get room to make sure we are actually out of time + const room = await db.query( + `SELECT turn_end, next_player_id FROM rooms WHERE id=$1`, + [roomId] + ); + + const { turn_end, next_player_id } = room.rows[0]; + + if (!turn_end) return; + if (!next_player_id) return; + if (turn_end > new Date()) return; //not deadline yet :) + + //update turnEnd and nextPlayer + await ResetRoomTurnEnd(roomId); + const players = await GetPlayersInRoom(roomId); + const nextPlayerId = await GetNextPlayerId(players, next_player_id); + await SetNextPlayerInRoom(roomId, nextPlayerId); + + console.log('deadline reset'); + +} + //TRANSACTIONS async function BeginTransaction() { await db.query('BEGIN'); @@ -282,7 +308,7 @@ async function TryTransaction(req, res, next) { catch (error) { Rollback(); console.error(error); - res.status(400).send({ok: false, message: error.message}); + res.status(400).send({ ok: false, message: error.message }); } } @@ -316,5 +342,6 @@ module.exports = { GetLoggedUserInfo, Login, GetPushToken, - GetNextPlayerId + GetNextPlayerId, + CheckDeadline }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 39cdba1..50d9396 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", + "node-schedule": "^2.1.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.8.0" @@ -370,6 +371,18 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "node_modules/cron-parser": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", + "dependencies": { + "is-nan": "^1.3.2", + "luxon": "^1.26.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -378,6 +391,21 @@ "ms": "2.0.0" } }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -676,6 +704,17 @@ "node": ">= 0.4.0" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -781,6 +820,21 @@ "node": ">=8" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -861,6 +915,11 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -872,6 +931,14 @@ "node": ">=10" } }, + "node_modules/luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", + "engines": { + "node": "*" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -1050,6 +1117,19 @@ } } }, + "node_modules/node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", + "dependencies": { + "cron-parser": "^3.5.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -1091,6 +1171,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -1525,6 +1613,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "node_modules/split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", @@ -1968,6 +2061,15 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "cron-parser": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", + "requires": { + "is-nan": "^1.3.2", + "luxon": "^1.26.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1976,6 +2078,15 @@ "ms": "2.0.0" } }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -2219,6 +2330,14 @@ "function-bind": "^1.1.1" } }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -2297,6 +2416,15 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -2375,6 +2503,11 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2383,6 +2516,11 @@ "yallist": "^4.0.0" } }, + "luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2503,6 +2641,16 @@ "whatwg-url": "^5.0.0" } }, + "node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", + "requires": { + "cron-parser": "^3.5.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + } + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -2532,6 +2680,11 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2863,6 +3016,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", diff --git a/package.json b/package.json index d1a4934..8e76789 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-fetch": "^2.6.1", + "node-schedule": "^2.1.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.8.0" diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 4062bbd..dbbc466 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -8,6 +8,7 @@ const {isAuth} = require('../middleware/authentication'); const GetRoomData = async (req, res, next) => { try { + await dbFunctions.CheckDeadline(req.params.id) const room = await dbFunctions.GetRoomInfo(req.params.id) room.players = await dbFunctions.GetPlayersInRoom(req.params.id); room.scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); @@ -150,7 +151,7 @@ roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); //EXPORT module.exports = roomRouter; -//FUNCTIONS +//MIDDLEWARE async function RetrieveRooms(req, res) { try { diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 8de4288..725664f 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,15 +1,23 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); -const {isAuth} = require('../middleware/authentication'); -const {SendTurnNotification} = require('../notifications/notifications') +const { isAuth } = require('../middleware/authentication'); +const { SendTurnNotification } = require('../notifications/notifications') +// const schedule = require('node-schedule'); + +// const time = new Date(Date.now() - 10770000); +// console.log('scheduling for time: ', time); +// const job = schedule.scheduleJob(time, () =>{ +// console.log('scheduled event!!!') +// dbFunctions.OutOfTime(52); +// }); const AttachAddScenarioTransaction = async (req, res, next) => { req.Transaction = async () => { const userId = req.userId; - const {roomId, text} = req.query + const { roomId, text } = req.query const isEnd = (req.query.end == true); //initial error checks @@ -19,6 +27,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries + await dbFunctions.CheckDeadline(roomId); const players = await dbFunctions.GetPlayersInRoom(roomId); const room = await dbFunctions.GetRoomInfo(roomId); @@ -31,15 +40,22 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //carry out the transaction const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); - const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); - if (isEnd) await dbFunctions.GiveKeyToEachPlayer(roomId); + if (isEnd) { + await dbFunctions.GiveKeyToEachPlayer(roomId); + } else { await dbFunctions.UpdateCharCount(text, roomId, userId); const pushToken = await dbFunctions.GetPushToken(nextPlayerId); SendTurnNotification(pushToken, roomId, room.title) + + //schedule an automatic update on deadline + // const job = schedule.scheduleJob(turnEnd, () =>{ + // dbFunctions.OutOfTime(roomId); + // }); + } //send response From 0ea560d3008eb17f22c655593ad59e169ab4a7b0 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 18 Nov 2022 17:41:29 -0300 Subject: [PATCH 051/127] strike system implemented --- database/dbFunctions.js | 88 +++++++++++++++++++++++++++------- notifications/notifications.js | 36 ++++++++++++-- routers/roomRouter.js | 9 +++- routers/scenarioRouter.js | 71 +++++++++++++-------------- routers/userRouter.js | 5 -- 5 files changed, 144 insertions(+), 65 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index bb3adcd..4867aaf 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -1,5 +1,6 @@ const db = require('./dbConnect.js'); const bcrypt = require('bcrypt'); +const { SendStrikeNotification, SendKickNotification } = require('../notifications/notifications.js'); //AUTH async function CreateUser(name, email, password, pushToken) { @@ -115,6 +116,18 @@ function MakeSurePlayerHasEnoughChars(players, scenario, userId) { }); } +function MakeSurePlayerIsActive(players, userId){ + let playerFound = false; + players.forEach(player => { + if (player.id == userId) { + playerFound = true; + if(!player.active) throw new Error('player has been kicked'); + }; + }); + + if(!playerFound) throw new Error('player is not in this room'); +} + async function MakeSureItsNotTheLastTurn(roomId) { const scenariosQuery = await db.query( `SELECT * @@ -258,30 +271,72 @@ async function RemoveKeyFromLoggedUser(userId) { ); } +async function AddStrike(roomId, userId) { + + const query = await db.query( + `UPDATE rooms_users + SET strikes = strikes + 1 + WHERE room_id = $1 AND user_id = $2 + RETURNING strikes`, + [roomId, userId] + ); + + return query.rows[0].strikes; + +} + +async function DeactivatePlayer(roomId, userId) { + + await db.query(` + UPDATE rooms_users + SET active = false + WHERE room_id = $1 AND user_id = $2 + `, [roomId, userId]); + +} + +async function SetRoomSearching(roomId) { + + await db.query(` + UPDATE rooms + SET "full"=false, next_player_id=null, turn_end=null + WHERE id=$1 + `, [roomId]); + +} + async function CheckDeadline(roomId) { - console.log('checking dealine'); + const room = await GetRoomInfo(roomId); + const { turn_end, title, next_player_id: currentPlayerId } = room; - //get room to make sure we are actually out of time - const room = await db.query( - `SELECT turn_end, next_player_id FROM rooms WHERE id=$1`, - [roomId] - ); + if (!turn_end) return false; + if (!currentPlayerId) return false; + if (turn_end > new Date()) return false; //not deadline yet :) - const { turn_end, next_player_id } = room.rows[0]; + const strikes = await AddStrike(roomId, currentPlayerId); + const currentPlayer = await GetLoggedUserInfo(currentPlayerId); + const pushToken = currentPlayer.expo_push_token; - if (!turn_end) return; - if (!next_player_id) return; - if (turn_end > new Date()) return; //not deadline yet :) + if (strikes >= 3) { + DeactivatePlayer(roomId, currentPlayerId); + SetRoomSearching(roomId); + SendKickNotification(pushToken, title); + } + else { + PassTurn(roomId, currentPlayerId); + SendStrikeNotification(pushToken, title, strikes, roomId); + } - //update turnEnd and nextPlayer + return true; + +} + +async function PassTurn(roomId, currentPlayerId) { await ResetRoomTurnEnd(roomId); const players = await GetPlayersInRoom(roomId); - const nextPlayerId = await GetNextPlayerId(players, next_player_id); + const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); await SetNextPlayerInRoom(roomId, nextPlayerId); - - console.log('deadline reset'); - } //TRANSACTIONS @@ -343,5 +398,6 @@ module.exports = { Login, GetPushToken, GetNextPlayerId, - CheckDeadline + CheckDeadline, + MakeSurePlayerIsActive }; \ No newline at end of file diff --git a/notifications/notifications.js b/notifications/notifications.js index e43b64c..a7767d2 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -1,8 +1,9 @@ const { Expo } = require('expo-server-sdk'); +const dbFunctions = require('../database/dbFunctions'); let expo = new Expo(); -const SendPushNotification = async (pushToken, title, body, data) => { +const SendNotification = async (pushToken, title, body, data) => { if (!Expo.isExpoPushToken(pushToken)) { console.error(`Push token ${pushToken} is not a valid Expo push token`); @@ -22,8 +23,6 @@ const SendPushNotification = async (pushToken, title, body, data) => { try { const ticketChunk = await expo.sendPushNotificationsAsync(chunk); - console.log('sent notification!'); - console.log(ticketChunk); } catch (error) { console.error(error); } @@ -32,7 +31,7 @@ const SendPushNotification = async (pushToken, title, body, data) => { const SendTurnNotification = (pushToken, roomId, storyTitle) => { - SendPushNotification( + SendNotification( pushToken, 'Your turn!', storyTitle + ' was updated. You are the next player in line to write!', @@ -44,4 +43,31 @@ const SendTurnNotification = (pushToken, roomId, storyTitle) => { } -module.exports = { SendTurnNotification }; \ No newline at end of file +const SendStrikeNotification = async (pushToken, storyTitle, strikes, roomId) => { + + SendNotification( + pushToken, + `❌ You missed your turn in "${storyTitle}"`, + `You now have ${strikes} strikes. 3 Strikes and you are out!`, + { + type: 'strike', + roomId: roomId + } + ) + +} + +const SendKickNotification = async (pushToken, storyTitle) => { + + SendNotification( + pushToken, + `You got kicked from "${storyTitle}"`, + `You missed 3 turns. You will no longer be able to contribute to this story`, + { + type: 'kick', + } + ) + +} + +module.exports = { SendTurnNotification, SendStrikeNotification, SendKickNotification }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index dbbc466..ff811a4 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -59,7 +59,12 @@ const AttachUserRoomsQuery = async (req, res, next) => { case when rooms.next_player_id = $1 then true else false end as users_turn FROM rooms JOIN rooms_users ON rooms_users.room_id = rooms.id - WHERE EXISTS (SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1);` + WHERE EXISTS ( + SELECT * FROM rooms_users + WHERE room_id = rooms.id + AND user_id = $1 + ) + AND rooms_users.active = true;` ); req.roomQueryParams = [req.userId]; next(); @@ -141,10 +146,12 @@ const AttachJoinRoomTransaction = async (req, res, next) => { //MOUNT ROUTes roomRouter.use(isAuth); + roomRouter.get('/data/:id', GetRoomData); roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); roomRouter.get('/archive', AttachArchiveQuery, RetrieveRooms); + roomRouter.post('/', AttachCreateRoomTransaction, dbFunctions.TryTransaction); roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 725664f..6d59fa9 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -3,14 +3,6 @@ const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const { isAuth } = require('../middleware/authentication'); const { SendTurnNotification } = require('../notifications/notifications') -// const schedule = require('node-schedule'); - -// const time = new Date(Date.now() - 10770000); -// console.log('scheduling for time: ', time); -// const job = schedule.scheduleJob(time, () =>{ -// console.log('scheduled event!!!') -// dbFunctions.OutOfTime(52); -// }); const AttachAddScenarioTransaction = async (req, res, next) => { @@ -22,45 +14,48 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //initial error checks if (!roomId) throw new Error('No room_id provided'); - if (!scenario) throw new Error('No text provided'); - if (scenario.length < 3) throw new Error('text must be at least 3 characters long'); + if (!text) throw new Error('No text provided'); + if (text.length < 3) throw new Error('text must be at least 3 characters long'); if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries - await dbFunctions.CheckDeadline(roomId); - const players = await dbFunctions.GetPlayersInRoom(roomId); - const room = await dbFunctions.GetRoomInfo(roomId); - - //some db checks - dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); - dbFunctions.MakeSureItsPlayersTurn(room, userId); - dbFunctions.MakeSureItsNotFinished(room); - dbFunctions.MakeSureDeadlineHasNotPassed(room); - if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); + const deadlinePassed = await dbFunctions.CheckDeadline(roomId); - //carry out the transaction - const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); - const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); - const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); - await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); - if (isEnd) { - await dbFunctions.GiveKeyToEachPlayer(roomId); + if (deadlinePassed) { + req.responseMessage = 'deadline already passed!'; } else { - await dbFunctions.UpdateCharCount(text, roomId, userId); - const pushToken = await dbFunctions.GetPushToken(nextPlayerId); - SendTurnNotification(pushToken, roomId, room.title) + const players = await dbFunctions.GetPlayersInRoom(roomId); + const room = await dbFunctions.GetRoomInfo(roomId); - //schedule an automatic update on deadline - // const job = schedule.scheduleJob(turnEnd, () =>{ - // dbFunctions.OutOfTime(roomId); - // }); + console.log(players); + console.log('user id: ', userId); - } + //some db checks + dbFunctions.MakeSurePlayerIsActive(players, userId); + dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); + dbFunctions.MakeSureItsPlayersTurn(room, userId); + dbFunctions.MakeSureItsNotFinished(room); + if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); - //send response - if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId - else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + //carry out the transaction + const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); + const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); + const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); + await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); + if (isEnd) { + await dbFunctions.GiveKeyToEachPlayer(roomId); + } + else { + await dbFunctions.UpdateCharCount(text, roomId, userId); + const pushToken = await dbFunctions.GetPushToken(nextPlayerId); + SendTurnNotification(pushToken, roomId, room.title) + } + + //send response + if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId + else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + } } next(); diff --git a/routers/userRouter.js b/routers/userRouter.js index 9d5e44a..9466bf0 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -6,11 +6,6 @@ const {isAuth, Login} = require('../middleware/authentication'); const AddNewUser = async (req, res, next) => { try { - - // const name = req.query.name; - // const email = req.query.email; - // const password = req.query.password; - // const pushToken = req.query.pushToken; const {name, email, password, pushToken} = req.query; const exists = text =>{ From 52a412877566c8678d8d6d521f0f0d9c4bd0a3bc Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 18 Nov 2022 18:26:51 -0300 Subject: [PATCH 052/127] strikes returned with room data --- database/dbFunctions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 17436b4..47e1321 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -48,7 +48,7 @@ async function GetRoomInfo(roomId) { async function GetPlayersInRoom(roomId) { const query = await db.query( - `SELECT users.id, users.name, rooms_users.char_count, active + `SELECT users.id, users.name, rooms_users.char_count, active, strikes FROM rooms_users JOIN users ON rooms_users.user_id = users.id WHERE rooms_users.room_id = $1 From c37d8aa297074ad2a21483d067c408654d88cec6 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sat, 19 Nov 2022 14:21:11 -0300 Subject: [PATCH 053/127] notification data fix --- database/dbFunctions.js | 2 +- notifications/notifications.js | 9 +++++---- routers/scenarioRouter.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 47e1321..085857b 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -323,7 +323,7 @@ async function CheckDeadline(roomId) { } else { PassTurn(roomId, currentPlayerId); - SendStrikeNotification(pushToken, title, strikes, roomId); + SendStrikeNotification(pushToken, title, strikes, roomId, currentPlayerId); } return true; diff --git a/notifications/notifications.js b/notifications/notifications.js index ddf1973..51690c9 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -29,7 +29,7 @@ const SendNotification = async (pushToken, title, body, data) => { } -const SendTurnNotification = (pushToken, roomId, storyTitle) => { +const SendTurnNotification = (pushToken, roomId, storyTitle, userId) => { SendNotification( pushToken, @@ -38,13 +38,13 @@ const SendTurnNotification = (pushToken, roomId, storyTitle) => { { type: 'turn', roomId: roomId, - userId: user.id + userId: userId } ); } -const SendStrikeNotification = async (pushToken, storyTitle, strikes, roomId) => { +const SendStrikeNotification = async (pushToken, storyTitle, strikes, roomId, userId) => { SendNotification( pushToken, @@ -52,7 +52,8 @@ const SendStrikeNotification = async (pushToken, storyTitle, strikes, roomId) => `You now have ${strikes} strikes. 3 Strikes and you are out!`, { type: 'strike', - roomId: roomId + roomId: roomId, + userId: userId } ) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 6d59fa9..89a6a25 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -49,7 +49,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { else { await dbFunctions.UpdateCharCount(text, roomId, userId); const pushToken = await dbFunctions.GetPushToken(nextPlayerId); - SendTurnNotification(pushToken, roomId, room.title) + SendTurnNotification(pushToken, roomId, room.title, nextPlayerId) } //send response From 4e6898cdec4a317c74258e220f67a4cfbc29a885 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sat, 19 Nov 2022 16:52:37 -0300 Subject: [PATCH 054/127] notification hotfixing --- database/dbFunctions.js | 67 +++++++++++++++++++++++++++++++-------- routers/roomRouter.js | 2 +- routers/scenarioRouter.js | 10 +++--- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 085857b..28817f1 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -1,6 +1,6 @@ const db = require('./dbConnect.js'); const bcrypt = require('bcrypt'); -const { SendStrikeNotification, SendKickNotification } = require('../notifications/notifications.js'); +const { SendStrikeNotification, SendKickNotification, SendTurnNotification } = require('../notifications/notifications.js'); //AUTH async function CreateUser(name, email, password, pushToken) { @@ -116,16 +116,16 @@ function MakeSurePlayerHasEnoughChars(players, scenario, userId) { }); } -function MakeSurePlayerIsActive(players, userId){ +function MakeSurePlayerIsActive(players, userId) { let playerFound = false; players.forEach(player => { if (player.id == userId) { playerFound = true; - if(!player.active) throw new Error('player has been kicked'); + if (!player.active) throw new Error('player has been kicked'); }; }); - if(!playerFound) throw new Error('player is not in this room'); + if (!playerFound) throw new Error('player is not in this room'); } async function MakeSureItsNotTheLastTurn(roomId) { @@ -317,12 +317,12 @@ async function CheckDeadline(roomId) { const pushToken = currentPlayer.expo_push_token; if (strikes >= 3) { - DeactivatePlayer(roomId, currentPlayerId); - SetRoomSearching(roomId); + await DeactivatePlayer(roomId, currentPlayerId); + await SetRoomSearching(roomId); SendKickNotification(pushToken, title); } else { - PassTurn(roomId, currentPlayerId); + await PassTurn(room, currentPlayerId); SendStrikeNotification(pushToken, title, strikes, roomId, currentPlayerId); } @@ -330,11 +330,50 @@ async function CheckDeadline(roomId) { } -async function PassTurn(roomId, currentPlayerId) { - await ResetRoomTurnEnd(roomId); - const players = await GetPlayersInRoom(roomId); - const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); - await SetNextPlayerInRoom(roomId, nextPlayerId); +async function PassTurn(room, currentPlayerId) { + + if (room.full) { + const players = await GetPlayersInRoom(room.id); + const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); + + await db.query(` + UPDATE rooms + SET + turn_end = (NOW() + interval '2 day'), + next_player_id = $1 + WHERE id = $2 + `, [nextPlayerId, room.id] + ); + + //notify the next player + const nextPlayer = await GetLoggedUserInfo(nextPlayerId); + SendTurnNotification(nextPlayer.expo_push_token, room.id, room.title, nextPlayerId); + } + else { + await db.query(` + UPDATE rooms + SET + turn_end = null, + next_player_id = null + WHERE id = $1 + `, [room.id] + ); + } + + +} + +async function EndStory(roomId) { + GiveKeyToEachPlayer(roomId); + db.query( + `UPDATE rooms + SET + turn_end = null, + next_player_id = null, + finished = true + WHERE id = $1`, + [roomId] + ); } //TRANSACTIONS @@ -397,5 +436,7 @@ module.exports = { GetPushToken, GetNextPlayerId, CheckDeadline, - MakeSurePlayerIsActive + MakeSurePlayerIsActive, + PassTurn, + EndStory }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 612b6f9..50b6420 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -189,8 +189,8 @@ const AttachJoinRoomTransaction = async (req, res, next) => { //update await dbFunctions.AddUserToRoom(roomId, req.userId); - await dbFunctions.ResetRoomTurnEnd(roomId); await dbFunctions.UpdateRoomFullStatus(roomId); + await dbFunctions.ResetRoomTurnEnd(roomId); await dbFunctions.SetNextPlayerInRoom(roomId, req.userId); //response diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 89a6a25..e2aee3c 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -41,15 +41,15 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //carry out the transaction const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); - const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); - await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); + //const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); + //await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); + if (isEnd) { - await dbFunctions.GiveKeyToEachPlayer(roomId); + await dbFunctions.EndStory(roomId); } else { + await dbFunctions.PassTurn(room, userId); await dbFunctions.UpdateCharCount(text, roomId, userId); - const pushToken = await dbFunctions.GetPushToken(nextPlayerId); - SendTurnNotification(pushToken, roomId, room.title, nextPlayerId) } //send response From 7f5574d347065a27a70a3bd3213c922d8180b535 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 21 Nov 2022 17:57:59 -0300 Subject: [PATCH 055/127] Add a check to make sure really is full --- database/dbFunctions.js | 82 ++++++++++++++++++++++++++++++++++----- routers/roomRouter.js | 12 ++++-- routers/scenarioRouter.js | 17 ++++---- 3 files changed, 88 insertions(+), 23 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 28817f1..743cfab 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -73,10 +73,10 @@ const GetScenariosInRoom = async (roomId) => { } -function GetNextPlayerId(players, userId) { +function GetNextPlayerId(players, currentPlayerId) { let i = 0; players.forEach((player, j) => { - if (player.id != userId) return; + if (player.id != currentPlayerId) return; if (j == (players.length - 1)) return; i = j + 1; }); @@ -303,33 +303,95 @@ async function SetRoomSearching(roomId) { } -async function CheckDeadline(roomId) { +async function SetRoomFull(room, players, scenarios) { + + if (room.full && room.next_player_id && room.turn_end) return; + + const nextPlayerId = ( + room.next_player_id ? + room.next_player_id : + GetNextPlayerId(players, scenarios[scenarios.length - 1].creator_id) + ); + + const mustUpdateTurnEnd = (!room.turn_end || room.turn_end < new Date()); + + await db.query(` + UPDATE rooms + SET + "full" = true, + next_player_id = $1 + WHERE id = $2 + `, [nextPlayerId, room.id] + ); + + if (!mustUpdateTurnEnd) return; + + await db.query(` + UPDATE rooms + SET turn_end = (NOW() + interval '2 day') + WHERE id = $1 + `, [room.id]); + +} + +async function CheckRoomSearching(room, players, scenarios) { + + const activePlayers = players.filter(player => player.active); + + if (activePlayers.length == 4) { + await SetRoomFull(room, players, scenarios); + return false; + } + else { + await SetRoomSearching(room.id); + return true; + } + +} + +async function CheckRoomDeadline(room) { + + //Checking if the room deadline has been reached, and passing turn if it has + //returning TRUE if the turn was passed - const room = await GetRoomInfo(roomId); const { turn_end, title, next_player_id: currentPlayerId } = room; if (!turn_end) return false; if (!currentPlayerId) return false; - if (turn_end > new Date()) return false; //not deadline yet :) + if (turn_end > new Date()) return false; - const strikes = await AddStrike(roomId, currentPlayerId); + const strikes = await AddStrike(room.id, currentPlayerId); const currentPlayer = await GetLoggedUserInfo(currentPlayerId); const pushToken = currentPlayer.expo_push_token; if (strikes >= 3) { - await DeactivatePlayer(roomId, currentPlayerId); - await SetRoomSearching(roomId); + await DeactivatePlayer(room.id, currentPlayerId); + if (!roomSearching) await SetRoomSearching(room); SendKickNotification(pushToken, title); } else { await PassTurn(room, currentPlayerId); - SendStrikeNotification(pushToken, title, strikes, roomId, currentPlayerId); + SendStrikeNotification(pushToken, title, strikes, room.id, currentPlayerId); } return true; } +async function CheckRoomInfo(room, players, scenarios) { + + //this function returns TRUE if a correction was made to who is the next player + + //check to see if the room has space and if so set it searching for new players + const roomSearching = await CheckRoomSearching(room, players, scenarios); + if (roomSearching) return true; + + //check deadline, if it has been reached we should move to the next player + const turnPassed = await CheckRoomDeadline(room); + return turnPassed; + +} + async function PassTurn(room, currentPlayerId) { if (room.full) { @@ -435,7 +497,7 @@ module.exports = { Login, GetPushToken, GetNextPlayerId, - CheckDeadline, + CheckRoomInfo, MakeSurePlayerIsActive, PassTurn, EndStory diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 50b6420..5463593 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -8,10 +8,16 @@ const { isAuth } = require('../middleware/authentication'); const GetRoomData = async (req, res, next) => { try { - await dbFunctions.CheckDeadline(req.params.id) + const room = await dbFunctions.GetRoomInfo(req.params.id) - room.players = await dbFunctions.GetPlayersInRoom(req.params.id); - room.scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); + const players = await dbFunctions.GetPlayersInRoom(req.params.id); + const scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); + + await dbFunctions.CheckRoomInfo(room, players, scenarios); + + room.players = players; + room.scenarios = scenarios; + res.status(200).send(room); } catch (error) { diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index e2aee3c..323893e 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -19,18 +19,15 @@ const AttachAddScenarioTransaction = async (req, res, next) => { if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries - const deadlinePassed = await dbFunctions.CheckDeadline(roomId); + const room = await dbFunctions.GetRoomInfo(roomId); + const players = await dbFunctions.GetPlayersInRoom(roomId); + const scenarios = await dbFunctions.GetScenariosInRoom(roomId) + const turnCorrectionMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); - if (deadlinePassed) { - req.responseMessage = 'deadline already passed!'; + if (turnCorrectionMade) { + req.responseMessage = 'Its not your turn to write'; } else { - const players = await dbFunctions.GetPlayersInRoom(roomId); - const room = await dbFunctions.GetRoomInfo(roomId); - - console.log(players); - console.log('user id: ', userId); - //some db checks dbFunctions.MakeSurePlayerIsActive(players, userId); dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); @@ -43,7 +40,7 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); //const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); //await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); - + if (isEnd) { await dbFunctions.EndStory(roomId); } From e3c85bcfcf75666c87273b2907182e61112f4f2b Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 21 Nov 2022 18:46:39 -0300 Subject: [PATCH 056/127] =?UTF-8?q?s=C3=A4tt=20turn=20end=20r=C3=A4tt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/dbFunctions.js | 68 +++++++++++++-------------------------- routers/roomRouter.js | 12 ++++--- routers/scenarioRouter.js | 4 --- 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 743cfab..67f073d 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -101,13 +101,6 @@ async function GetPushToken(userId) { } //CHECKS -function MakeSureDeadlineHasNotPassed(room) { - if (room.turn_end < new Date()) { - //++update player turn - throw new Error('turn has already passed'); - } -} - function MakeSurePlayerHasEnoughChars(players, scenario, userId) { players.forEach(player => { if (player.user_id == userId && player.char_count < scenario.length) { @@ -162,24 +155,6 @@ function MakeSureRoomExists(roomQuery) { } //SETTERS -async function UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd) { - - await db.query( - `UPDATE rooms - SET - next_player_id = $1, - turn_end = $4, - finished = $3 - WHERE id = $2`, - [ - nextPlayerId, - roomId, - isEnd, - turnEnd - ] - ); -} - async function AddScenario(scenario, roomId, userId) { const scenarioQuery = await db.query( 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES ($1, $2, $3) RETURNING *', @@ -226,7 +201,7 @@ async function UpdateRoomFullStatus(roomId) { ); } -async function ResetRoomTurnEnd(roomId) { +async function SetDeadlineIn2Days(roomId) { await db.query( `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, [roomId] @@ -303,6 +278,20 @@ async function SetRoomSearching(roomId) { } +async function Add2DaysToDeadline(room){ + + let newTurnEnd; + if (room.turn_end) newTurnEnd = new Date(new Date(room.turn_end).getTime() + 172800000); + else newTurnEnd = new Date(new Date().getTime() + 172800000); + + await db.query(` + UPDATE rooms + SET turn_end = $2 + WHERE id = $1 + `, [room.id, newTurnEnd]); + +} + async function SetRoomFull(room, players, scenarios) { if (room.full && room.next_player_id && room.turn_end) return; @@ -313,8 +302,6 @@ async function SetRoomFull(room, players, scenarios) { GetNextPlayerId(players, scenarios[scenarios.length - 1].creator_id) ); - const mustUpdateTurnEnd = (!room.turn_end || room.turn_end < new Date()); - await db.query(` UPDATE rooms SET @@ -324,13 +311,8 @@ async function SetRoomFull(room, players, scenarios) { `, [nextPlayerId, room.id] ); - if (!mustUpdateTurnEnd) return; - - await db.query(` - UPDATE rooms - SET turn_end = (NOW() + interval '2 day') - WHERE id = $1 - `, [room.id]); + const mustUpdateTurnEnd = (!room.turn_end || room.turn_end < new Date()); + if (mustUpdateTurnEnd) await Add2DaysToDeadline(room); } @@ -397,15 +379,11 @@ async function PassTurn(room, currentPlayerId) { if (room.full) { const players = await GetPlayersInRoom(room.id); const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); + await SetNextPlayerInRoom(room.id, nextPlayerId) - await db.query(` - UPDATE rooms - SET - turn_end = (NOW() + interval '2 day'), - next_player_id = $1 - WHERE id = $2 - `, [nextPlayerId, room.id] - ); + const deadlineMet = room.turn_end > new Date(); + if (deadlineMet) await SetDeadlineIn2Days(room.id); + else await Add2DaysToDeadline(room); //notify the next player const nextPlayer = await GetLoggedUserInfo(nextPlayerId); @@ -473,12 +451,10 @@ module.exports = { GetPlayersInRoom, GetScenariosInRoom, MakeSureRoomExists, - MakeSureDeadlineHasNotPassed, MakeSurePlayerHasEnoughChars, MakeSureItsNotTheLastTurn, MakeSureItsNotFinished, MakeSureItsPlayersTurn, - UpdateRoomInfo, AddScenario, BeginTransaction, Rollback, @@ -488,7 +464,7 @@ module.exports = { TryTransaction, RemoveKeyFromLoggedUser, CreateNewRoom, - ResetRoomTurnEnd, + SetDeadlineIn2Days, UpdateRoomFullStatus, SetNextPlayerInRoom, AddUserToRoom, diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 5463593..7f0b0cd 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -9,11 +9,15 @@ const GetRoomData = async (req, res, next) => { try { - const room = await dbFunctions.GetRoomInfo(req.params.id) - const players = await dbFunctions.GetPlayersInRoom(req.params.id); + let room = await dbFunctions.GetRoomInfo(req.params.id) + let players = await dbFunctions.GetPlayersInRoom(req.params.id); const scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); - await dbFunctions.CheckRoomInfo(room, players, scenarios); + const roomCorrected = await dbFunctions.CheckRoomInfo(room, players, scenarios); + if (roomCorrected) { + room = await dbFunctions.GetRoomInfo(req.params.id); + players = await dbFunctions.GetPlayersInRoom(req.params.id); + } room.players = players; room.scenarios = scenarios; @@ -196,7 +200,7 @@ const AttachJoinRoomTransaction = async (req, res, next) => { //update await dbFunctions.AddUserToRoom(roomId, req.userId); await dbFunctions.UpdateRoomFullStatus(roomId); - await dbFunctions.ResetRoomTurnEnd(roomId); + await dbFunctions.SetDeadlineIn2Days(roomId); await dbFunctions.SetNextPlayerInRoom(roomId, req.userId); //response diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 323893e..47c20ab 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -2,7 +2,6 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const { isAuth } = require('../middleware/authentication'); -const { SendTurnNotification } = require('../notifications/notifications') const AttachAddScenarioTransaction = async (req, res, next) => { @@ -37,9 +36,6 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //carry out the transaction const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); - const nextPlayerId = (isEnd || !room.full) ? null : dbFunctions.GetNextPlayerId(players, userId); - //const turnEnd = (isEnd || !room.full) ? null : new Date(Date.now() + 172800000); - //await dbFunctions.UpdateRoomInfo(isEnd, roomId, nextPlayerId, turnEnd); if (isEnd) { await dbFunctions.EndStory(roomId); From 20f0a274b71816db447df5436eb014f671a35d11 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 21 Nov 2022 19:02:15 -0300 Subject: [PATCH 057/127] Update roomRouter.js --- routers/roomRouter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 7f0b0cd..d60fdbf 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -50,6 +50,7 @@ const AttachAvailableRoomsQuery = async (req, res, next) => { rooms.full = false AND rooms.finished = false AND rooms.next_player_id IS NULL + AND rooms_users.active = true AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) GROUP BY (rooms_users.user_id, rooms.id, users.name) ORDER BY id`, From 1897c3d027296996a10bd10114ac7d255d4ebc3d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 22 Nov 2022 13:50:09 -0300 Subject: [PATCH 058/127] room correction fix for new rooms --- database/dbFunctions.js | 56 +++++++++++++++++++++++++++++++++------ routers/scenarioRouter.js | 46 +++++++++++++++----------------- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 67f073d..d204bc5 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -278,7 +278,7 @@ async function SetRoomSearching(roomId) { } -async function Add2DaysToDeadline(room){ +async function Add2DaysToDeadline(room) { let newTurnEnd; if (room.turn_end) newTurnEnd = new Date(new Date(room.turn_end).getTime() + 172800000); @@ -316,17 +316,55 @@ async function SetRoomFull(room, players, scenarios) { } -async function CheckRoomSearching(room, players, scenarios) { +async function CorrectRoomSearching(room, players, scenarios) { const activePlayers = players.filter(player => player.active); + const roomSetToSearching = (!room.full && !room.turn_end && !room.next_player_id); + const isNewRoom = (scenarios.length < 4); + + //new room logic + if (isNewRoom) { + if (scenarios.length >= activePlayers.length) { + if (roomSetToSearching) { + return false; + } + else { + await SetRoomSearching(room.id); + return true; + } + } + else { + if (!room.turn_end && !room.next_player_id) { + await db.query(` + UPDATE rooms + SET + "full" = false, + turn_end = NOW() + Interval '2 day', + next_player_id = $1 + WHERE id = $2 + `, [activePlayers[activePlayers.length - 1].id, room.id]); + return true; + } + else { + return false; + } + } + } + //old room logic if (activePlayers.length == 4) { - await SetRoomFull(room, players, scenarios); - return false; + if (room.full && room.next_player_id && room.turn_end) return false; + else { + await SetRoomFull(room, players, scenarios); + return true; + } } else { - await SetRoomSearching(room.id); - return true; + if (!room.full && !room.turn_end && !room.next_player_id) return false; + else { + await SetRoomSearching(room.id); + return true; + } } } @@ -362,11 +400,13 @@ async function CheckRoomDeadline(room) { async function CheckRoomInfo(room, players, scenarios) { + //this function is a bit bloated. Wierd that it is separated in 2. Should probably just check everything here. maybe... + //this function returns TRUE if a correction was made to who is the next player //check to see if the room has space and if so set it searching for new players - const roomSearching = await CheckRoomSearching(room, players, scenarios); - if (roomSearching) return true; + const roomSearchingCorrected = await CorrectRoomSearching(room, players, scenarios); + if (roomSearchingCorrected) return true; //check deadline, if it has been reached we should move to the next player const turnPassed = await CheckRoomDeadline(room); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 47c20ab..f9831b5 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -21,34 +21,32 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const room = await dbFunctions.GetRoomInfo(roomId); const players = await dbFunctions.GetPlayersInRoom(roomId); const scenarios = await dbFunctions.GetScenariosInRoom(roomId) - const turnCorrectionMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); + const turnCorrectionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); - if (turnCorrectionMade) { - req.responseMessage = 'Its not your turn to write'; + if (turnCorrectionsMade) room = await dbFunctions.GetRoomInfo(roomId); + + //some db checks + dbFunctions.MakeSurePlayerIsActive(players, userId); + dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); + dbFunctions.MakeSureItsPlayersTurn(room, userId); + dbFunctions.MakeSureItsNotFinished(room); + if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); + + //carry out the transaction + const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); + + if (isEnd) { + await dbFunctions.EndStory(roomId); } else { - //some db checks - dbFunctions.MakeSurePlayerIsActive(players, userId); - dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); - dbFunctions.MakeSureItsPlayersTurn(room, userId); - dbFunctions.MakeSureItsNotFinished(room); - if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); - - //carry out the transaction - const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); - - if (isEnd) { - await dbFunctions.EndStory(roomId); - } - else { - await dbFunctions.PassTurn(room, userId); - await dbFunctions.UpdateCharCount(text, roomId, userId); - } - - //send response - if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId - else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + await dbFunctions.PassTurn(room, userId); + await dbFunctions.UpdateCharCount(text, roomId, userId); } + + //send response + if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId + else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + } next(); From 6c8a3ade4f0796ad9e7b561d7edfae63f42b92af Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 1 Dec 2022 16:58:06 -0300 Subject: [PATCH 059/127] Update roomRouter.js --- routers/roomRouter.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index d60fdbf..efdd9ac 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -34,6 +34,26 @@ const AttachAvailableRoomsQuery = async (req, res, next) => { try { + //Easier to parse + // SELECT + // rooms.id, + // rooms.title AS title, + // rooms.description AS description, + // STRING_AGG(users.name, ';') AS user, + // (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + // (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count + // FROM rooms + // JOIN rooms_users ON rooms.id = rooms_users.room_id + // JOIN users ON rooms_users.user_id = users.id + // WHERE + // rooms.full = false + // AND rooms.finished = false + // AND rooms.next_player_id IS NULL + // AND rooms_users.active = true + // AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = 1 AND room_id = rooms.id) + // GROUP BY (rooms.id) + // ORDER BY id + const availableRooms = await GetRoomsDb( `SELECT rooms.id, From 63a243b12722c9e8c2eb9c292227f68c0f04ea44 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sat, 3 Dec 2022 14:34:22 -0300 Subject: [PATCH 060/127] added route for fetching user chars --- database/dbFunctions.js | 13 ++++++++++++- routers/roomRouter.js | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index d204bc5..8466f6f 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -99,6 +99,16 @@ async function GetPushToken(userId) { if (query.rowCount != 0) return query.rows[0].expo_push_token; else return null; } +async function GetUserChars(roomId, userId){ + const query = await db.query( + `SELECT char_count + FROM rooms_users + WHERE room_id = $1 AND user_id = $2`, + [roomId, userId] + ) + if (query.rowCount != 0) return query.rows[0].char_count; + else return null; +} //CHECKS function MakeSurePlayerHasEnoughChars(players, scenario, userId) { @@ -516,5 +526,6 @@ module.exports = { CheckRoomInfo, MakeSurePlayerIsActive, PassTurn, - EndStory + EndStory, + GetUserChars }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index efdd9ac..0f05bc0 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -174,6 +174,28 @@ const AttachArchiveQuery = async (req, res, next) => { req.roomQueryParams = []; next(); +} +const GetUserChars = async (req, res, next) => { + + try { + const {userId, roomId} = req.query; + if(!userId) throw new Error('must provide a userId to get the chars'); + if(!roomId) throw new Error('must provide a roomId to get the chars'); + const chars = await dbFunctions.GetUserChars(roomId, userId); + if (!chars) throw new Error('could not find any chars for that room and user'); + res.status(200).send({ + ok: true, + message: 'successfully found chars', + chars: chars + }) + } catch (error) { + console.error(error); + res.status(400).send({ + ok: false, + message: 'Failed to get chars: ' + error.message + }); + } + } //POST TRANSACTION FUNCTIONS @@ -235,6 +257,8 @@ const AttachJoinRoomTransaction = async (req, res, next) => { roomRouter.use(isAuth); roomRouter.get('/data/:id', GetRoomData); +roomRouter.get('/user/chars', GetUserChars); + roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); roomRouter.get('/archive', AttachArchiveQuery, RetrieveRooms); From 58c1370678f56019af1d5881576bf8f8b017dbbc Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 9 Dec 2022 16:22:02 -0300 Subject: [PATCH 061/127] begin work on input validation --- app.js | 16 ++++------------ middleware/validation.js | 9 +++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 middleware/validation.js diff --git a/app.js b/app.js index 3a538b0..e2d2633 100644 --- a/app.js +++ b/app.js @@ -4,22 +4,12 @@ const app = express(); const PORT = process.env.PORT || 5000; require('dotenv').config() -//test pushing -// const {GetPushToken} = require('./database/dbFunctions'); -// const {SendTurnNotification} = require('./notifications/notifications'); -// const TestPush = async () => { -// const pushToken = await GetPushToken(13); -// SendTurnNotification(pushToken, 52, 'Lambda 5'); -// } -// TestPush(); - - //logging middleware const morgan = require('morgan'); app.use(morgan('tiny')); //error handling for dev environment -if (process.env.NODE_ENV === 'development'){ +if (process.env.NODE_ENV === 'development') { const errorhandler = require('errorhandler'); app.use(errorhandler()); } @@ -42,4 +32,6 @@ app.get('/', async (req, res) => { //start server app.listen(PORT, () => { console.log(`App is running on ${PORT}`) -}) \ No newline at end of file +}) + +//test diff --git a/middleware/validation.js b/middleware/validation.js new file mode 100644 index 0000000..2867bb1 --- /dev/null +++ b/middleware/validation.js @@ -0,0 +1,9 @@ +function onlyAlphaSomeChar(value) { + const re = /^[ A-Z a-z 0-9 . , ; ' " ( ) & @ # % / ! ? * -]+$/; + const allowed = re.test(value); + // if (!allowed) console.error( + // `forbidden characters used. Your text can only contain these characters: A-Z a-z 0-9 . , ; ' " ( ) & @ # $ % / ! ? * -` + // ); + // else console.log('string allowed'); + return allowed; +} \ No newline at end of file From 01659a5e5d5f1bbe42a9bf6203e42cc032bd5fdf Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sun, 11 Dec 2022 15:50:46 -0300 Subject: [PATCH 062/127] input validation --- database/dbFunctions.js | 1 + middleware/authentication.js | 3 +++ middleware/validation.js | 29 +++++++++++++++++------- routers/roomRouter.js | 44 ++++-------------------------------- routers/scenarioRouter.js | 4 +++- routers/userRouter.js | 5 ++++ 6 files changed, 38 insertions(+), 48 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 8466f6f..f33aca5 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -1,6 +1,7 @@ const db = require('./dbConnect.js'); const bcrypt = require('bcrypt'); const { SendStrikeNotification, SendKickNotification, SendTurnNotification } = require('../notifications/notifications.js'); +const { ValidateChars } = require('../middleware/validation'); //AUTH async function CreateUser(name, email, password, pushToken) { diff --git a/middleware/authentication.js b/middleware/authentication.js index 83c6ffe..345769d 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -1,5 +1,6 @@ const jwt = require('jsonwebtoken'); const dbFunctions = require('../database/dbFunctions'); +const { ValidateChars } = require('./validation'); const isAuth = async (req, res, next) => { @@ -31,6 +32,8 @@ const Login = async (req, res, next) => { if (!email) throw new Error('no email provided'); if (!password) throw new Error('no password provided'); + ValidateChars(email); + ValidateChars(password); const user = await dbFunctions.Login(email, password); diff --git a/middleware/validation.js b/middleware/validation.js index 2867bb1..9844452 100644 --- a/middleware/validation.js +++ b/middleware/validation.js @@ -1,9 +1,22 @@ -function onlyAlphaSomeChar(value) { - const re = /^[ A-Z a-z 0-9 . , ; ' " ( ) & @ # % / ! ? * -]+$/; - const allowed = re.test(value); - // if (!allowed) console.error( - // `forbidden characters used. Your text can only contain these characters: A-Z a-z 0-9 . , ; ' " ( ) & @ # $ % / ! ? * -` - // ); - // else console.log('string allowed'); +function ValidateChars(text){ + const arr = text.split(""); + arr.forEach(char => { + if (!CharAllowed(char)){ + throw new Error(`The following character is not allowed: ${char}`); + } + }); +} + +function CharAllowed(char) { + const re = /[ A-Z a-z 0-9 , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]/; + const allowed = re.test(char); return allowed; -} \ No newline at end of file +} + +function CharsAllowed(str) { + const re = /^[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]+$/; + const allowed = re.test(str); + return allowed; +} + +module.exports = { CharsAllowed, ValidateChars }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 0f05bc0..95a4c02 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -3,6 +3,7 @@ const roomRouter = express.Router(); const db = require('../database/dbConnect.js'); const dbFunctions = require('../database/dbFunctions'); const { isAuth } = require('../middleware/authentication'); +const { ValidateChars } = require('../middleware/validation'); //GETTER FUNCTIONS const GetRoomData = async (req, res, next) => { @@ -92,45 +93,6 @@ const AttachAvailableRoomsQuery = async (req, res, next) => { res.status(400).send('unable to get available rooms: ' + error.message); } - - //old code - // req.roomQuery = ( - // `SELECT - // rooms.id, - // rooms.title AS title, - // rooms.description AS description, - // users.name AS user, - // (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, - // COUNT(scenarios.id) AS scenario_count - // FROM rooms - // JOIN rooms_users ON rooms.id = rooms_users.room_id - // JOIN users ON rooms_users.user_id = users.id - // JOIN scenarios ON scenarios.room_id = rooms.id - // WHERE - // rooms.full = false - // AND rooms.finished = false - // AND NOT EXISTS(SELECT * FROM rooms_users WHERE user_id = $1 AND room_id = rooms.id) - // GROUP BY (rooms_users.user_id, rooms.id, users.name) - // ORDER BY id;` - // ); - // /* - // this should be limited to 3 new rooms and 3 old rooms! - // cannot do double queries here - // can do in many ways - // seems reasonable that the backend should return new and old in separate objects - // this means we also have to change the front-end to accomodate for this - // alternatively we split into 2 - // but no, right now we will always query together. - // However - we COULD make 2 queries here - // Or take the array of retrieved rooms and filter them into 2 array - // and limit that array - // but idk, kina like the double query idea. - // seems we might have to custom make this function since it will be a special case - // Alternatively we could - // */ - // req.roomQueryParams = [req.userId]; - // next(); - } const AttachUserRoomsQuery = async (req, res, next) => { @@ -220,6 +182,10 @@ const AttachCreateRoomTransaction = async (req, res, next) => { if (scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); + ValidateChars(title); + ValidateChars(description); + ValidateChars(scenario); + //TRY ADD TO DATABASE await dbFunctions.RemoveKeyFromLoggedUser(req.userId); const newRoomId = await dbFunctions.CreateNewRoom(title, description, scenario, req.userId); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index f9831b5..5e011c2 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -2,6 +2,7 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const { isAuth } = require('../middleware/authentication'); +const { CharsAllowed, ValidateChars } = require('../middleware/validation'); const AttachAddScenarioTransaction = async (req, res, next) => { @@ -12,13 +13,14 @@ const AttachAddScenarioTransaction = async (req, res, next) => { const isEnd = (req.query.end == true); //initial error checks + ValidateChars(text); if (!roomId) throw new Error('No room_id provided'); if (!text) throw new Error('No text provided'); if (text.length < 3) throw new Error('text must be at least 3 characters long'); if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') //make queries - const room = await dbFunctions.GetRoomInfo(roomId); + let room = await dbFunctions.GetRoomInfo(roomId); const players = await dbFunctions.GetPlayersInRoom(roomId); const scenarios = await dbFunctions.GetScenariosInRoom(roomId) const turnCorrectionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); diff --git a/routers/userRouter.js b/routers/userRouter.js index 9466bf0..5d1b85c 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -2,6 +2,7 @@ const express = require('express'); const userRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const {isAuth, Login} = require('../middleware/authentication'); +const { ValidateChars } = require('../middleware/validation'); const AddNewUser = async (req, res, next) => { @@ -18,6 +19,10 @@ const AddNewUser = async (req, res, next) => { if (!exists(name)) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') + + ValidateChars(email); + ValidateChars(password); + ValidateChars(name); await dbFunctions.CreateUser(name, email, password, pushToken); From 670f538a555893aa0c79025a978bda97f8abd4e4 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sun, 11 Dec 2022 15:59:38 -0300 Subject: [PATCH 063/127] correct feedback on room creation length --- routers/roomRouter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 95a4c02..1866e0f 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -171,15 +171,15 @@ const AttachCreateRoomTransaction = async (req, res, next) => { //ERROR CHECKS if (!title) throw new Error('Please provide a title'); - if (title.length <= 3) throw new Error('Title must be at least 3 chars long'); + if (title.length < 3) throw new Error('Title must be at least 3 chars long'); if (title.length > 50) throw new Error('Title can me maximum 50 characters long'); if (!description) throw new Error('Please provide a description'); - if (description.length <= 3) throw new Error('Description must be at least 3 chars long'); + if (description.length < 3) throw new Error('Description must be at least 3 chars long'); if (description.length > 200) throw new Error('Description can be at max 200 characters'); if (!scenario) throw new Error('Please provide a starting scenario'); - if (scenario.length <= 19) throw new Error('Starting scenario must be at least 20 characters'); + if (scenario.length < 20) throw new Error('Starting scenario must be at least 20 characters'); if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); ValidateChars(title); From 2bf856d08b9288333a2d09b72636710aab7fdbb7 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 12 Dec 2022 11:44:29 -0300 Subject: [PATCH 064/127] added test job to schedule in heroky --- notifications/notifications.js | 13 +- package-lock.json | 837 ++++++++++++++------------------- package.json | 1 + scheduling/testJob.js | 3 + 4 files changed, 363 insertions(+), 491 deletions(-) create mode 100644 scheduling/testJob.js diff --git a/notifications/notifications.js b/notifications/notifications.js index 51690c9..03dcaef 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -72,4 +72,15 @@ const SendKickNotification = async (pushToken, storyTitle) => { } -module.exports = { SendTurnNotification, SendStrikeNotification, SendKickNotification }; \ No newline at end of file +const SendTestNotification = () => { + + SendNotification( + 'ExponentPushToken[ZeNN1xHXaxNE3Nl0NBQxMT]', + 'Scheduled notification', + 'Hello smoggy! you should receive this notification when heroku runs its script :)', + {} + ) + +} + +module.exports = { SendTurnNotification, SendStrikeNotification, SendKickNotification, SendTestNotification }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 50d9396..f12fb63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", + "node-cron": "^3.0.2", "node-fetch": "^2.6.1", "node-schedule": "^2.1.0", "passport": "^0.6.0", @@ -63,12 +64,12 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -188,43 +189,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/body-parser/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -236,41 +200,6 @@ "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/body-parser/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -340,16 +269,35 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -359,9 +307,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -412,17 +360,21 @@ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-libc": { "version": "2.0.1", @@ -461,7 +413,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -491,7 +443,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -507,37 +459,38 @@ } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -546,69 +499,68 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, - "node_modules/express/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } @@ -616,7 +568,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -732,18 +684,18 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/https-proxy-agent": { @@ -800,9 +752,9 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -994,19 +946,19 @@ } }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -1072,23 +1024,15 @@ "node": ">= 0.8.0" } }, - "node_modules/morgan/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -1098,6 +1042,17 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" }, + "node_modules/node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -1400,11 +1355,11 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { @@ -1412,11 +1367,17 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { @@ -1441,55 +1402,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/raw-body/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -1544,42 +1456,53 @@ } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -1591,9 +1514,9 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/side-channel": { "version": "1.0.4", @@ -1627,11 +1550,11 @@ } }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/string_decoder": { @@ -1702,9 +1625,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } @@ -1747,6 +1670,14 @@ "node": ">= 0.4.0" } }, + "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/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1829,12 +1760,12 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "agent-base": { @@ -1925,33 +1856,6 @@ "unpipe": "1.0.0" }, "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1959,29 +1863,6 @@ "requires": { "ee-first": "1.1.1" } - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" } } }, @@ -2039,11 +1920,18 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "content-type": { @@ -2052,9 +1940,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", @@ -2093,14 +1981,14 @@ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detect-libc": { "version": "2.0.1", @@ -2133,7 +2021,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "err-code": { "version": "2.0.3", @@ -2157,7 +2045,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "expo-server-sdk": { "version": "3.7.0", @@ -2170,100 +2058,91 @@ } }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "ee-first": "1.1.1" } }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "dependencies": { + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + } } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-minipass": { "version": "2.1.0", @@ -2349,15 +2228,15 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "https-proxy-agent": { @@ -2402,9 +2281,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ipaddr.js": { "version": "1.9.1", @@ -2557,16 +2436,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" } }, "minimatch": { @@ -2609,13 +2488,6 @@ "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.0.2" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } } }, "ms": { @@ -2624,15 +2496,23 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "node-addon-api": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" }, + "node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "requires": { + "uuid": "8.3.2" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -2847,18 +2727,21 @@ } }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, "range-parser": { "version": "1.2.1", @@ -2874,45 +2757,6 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - } } }, "readable-stream": { @@ -2954,41 +2798,49 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } } } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "set-blocking": { @@ -2997,9 +2849,9 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "side-channel": { "version": "1.0.4", @@ -3027,9 +2879,9 @@ "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "string_decoder": { "version": "1.3.0", @@ -3078,9 +2930,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tr46": { "version": "0.0.3", @@ -3111,6 +2963,11 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 8e76789..3a1116a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", + "node-cron": "^3.0.2", "node-fetch": "^2.6.1", "node-schedule": "^2.1.0", "passport": "^0.6.0", diff --git a/scheduling/testJob.js b/scheduling/testJob.js new file mode 100644 index 0000000..9cc8a8f --- /dev/null +++ b/scheduling/testJob.js @@ -0,0 +1,3 @@ +const { SendTestNotification } = require('../notifications/notifications'); +SendTestNotification(); +console.log('test job executed!!!'); \ No newline at end of file From 64e2aadd6fe677f5418ee256f6493ae846c456bc Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 12 Dec 2022 15:55:11 -0300 Subject: [PATCH 065/127] created auto room deadline clean function --- database/dbFunctions.js | 71 ++++++++++++++---------------------- scheduling/checkDeadlines.js | 32 ++++++++++++++++ 2 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 scheduling/checkDeadlines.js diff --git a/database/dbFunctions.js b/database/dbFunctions.js index f33aca5..c7aaaa0 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -46,7 +46,6 @@ async function GetRoomInfo(roomId) { return roomQuery.rows[0]; } - async function GetPlayersInRoom(roomId) { const query = await db.query( `SELECT users.id, users.name, rooms_users.char_count, active, strikes @@ -59,7 +58,6 @@ async function GetPlayersInRoom(roomId) { return query.rows; } - const GetScenariosInRoom = async (roomId) => { const scenarioQuery = await db.query( @@ -73,7 +71,6 @@ const GetScenariosInRoom = async (roomId) => { return scenarioQuery.rows; } - function GetNextPlayerId(players, currentPlayerId) { let i = 0; players.forEach((player, j) => { @@ -84,7 +81,6 @@ function GetNextPlayerId(players, currentPlayerId) { const nextPlayerId = players[i].id; return nextPlayerId; } - async function GetLoggedUserInfo(id) { const query = await db.query('SELECT * FROM users WHERE id=' + id); //change to logged user when session is implemented @@ -94,13 +90,12 @@ async function GetLoggedUserInfo(id) { return query.rows[0]; } - async function GetPushToken(userId) { const query = await db.query('SELECT expo_push_token FROM users WHERE id = $1', [userId]); if (query.rowCount != 0) return query.rows[0].expo_push_token; else return null; } -async function GetUserChars(roomId, userId){ +async function GetUserChars(roomId, userId) { const query = await db.query( `SELECT char_count FROM rooms_users @@ -119,7 +114,6 @@ function MakeSurePlayerHasEnoughChars(players, scenario, userId) { }; }); } - function MakeSurePlayerIsActive(players, userId) { let playerFound = false; players.forEach(player => { @@ -131,7 +125,6 @@ function MakeSurePlayerIsActive(players, userId) { if (!playerFound) throw new Error('player is not in this room'); } - async function MakeSureItsNotTheLastTurn(roomId) { const scenariosQuery = await db.query( `SELECT * @@ -142,12 +135,10 @@ async function MakeSureItsNotTheLastTurn(roomId) { if (scenariosQuery.rowCount >= 39) throw Error('Scenario limit reached! Must create ending'); } - function MakeSureItsNotFinished(room) { if (room.finished) throw new Error('the story has already been ended'); } - function MakeSureItsPlayersTurn(room, userId) { if (!room.next_player_id) { @@ -159,7 +150,6 @@ function MakeSureItsPlayersTurn(room, userId) { } } - function MakeSureRoomExists(roomQuery) { if (roomQuery.rowCount == 0) throw new Error('No room found with the given id'); @@ -173,14 +163,12 @@ async function AddScenario(scenario, roomId, userId) { ); return scenarioQuery.rows[0].id; } - async function UpdateCharCount(scenario, roomId, userId) { await db.query( 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', [scenario.length, userId, roomId] ); } - async function GiveKeyToEachPlayer(roomId) { await db.query( ` @@ -195,14 +183,12 @@ async function GiveKeyToEachPlayer(roomId) { [roomId] ); } - async function SetNextPlayerInRoom(roomId, userId) { await db.query( 'UPDATE rooms SET next_player_id=$1 WHERE id=$2', [userId, roomId] ); } - async function UpdateRoomFullStatus(roomId) { await db.query( `UPDATE rooms @@ -211,28 +197,24 @@ async function UpdateRoomFullStatus(roomId) { [roomId] ); } - async function SetDeadlineIn2Days(roomId) { await db.query( `UPDATE rooms SET turn_end=(NOW() + interval '2 day') WHERE id=$1`, [roomId] ); } - async function AddUserToRoom(roomId, userId) { await db.query( 'INSERT INTO rooms_users (room_id, user_id) VALUES ($1, $2)', [roomId, userId] ); } - async function CreateNewRoom(title, description, scenario, creator_id) { const roomId = await AddRoom(title, description, creator_id); await AddUserToRoom(roomId, creator_id); await AddScenario(scenario, roomId, creator_id) return roomId; } - async function AddRoom(title, description, creator_id) { const query = await db.query( 'INSERT INTO rooms(title, description, creator_id) VALUES($1, $2, $3) RETURNING *', @@ -240,21 +222,18 @@ async function AddRoom(title, description, creator_id) { ); return query.rows[0].id; } - async function AddUserToRoom(roomId, user_id) { await db.query( 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', [roomId, user_id] ); } - async function RemoveKeyFromLoggedUser(userId) { await db.query( 'UPDATE users SET room_keys = room_keys-1 WHERE id = $1', [userId] ); } - async function AddStrike(roomId, userId) { const query = await db.query( @@ -268,7 +247,6 @@ async function AddStrike(roomId, userId) { return query.rows[0].strikes; } - async function DeactivatePlayer(roomId, userId) { await db.query(` @@ -278,7 +256,6 @@ async function DeactivatePlayer(roomId, userId) { `, [roomId, userId]); } - async function SetRoomSearching(roomId) { await db.query(` @@ -288,7 +265,6 @@ async function SetRoomSearching(roomId) { `, [roomId]); } - async function Add2DaysToDeadline(room) { let newTurnEnd; @@ -302,7 +278,6 @@ async function Add2DaysToDeadline(room) { `, [room.id, newTurnEnd]); } - async function SetRoomFull(room, players, scenarios) { if (room.full && room.next_player_id && room.turn_end) return; @@ -326,7 +301,6 @@ async function SetRoomFull(room, players, scenarios) { if (mustUpdateTurnEnd) await Add2DaysToDeadline(room); } - async function CorrectRoomSearching(room, players, scenarios) { const activePlayers = players.filter(player => player.active); @@ -379,7 +353,6 @@ async function CorrectRoomSearching(room, players, scenarios) { } } - async function CheckRoomDeadline(room) { //Checking if the room deadline has been reached, and passing turn if it has @@ -387,28 +360,40 @@ async function CheckRoomDeadline(room) { const { turn_end, title, next_player_id: currentPlayerId } = room; + const now = new Date(); + const offset = now.getTimezoneOffset(); + const nowUTC = new Date(now + (offset * 60 * 1000)); + if (!turn_end) return false; if (!currentPlayerId) return false; - if (turn_end > new Date()) return false; + if (turn_end < nowUTC) return false; + + await HandleDeadlinePassed(room); + + return true; + +} +async function HandleDeadlinePassed(room) { - const strikes = await AddStrike(room.id, currentPlayerId); - const currentPlayer = await GetLoggedUserInfo(currentPlayerId); + const playerThatMissedID = room.next_player_id; + + const strikes = await AddStrike(room.id, playerThatMissedID); + const currentPlayer = await GetLoggedUserInfo(playerThatMissedID); const pushToken = currentPlayer.expo_push_token; if (strikes >= 3) { - await DeactivatePlayer(room.id, currentPlayerId); - if (!roomSearching) await SetRoomSearching(room); - SendKickNotification(pushToken, title); + await DeactivatePlayer(room.id, playerThatMissedID); + if (!roomSearching) + await SetRoomSearching(room); + SendKickNotification(pushToken, room.title); + console.log('kicked player from room with id ', room.id, ', set room searching, and sent a kick notification'); } else { - await PassTurn(room, currentPlayerId); - SendStrikeNotification(pushToken, title, strikes, room.id, currentPlayerId); + await PassTurn(room, playerThatMissedID); + SendStrikeNotification(pushToken, room.title, strikes, room.id, playerThatMissedID); + console.log('Passed the turn in room with id ', room.id, ', and sent a strike notification'); } - - return true; - } - async function CheckRoomInfo(room, players, scenarios) { //this function is a bit bloated. Wierd that it is separated in 2. Should probably just check everything here. maybe... @@ -424,7 +409,6 @@ async function CheckRoomInfo(room, players, scenarios) { return turnPassed; } - async function PassTurn(room, currentPlayerId) { if (room.full) { @@ -453,7 +437,6 @@ async function PassTurn(room, currentPlayerId) { } - async function EndStory(roomId) { GiveKeyToEachPlayer(roomId); db.query( @@ -528,5 +511,7 @@ module.exports = { MakeSurePlayerIsActive, PassTurn, EndStory, - GetUserChars + GetUserChars, + HandleDeadlinePassed, + CheckRoomDeadline }; \ No newline at end of file diff --git a/scheduling/checkDeadlines.js b/scheduling/checkDeadlines.js new file mode 100644 index 0000000..3bf7e6e --- /dev/null +++ b/scheduling/checkDeadlines.js @@ -0,0 +1,32 @@ +const db = require('../database/dbConnect'); +const { HandleDeadlinePassed, CheckRoomDeadline } = require('../database/dbFunctions'); + +const checkDeadlines = async () => { + + const query = await db.query( + `SELECT *, EXTRACT(epoch FROM (turn_end - NOW())/3600) AS time_left + FROM rooms + WHERE turn_end - NOW() < Interval '30 min' + AND finished = false + AND next_player_id IS NOT NULL` + ) + + if (query.rowCount < 1) return; + + const now = new Date(); + + query.rows.forEach(room => { + + if (room.time_left < 0) { + HandleDeadlinePassed(room) + } + else { + const delay = room.time_left * 60 * 60 * 1000; + setTimeout(CheckRoomDeadline, delay, room); + } + }) + + +} + +checkDeadlines(); \ No newline at end of file From d70d893914fb7b765e8e7c613f44aef27b26cc47 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 12 Dec 2022 16:11:04 -0300 Subject: [PATCH 066/127] bugfix to deadline schedule function --- database/dbFunctions.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index c7aaaa0..ed6fb20 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -383,8 +383,7 @@ async function HandleDeadlinePassed(room) { if (strikes >= 3) { await DeactivatePlayer(room.id, playerThatMissedID); - if (!roomSearching) - await SetRoomSearching(room); + if (!roomSearching) await SetRoomSearching(room); SendKickNotification(pushToken, room.title); console.log('kicked player from room with id ', room.id, ', set room searching, and sent a kick notification'); } @@ -415,27 +414,15 @@ async function PassTurn(room, currentPlayerId) { const players = await GetPlayersInRoom(room.id); const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); await SetNextPlayerInRoom(room.id, nextPlayerId) - - const deadlineMet = room.turn_end > new Date(); - if (deadlineMet) await SetDeadlineIn2Days(room.id); - else await Add2DaysToDeadline(room); - - //notify the next player + await SetDeadlineIn2Days(room.id); const nextPlayer = await GetLoggedUserInfo(nextPlayerId); SendTurnNotification(nextPlayer.expo_push_token, room.id, room.title, nextPlayerId); + console.log('turn passed :)'); } else { - await db.query(` - UPDATE rooms - SET - turn_end = null, - next_player_id = null - WHERE id = $1 - `, [room.id] - ); + await SetRoomSearching(room.id); } - } async function EndStory(roomId) { GiveKeyToEachPlayer(roomId); From 4335b8cce829cad58c069760ebb8dd0005a00269 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 12 Dec 2022 16:37:55 -0300 Subject: [PATCH 067/127] input validation added dots --- middleware/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/validation.js b/middleware/validation.js index 9844452..5d27575 100644 --- a/middleware/validation.js +++ b/middleware/validation.js @@ -8,7 +8,7 @@ function ValidateChars(text){ } function CharAllowed(char) { - const re = /[ A-Z a-z 0-9 , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]/; + const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]/; const allowed = re.test(char); return allowed; } From c91199337e82bcb7cce905d7c0f5985838d9de95 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 12 Dec 2022 18:34:03 -0300 Subject: [PATCH 068/127] start on password reset flow --- database/dbFunctions.js | 28 +++++++++++++++++++++- helpers/generateString.js | 11 +++++++++ nodemailertest.js | 38 +++++++++++++++++++++++++++++ package-lock.json | 14 +++++++++++ package.json | 1 + routers/userRouter.js | 50 +++++++++++++++++++++++++++++++++------ 6 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 helpers/generateString.js create mode 100644 nodemailertest.js diff --git a/database/dbFunctions.js b/database/dbFunctions.js index ed6fb20..bad4e4d 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -154,6 +154,17 @@ function MakeSureRoomExists(roomQuery) { if (roomQuery.rowCount == 0) throw new Error('No room found with the given id'); } +async function EmailExists(email) { + + const query = await db.query(` + SELECT * + FROM users + WHERE email = $1 + `, [email]); + + return (query.rowCount > 0); + +} //SETTERS async function AddScenario(scenario, roomId, userId) { @@ -436,6 +447,19 @@ async function EndStory(roomId) { [roomId] ); } +async function AddPasswordResetCode(code, userEmail) { + + await db.query(` + UPDATE users + SET + password_reset_code = $1, + password_reset_timeout = NOW() + Interval '30 min' + WHERE email = $2; + `, [code, userEmail]); + + return; + +} //TRANSACTIONS async function BeginTransaction() { @@ -500,5 +524,7 @@ module.exports = { EndStory, GetUserChars, HandleDeadlinePassed, - CheckRoomDeadline + CheckRoomDeadline, + EmailExists, + AddPasswordResetCode }; \ No newline at end of file diff --git a/helpers/generateString.js b/helpers/generateString.js new file mode 100644 index 0000000..887ca9c --- /dev/null +++ b/helpers/generateString.js @@ -0,0 +1,11 @@ +function makeid(length) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + +module.exports = {makeid}; \ No newline at end of file diff --git a/nodemailertest.js b/nodemailertest.js new file mode 100644 index 0000000..19f198f --- /dev/null +++ b/nodemailertest.js @@ -0,0 +1,38 @@ +"use strict"; +const nodemailer = require("nodemailer"); + +// async..await is not allowed in global scope, must use a wrapper +async function main() { + // Generate test SMTP service account from ethereal.email + // Only needed if you don't have a real mail account for testing + let testAccount = await nodemailer.createTestAccount(); + + // create reusable transporter object using the default SMTP transport + let transporter = nodemailer.createTransport({ + host: "smtp.ethereal.email", + port: 587, + secure: false, // true for 465, false for other ports + auth: { + user: testAccount.user, // generated ethereal user + pass: testAccount.pass, // generated ethereal password + }, + }); + + // send mail with defined transport object + let info = await transporter.sendMail({ + from: '"Fred Foo 👻" ', // sender address + to: "douglasdriving@gmail.com", // list of receivers + subject: "Hello ✔", // Subject line + text: "Hello world?", // plain text body + html: "Hello world?", // html body + }); + + console.log("Message sent: %s", info.messageId); + // Message sent: + + // Preview only available when sending through an Ethereal account + console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info)); + // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou... +} + +main().catch(console.error); diff --git a/package-lock.json b/package-lock.json index f12fb63..f55ebfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "node-cron": "^3.0.2", "node-fetch": "^2.6.1", "node-schedule": "^2.1.0", + "nodemailer": "^6.8.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.8.0" @@ -1085,6 +1086,14 @@ "node": ">=6" } }, + "node_modules/nodemailer": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", + "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -2531,6 +2540,11 @@ "sorted-array-functions": "^1.3.0" } }, + "nodemailer": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", + "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==" + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", diff --git a/package.json b/package.json index 3a1116a..eb260b9 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "node-cron": "^3.0.2", "node-fetch": "^2.6.1", "node-schedule": "^2.1.0", + "nodemailer": "^6.8.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.8.0" diff --git a/routers/userRouter.js b/routers/userRouter.js index 5d1b85c..d2a5e38 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,15 +1,16 @@ const express = require('express'); const userRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); -const {isAuth, Login} = require('../middleware/authentication'); +const { isAuth, Login } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); +const { makeid } = require('../helpers/generateString'); const AddNewUser = async (req, res, next) => { try { - const {name, email, password, pushToken} = req.query; + const { name, email, password, pushToken } = req.query; - const exists = text =>{ + const exists = text => { return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); } @@ -19,7 +20,7 @@ const AddNewUser = async (req, res, next) => { if (!exists(name)) throw new Error('No name provided') if (name.length < 4) throw new Error('Name must be at least 4 characters') if (name.length > 20) throw new Error('Name must be max 20 characters') - + ValidateChars(email); ValidateChars(password); ValidateChars(name); @@ -31,12 +32,11 @@ const AddNewUser = async (req, res, next) => { } catch (error) { - res.status(400).send({ok: false, message: error.message}); + res.status(400).send({ ok: false, message: error.message }); } } - const GetUserInfo = async (req, res, next) => { try { @@ -53,10 +53,46 @@ const GetUserInfo = async (req, res, next) => { res.status(400).send('Unable to get user. ' + error.message); } +} +const RequestPasswordReset = async (req, res, next) => { + + try { + const { email } = req.query; + if (!exists(email)) throw new Error('No email provided'); + + //kolla att användare med email finns + const userExists = await dbFunctions.EmailExists(email); + if (!userExists) throw new Error('No user with that email registered'); + + //generera en kod + const resetCode = makeid(8); + + //++ spara koden i backend, tillsammans med en timeout stamp + dbFunctions.AddPasswordResetCode(resetCode, email); + + //++ skicka koden i ett email + //detta kräver ju uppenbarligen lite mer arbete. Du måste sätta dig in i hur du kan skicka email online + //kanske ska man köra på en service såsom mailersend + + //skicka ett "ok" status + res.status(200).send({ ok: true, message: 'email with reset code sent' }); + } + catch (error) { + + res.status(400).send({ ok: false, message: error.message }); + + } + } userRouter.get('/', isAuth, GetUserInfo); userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); +userRouter.post('/requestPasswordReset', RequestPasswordReset); + +module.exports = userRouter; -module.exports = userRouter; \ No newline at end of file +//HELPERS +const exists = text => { + return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); +} \ No newline at end of file From 9f8b5b0b0c1c914d2acab17ea64ca736a5de54c9 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 11:15:16 -0300 Subject: [PATCH 069/127] revert changes for email var --- nodemailertest.js | 38 -------------------------------- routers/userRouter.js | 50 ++++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 65 deletions(-) delete mode 100644 nodemailertest.js diff --git a/nodemailertest.js b/nodemailertest.js deleted file mode 100644 index 19f198f..0000000 --- a/nodemailertest.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -const nodemailer = require("nodemailer"); - -// async..await is not allowed in global scope, must use a wrapper -async function main() { - // Generate test SMTP service account from ethereal.email - // Only needed if you don't have a real mail account for testing - let testAccount = await nodemailer.createTestAccount(); - - // create reusable transporter object using the default SMTP transport - let transporter = nodemailer.createTransport({ - host: "smtp.ethereal.email", - port: 587, - secure: false, // true for 465, false for other ports - auth: { - user: testAccount.user, // generated ethereal user - pass: testAccount.pass, // generated ethereal password - }, - }); - - // send mail with defined transport object - let info = await transporter.sendMail({ - from: '"Fred Foo 👻" ', // sender address - to: "douglasdriving@gmail.com", // list of receivers - subject: "Hello ✔", // Subject line - text: "Hello world?", // plain text body - html: "Hello world?", // html body - }); - - console.log("Message sent: %s", info.messageId); - // Message sent: - - // Preview only available when sending through an Ethereal account - console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info)); - // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou... -} - -main().catch(console.error); diff --git a/routers/userRouter.js b/routers/userRouter.js index d2a5e38..8476dc7 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -3,17 +3,13 @@ const userRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const { isAuth, Login } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); -const { makeid } = require('../helpers/generateString'); +// const { makeid } = require('../helpers/generateString'); const AddNewUser = async (req, res, next) => { try { const { name, email, password, pushToken } = req.query; - const exists = text => { - return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); - } - if (!exists(email)) throw new Error('No email provided') if (!exists(password)) throw new Error('No password provided') if (password.length < 6) throw new Error('Password must be at least 6 characters') @@ -54,41 +50,41 @@ const GetUserInfo = async (req, res, next) => { } } -const RequestPasswordReset = async (req, res, next) => { +// const RequestPasswordReset = async (req, res, next) => { - try { - const { email } = req.query; - if (!exists(email)) throw new Error('No email provided'); +// try { +// const { email } = req.query; +// if (!exists(email)) throw new Error('No email provided'); - //kolla att användare med email finns - const userExists = await dbFunctions.EmailExists(email); - if (!userExists) throw new Error('No user with that email registered'); +// //kolla att användare med email finns +// const userExists = await dbFunctions.EmailExists(email); +// if (!userExists) throw new Error('No user with that email registered'); - //generera en kod - const resetCode = makeid(8); +// //generera en kod +// const resetCode = makeid(8); - //++ spara koden i backend, tillsammans med en timeout stamp - dbFunctions.AddPasswordResetCode(resetCode, email); +// //++ spara koden i backend, tillsammans med en timeout stamp +// dbFunctions.AddPasswordResetCode(resetCode, email); - //++ skicka koden i ett email - //detta kräver ju uppenbarligen lite mer arbete. Du måste sätta dig in i hur du kan skicka email online - //kanske ska man köra på en service såsom mailersend +// //++ skicka koden i ett email +// //detta kräver ju uppenbarligen lite mer arbete. Du måste sätta dig in i hur du kan skicka email online +// //kanske ska man köra på en service såsom mailersend - //skicka ett "ok" status - res.status(200).send({ ok: true, message: 'email with reset code sent' }); - } - catch (error) { +// //skicka ett "ok" status +// res.status(200).send({ ok: true, message: 'email with reset code sent' }); +// } +// catch (error) { - res.status(400).send({ ok: false, message: error.message }); +// res.status(400).send({ ok: false, message: error.message }); - } +// } -} +// } userRouter.get('/', isAuth, GetUserInfo); userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); -userRouter.post('/requestPasswordReset', RequestPasswordReset); +// userRouter.post('/requestPasswordReset', RequestPasswordReset); module.exports = userRouter; From 3b600501eff114691dca3e2e5e298eb309f3926b Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 12:14:22 -0300 Subject: [PATCH 070/127] Kick new players if they dont write in 30 --- database/dbFunctions.js | 70 ++++++++++++++++++++++++------------ routers/roomRouter.js | 2 +- routers/scenarioRouter.js | 10 ++++-- scheduling/checkDeadlines.js | 5 ++- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index bad4e4d..236896d 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -21,7 +21,6 @@ async function CreateUser(name, email, password, pushToken) { ); } - async function Login(email, password) { const query = await db.query( @@ -164,6 +163,17 @@ async function EmailExists(email) { return (query.rowCount > 0); +} +async function PlayerHasWrittenInRoom(roomID, userID) { + + const query = await db.query(` + SELECT * + FROM scenarios + WHERE room_id = $1 AND user_id = $2 + `, [roomID, userID]); + + return (query.rowCount > 0); + } //SETTERS @@ -214,6 +224,12 @@ async function SetDeadlineIn2Days(roomId) { [roomId] ); } +async function SetDeadlineIn30Min(roomId) { + await db.query( + `UPDATE rooms SET turn_end=(NOW() + interval '30 min') WHERE id=$1`, + [roomId] + ); +} async function AddUserToRoom(roomId, userId) { await db.query( 'INSERT INTO rooms_users (room_id, user_id) VALUES ($1, $2)', @@ -388,21 +404,33 @@ async function HandleDeadlinePassed(room) { const playerThatMissedID = room.next_player_id; + //kick if new player + const newPlayer = !(await PlayerHasWrittenInRoom(room.id, playerThatMissedID)); + if (newPlayer) { + await DeactivatePlayer(room.id, playerThatMissedID); + await SetRoomSearching(room.id); + return; + } + + //add strike const strikes = await AddStrike(room.id, playerThatMissedID); + + //get player info const currentPlayer = await GetLoggedUserInfo(playerThatMissedID); const pushToken = currentPlayer.expo_push_token; + //kick if 3 strikes if (strikes >= 3) { await DeactivatePlayer(room.id, playerThatMissedID); - if (!roomSearching) await SetRoomSearching(room); + if (!roomSearching) await SetRoomSearching(room.id); SendKickNotification(pushToken, room.title); - console.log('kicked player from room with id ', room.id, ', set room searching, and sent a kick notification'); - } - else { - await PassTurn(room, playerThatMissedID); - SendStrikeNotification(pushToken, room.title, strikes, room.id, playerThatMissedID); - console.log('Passed the turn in room with id ', room.id, ', and sent a strike notification'); + return; } + + //pass the turn + await PassTurn(room, playerThatMissedID); + SendStrikeNotification(pushToken, room.title, strikes, room.id, playerThatMissedID); + } async function CheckRoomInfo(room, players, scenarios) { @@ -421,19 +449,19 @@ async function CheckRoomInfo(room, players, scenarios) { } async function PassTurn(room, currentPlayerId) { - if (room.full) { - const players = await GetPlayersInRoom(room.id); - const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); - await SetNextPlayerInRoom(room.id, nextPlayerId) - await SetDeadlineIn2Days(room.id); - const nextPlayer = await GetLoggedUserInfo(nextPlayerId); - SendTurnNotification(nextPlayer.expo_push_token, room.id, room.title, nextPlayerId); - console.log('turn passed :)'); - } - else { - await SetRoomSearching(room.id); + if (!room.full) { + SetRoomSearching(room.id); + return; } + const players = await GetPlayersInRoom(room.id); + const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); + await SetNextPlayerInRoom(room.id, nextPlayerId) + await SetDeadlineIn2Days(room.id); + const nextPlayer = await GetLoggedUserInfo(nextPlayerId); + SendTurnNotification(nextPlayer.expo_push_token, room.id, room.title, nextPlayerId); + console.log('turn passed :)'); + } async function EndStory(roomId) { GiveKeyToEachPlayer(roomId); @@ -465,15 +493,12 @@ async function AddPasswordResetCode(code, userEmail) { async function BeginTransaction() { await db.query('BEGIN'); } - function Rollback() { db.query('ROLLBACK'); } - async function Commit() { await db.query('COMMIT'); } - async function TryTransaction(req, res, next) { try { @@ -510,6 +535,7 @@ module.exports = { RemoveKeyFromLoggedUser, CreateNewRoom, SetDeadlineIn2Days, + SetDeadlineIn30Min, UpdateRoomFullStatus, SetNextPlayerInRoom, AddUserToRoom, diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 1866e0f..e9f8d9d 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -209,7 +209,7 @@ const AttachJoinRoomTransaction = async (req, res, next) => { //update await dbFunctions.AddUserToRoom(roomId, req.userId); await dbFunctions.UpdateRoomFullStatus(roomId); - await dbFunctions.SetDeadlineIn2Days(roomId); + await dbFunctions.SetDeadlineIn30Min(roomId); await dbFunctions.SetNextPlayerInRoom(roomId, req.userId); //response diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 5e011c2..6871a12 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -22,10 +22,14 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //make queries let room = await dbFunctions.GetRoomInfo(roomId); const players = await dbFunctions.GetPlayersInRoom(roomId); - const scenarios = await dbFunctions.GetScenariosInRoom(roomId) - const turnCorrectionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); + const scenarios = await dbFunctions.GetScenariosInRoom(roomId); - if (turnCorrectionsMade) room = await dbFunctions.GetRoomInfo(roomId); + const turnCorrectionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); + if (turnCorrectionsMade) { + room = await dbFunctions.GetRoomInfo(roomId); + players = await dbFunctions.GetPlayersInRoom(roomId); + scenarios = await dbFunctions.GetScenariosInRoom(roomId); + } //some db checks dbFunctions.MakeSurePlayerIsActive(players, userId); diff --git a/scheduling/checkDeadlines.js b/scheduling/checkDeadlines.js index 3bf7e6e..666e86c 100644 --- a/scheduling/checkDeadlines.js +++ b/scheduling/checkDeadlines.js @@ -13,17 +13,16 @@ const checkDeadlines = async () => { if (query.rowCount < 1) return; - const now = new Date(); - query.rows.forEach(room => { if (room.time_left < 0) { - HandleDeadlinePassed(room) + HandleDeadlinePassed(room); } else { const delay = room.time_left * 60 * 60 * 1000; setTimeout(CheckRoomDeadline, delay, room); } + }) From 6deb593ea5289697ad1f517efac0c28a7c5c481f Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 12:56:52 -0300 Subject: [PATCH 071/127] user request bugs, might fix? --- database/dbFunctions.js | 8 ++++---- middleware/authentication.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 236896d..8b122bf 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -108,7 +108,7 @@ async function GetUserChars(roomId, userId) { //CHECKS function MakeSurePlayerHasEnoughChars(players, scenario, userId) { players.forEach(player => { - if (player.user_id == userId && player.char_count < scenario.length) { + if (player.id == userId && player.char_count < scenario.length) { throw new Error('player does not have enough characters left'); }; }); @@ -169,7 +169,7 @@ async function PlayerHasWrittenInRoom(roomID, userID) { const query = await db.query(` SELECT * FROM scenarios - WHERE room_id = $1 AND user_id = $2 + WHERE room_id = $1 AND creator_id = $2 `, [roomID, userID]); return (query.rowCount > 0); @@ -249,10 +249,10 @@ async function AddRoom(title, description, creator_id) { ); return query.rows[0].id; } -async function AddUserToRoom(roomId, user_id) { +async function AddUserToRoom(roomID, userID) { await db.query( 'INSERT INTO rooms_users(room_id, user_id) VALUES($1, $2)', - [roomId, user_id] + [roomID, userID] ); } async function RemoveKeyFromLoggedUser(userId) { diff --git a/middleware/authentication.js b/middleware/authentication.js index 345769d..28018a1 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -16,7 +16,7 @@ const isAuth = async (req, res, next) => { res.status(403).send('invalid auth token'); } else { - req.userId = data.userId; + req.userId = data.id; console.log('attaching user id: ', req.userId); next(); } @@ -38,9 +38,9 @@ const Login = async (req, res, next) => { const user = await dbFunctions.Login(email, password); const token = jwt.sign( - {userId: user.id}, + { userId: user.id }, process.env.JWT_SECRET, - {expiresIn: process.env.JWT_EXPIRES_IN} + { expiresIn: process.env.JWT_EXPIRES_IN } ); res.status(201).send({ message: 'logged in as ' + user.name, token: token }); From fe291d8915d636421f3b6234be56d02844ba8b46 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 13:20:56 -0300 Subject: [PATCH 072/127] possible fix to user fetch backend --- middleware/authentication.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/authentication.js b/middleware/authentication.js index 28018a1..a464a6f 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -16,7 +16,7 @@ const isAuth = async (req, res, next) => { res.status(403).send('invalid auth token'); } else { - req.userId = data.id; + req.userId = data.id || data.user_id || data.userId; console.log('attaching user id: ', req.userId); next(); } From 83647cf4a195d4a59f7bea31d8871d9cd4baeacc Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 13:24:34 -0300 Subject: [PATCH 073/127] quickfix for writing user query correctly --- database/dbFunctions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 8b122bf..c2fb744 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -81,7 +81,9 @@ function GetNextPlayerId(players, currentPlayerId) { return nextPlayerId; } async function GetLoggedUserInfo(id) { - const query = await db.query('SELECT * FROM users WHERE id=' + id); //change to logged user when session is implemented + if (!id) throw new Error('No user to query for user info'); + + const query = await db.query('SELECT * FROM users WHERE id=$1', [id]); if (!query.rows) throw new Error('Query returned nothing'); if (query.rowCount < 1) throw new Error('Found no user with that id'); From b4f2f954571dc5d6f058998054f3141b837cfc8c Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 13:49:03 -0300 Subject: [PATCH 074/127] prevent user from getting rooms that they are kicked from --- routers/roomRouter.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index e9f8d9d..05c3c1d 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -112,6 +112,7 @@ const AttachUserRoomsQuery = async (req, res, next) => { SELECT * FROM rooms_users WHERE room_id = rooms.id AND user_id = $1 + AND active = true ) AND rooms_users.active = true;` ); @@ -140,9 +141,9 @@ const AttachArchiveQuery = async (req, res, next) => { const GetUserChars = async (req, res, next) => { try { - const {userId, roomId} = req.query; - if(!userId) throw new Error('must provide a userId to get the chars'); - if(!roomId) throw new Error('must provide a roomId to get the chars'); + const { userId, roomId } = req.query; + if (!userId) throw new Error('must provide a userId to get the chars'); + if (!roomId) throw new Error('must provide a roomId to get the chars'); const chars = await dbFunctions.GetUserChars(roomId, userId); if (!chars) throw new Error('could not find any chars for that room and user'); res.status(200).send({ From 916aafd290fee7a65336449af1c9d71f241436ee Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 13 Dec 2022 14:17:40 -0300 Subject: [PATCH 075/127] fixed bugg: players are immediately kicked when joining a new room --- database/dbFunctions.js | 42 ++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index c2fb744..d31a7ef 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -332,6 +332,8 @@ async function SetRoomFull(room, players, scenarios) { } async function CorrectRoomSearching(room, players, scenarios) { + console.log('checking if room search needs to be corrected'); + const activePlayers = players.filter(player => player.active); const roomSetToSearching = (!room.full && !room.turn_end && !room.next_player_id); const isNewRoom = (scenarios.length < 4); @@ -343,12 +345,19 @@ async function CorrectRoomSearching(room, players, scenarios) { return false; } else { + console.log('new room, more scenarios than active players, and room is not yey searching - setting searching now!'); await SetRoomSearching(room.id); return true; } } else { if (!room.turn_end && !room.next_player_id) { + console.log(` + New room, + more or equal amount of active players to scenarios, + turn end and next player id is null, + -> setting full to false, turn end in 2 days, and next player id to something + `); await db.query(` UPDATE rooms SET @@ -387,23 +396,32 @@ async function CheckRoomDeadline(room) { //Checking if the room deadline has been reached, and passing turn if it has //returning TRUE if the turn was passed - const { turn_end, title, next_player_id: currentPlayerId } = room; - - const now = new Date(); - const offset = now.getTimezoneOffset(); - const nowUTC = new Date(now + (offset * 60 * 1000)); - - if (!turn_end) return false; - if (!currentPlayerId) return false; - if (turn_end < nowUTC) return false; - + const q = await db.query(` + SELECT (turn_end < NOW()) AS deadlinePassed + FROM rooms + WHERE id = $1; + `, [room.id]); + const { deadlinePassed } = q.rows[0]; + if (!deadlinePassed) console.log('deadline was not passed yet :)'); + if (!deadlinePassed) return false; + + // const { turn_end, title, next_player_id: currentPlayerId } = room; + // const now = new Date(); + // const offset = now.getTimezoneOffset(); + // const nowUTC = new Date(now + (offset * 60 * 1000)); //not sure it works. do in db instead + // if (!turn_end) return false; + // if (!currentPlayerId) return false; + // if (turn_end < nowUTC) return false; + + console.log('deadline was passed!!!!! handling'); await HandleDeadlinePassed(room); - return true; } async function HandleDeadlinePassed(room) { + console.log('deadline passed for room with id: ', room.id); + const playerThatMissedID = room.next_player_id; //kick if new player @@ -436,6 +454,8 @@ async function HandleDeadlinePassed(room) { } async function CheckRoomInfo(room, players, scenarios) { + console.log('checking the room info'); + //this function is a bit bloated. Wierd that it is separated in 2. Should probably just check everything here. maybe... //this function returns TRUE if a correction was made to who is the next player From d175504ab5e7e50d2f9dd53679e9b8ffac887773 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 14 Dec 2022 11:38:58 -0300 Subject: [PATCH 076/127] allow - character --- middleware/validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middleware/validation.js b/middleware/validation.js index 5d27575..6e3c31c 100644 --- a/middleware/validation.js +++ b/middleware/validation.js @@ -8,13 +8,13 @@ function ValidateChars(text){ } function CharAllowed(char) { - const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]/; + const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]/; const allowed = re.test(char); return allowed; } function CharsAllowed(str) { - const re = /^[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * - = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff]+$/; + const re = /^[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]+$/; const allowed = re.test(str); return allowed; } From f1206bf22ae331aa0703d2ec83bbf64371aed704 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 23 Dec 2022 13:52:40 -0300 Subject: [PATCH 077/127] corrected deadline passed function --- database/dbFunctions.js | 51 ++++++++++++++++------------------------- scheduling/testJob.js | 3 --- 2 files changed, 20 insertions(+), 34 deletions(-) delete mode 100644 scheduling/testJob.js diff --git a/database/dbFunctions.js b/database/dbFunctions.js index d31a7ef..59b3949 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -267,7 +267,7 @@ async function AddStrike(roomId, userId) { const query = await db.query( `UPDATE rooms_users - SET strikes = strikes + 1 + SET strikes = LEAST(3, strikes + 1) WHERE room_id = $1 AND user_id = $2 RETURNING strikes`, [roomId, userId] @@ -393,29 +393,17 @@ async function CorrectRoomSearching(room, players, scenarios) { } async function CheckRoomDeadline(room) { - //Checking if the room deadline has been reached, and passing turn if it has - //returning TRUE if the turn was passed - + //checking if deadline has been reached const q = await db.query(` - SELECT (turn_end < NOW()) AS deadlinePassed + SELECT (turn_end < NOW()) AS passed FROM rooms WHERE id = $1; `, [room.id]); - const { deadlinePassed } = q.rows[0]; - if (!deadlinePassed) console.log('deadline was not passed yet :)'); - if (!deadlinePassed) return false; - - // const { turn_end, title, next_player_id: currentPlayerId } = room; - // const now = new Date(); - // const offset = now.getTimezoneOffset(); - // const nowUTC = new Date(now + (offset * 60 * 1000)); //not sure it works. do in db instead - // if (!turn_end) return false; - // if (!currentPlayerId) return false; - // if (turn_end < nowUTC) return false; + const { passed } = q.rows[0]; - console.log('deadline was passed!!!!! handling'); - await HandleDeadlinePassed(room); - return true; + //handling and returning true if it was + if (passed) await HandleDeadlinePassed(room); + return passed; } async function HandleDeadlinePassed(room) { @@ -425,8 +413,9 @@ async function HandleDeadlinePassed(room) { const playerThatMissedID = room.next_player_id; //kick if new player - const newPlayer = !(await PlayerHasWrittenInRoom(room.id, playerThatMissedID)); - if (newPlayer) { + const isNewPlayer = !(await PlayerHasWrittenInRoom(room.id, playerThatMissedID)); + if (isNewPlayer) { + console.log(`Kicking new player ${playerThatMissedID} from room ${room.id}`); await DeactivatePlayer(room.id, playerThatMissedID); await SetRoomSearching(room.id); return; @@ -435,14 +424,14 @@ async function HandleDeadlinePassed(room) { //add strike const strikes = await AddStrike(room.id, playerThatMissedID); - //get player info - const currentPlayer = await GetLoggedUserInfo(playerThatMissedID); - const pushToken = currentPlayer.expo_push_token; + //get push token + const pushToken = await GetPushToken(playerThatMissedID); //kick if 3 strikes if (strikes >= 3) { + console.log(`Kicking player ${playerThatMissedID} from room ${room.id}`); await DeactivatePlayer(room.id, playerThatMissedID); - if (!roomSearching) await SetRoomSearching(room.id); + await SetRoomSearching(room.id); SendKickNotification(pushToken, room.title); return; } @@ -450,23 +439,23 @@ async function HandleDeadlinePassed(room) { //pass the turn await PassTurn(room, playerThatMissedID); SendStrikeNotification(pushToken, room.title, strikes, room.id, playerThatMissedID); - } async function CheckRoomInfo(room, players, scenarios) { console.log('checking the room info'); - //this function is a bit bloated. Wierd that it is separated in 2. Should probably just check everything here. maybe... + //this function is a bit bloated. Wierd that it is separated in 2. + //Should probably just check everything here. maybe... //this function returns TRUE if a correction was made to who is the next player //check to see if the room has space and if so set it searching for new players const roomSearchingCorrected = await CorrectRoomSearching(room, players, scenarios); - if (roomSearchingCorrected) return true; - - //check deadline, if it has been reached we should move to the next player + if (roomSearchingCorrected) { + room = await GetRoomInfo(room.id); + } const turnPassed = await CheckRoomDeadline(room); - return turnPassed; + return (turnPassed || roomSearchingCorrected); } async function PassTurn(room, currentPlayerId) { diff --git a/scheduling/testJob.js b/scheduling/testJob.js deleted file mode 100644 index 9cc8a8f..0000000 --- a/scheduling/testJob.js +++ /dev/null @@ -1,3 +0,0 @@ -const { SendTestNotification } = require('../notifications/notifications'); -SendTestNotification(); -console.log('test job executed!!!'); \ No newline at end of file From beae49fe6016aa1d153347ee37ed9dd9338d4e6c Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 23 Dec 2022 14:24:54 -0300 Subject: [PATCH 078/127] scenario add fix removed "&" from allowed characters, and made fixes to scenario add route --- database/dbFunctions.js | 4 +++- middleware/validation.js | 4 ++-- routers/scenarioRouter.js | 39 +++++++++++++++++++++++++-------------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 59b3949..dc8b289 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -142,8 +142,10 @@ function MakeSureItsNotFinished(room) { } function MakeSureItsPlayersTurn(room, userId) { + console.log('room is: ', room); + if (!room.next_player_id) { - throw new Error(`there is no next player!`); + throw new Error(`It's not the players turn to write!`); } if (!room.next_player_id || room.next_player_id != userId) { diff --git a/middleware/validation.js b/middleware/validation.js index 6e3c31c..5c31767 100644 --- a/middleware/validation.js +++ b/middleware/validation.js @@ -8,13 +8,13 @@ function ValidateChars(text){ } function CharAllowed(char) { - const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]/; + const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]/; const allowed = re.test(char); return allowed; } function CharsAllowed(str) { - const re = /^[ A-Z a-z 0-9 . , ; : ' " ( ) & @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]+$/; + const re = /^[ A-Z a-z 0-9 . , ; : ' " ( ) @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]+$/; const allowed = re.test(str); return allowed; } diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 6871a12..c6b62e4 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -2,11 +2,13 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); const { isAuth } = require('../middleware/authentication'); -const { CharsAllowed, ValidateChars } = require('../middleware/validation'); +const { ValidateChars } = require('../middleware/validation'); -const AttachAddScenarioTransaction = async (req, res, next) => { +const TryAddScenario = async (req, res, next) => { - req.Transaction = async () => { + let transactionInitiated = false; + + try { const userId = req.userId; const { roomId, text } = req.query @@ -21,11 +23,11 @@ const AttachAddScenarioTransaction = async (req, res, next) => { //make queries let room = await dbFunctions.GetRoomInfo(roomId); - const players = await dbFunctions.GetPlayersInRoom(roomId); - const scenarios = await dbFunctions.GetScenariosInRoom(roomId); + let players = await dbFunctions.GetPlayersInRoom(roomId); + let scenarios = await dbFunctions.GetScenariosInRoom(roomId); - const turnCorrectionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); - if (turnCorrectionsMade) { + const correctionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); + if (correctionsMade) { room = await dbFunctions.GetRoomInfo(roomId); players = await dbFunctions.GetPlayersInRoom(roomId); scenarios = await dbFunctions.GetScenariosInRoom(roomId); @@ -38,7 +40,10 @@ const AttachAddScenarioTransaction = async (req, res, next) => { dbFunctions.MakeSureItsNotFinished(room); if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); - //carry out the transaction + //transaction (things in here will be rolled back on error) + await dbFunctions.BeginTransaction(); + transactionInitiated = true; + const scenarioId = await dbFunctions.AddScenario(text, roomId, userId); if (isEnd) { @@ -49,16 +54,22 @@ const AttachAddScenarioTransaction = async (req, res, next) => { await dbFunctions.UpdateCharCount(text, roomId, userId); } - //send response - if (!isEnd) req.responseMessage = 'new scenario added with id: ' + scenarioId - else req.responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + await dbFunctions.Commit(); + //send response + let responseMessage; + if (!isEnd) responseMessage = 'new scenario added with id: ' + scenarioId + else responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + res.send({ ok: true, message: responseMessage }); + } + catch (error) { + if (transactionInitiated) Rollback(); + console.error(error); + res.status(400).send({ ok: false, message: error.message }); } - - next(); } scenarioRouter.use(isAuth); -scenarioRouter.post('/', AttachAddScenarioTransaction, dbFunctions.TryTransaction); +scenarioRouter.post('/', TryAddScenario); module.exports = scenarioRouter; \ No newline at end of file From 2c41d175241b7f5ecccf32587845c53f79098a7f Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 23 Dec 2022 15:01:21 -0300 Subject: [PATCH 079/127] fixed passed turn bug made sure to skip inactive players when setting next player id --- database/dbFunctions.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index dc8b289..f4a85e8 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -57,6 +57,19 @@ async function GetPlayersInRoom(roomId) { return query.rows; } +async function GetActivePlayers(roomId) { + const query = await db.query( + `SELECT users.id, users.name, rooms_users.char_count, active, strikes + FROM rooms_users + JOIN users ON rooms_users.user_id = users.id + WHERE rooms_users.room_id = $1 + AND rooms_users.active = true + ORDER BY rooms_users.id`, + [roomId] + ); + + return query.rows; +} const GetScenariosInRoom = async (roomId) => { const scenarioQuery = await db.query( @@ -70,13 +83,26 @@ const GetScenariosInRoom = async (roomId) => { return scenarioQuery.rows; } -function GetNextPlayerId(players, currentPlayerId) { +async function GetNextPlayerId(roomId, currentPlayerId) { + + //get all the active players in order + const players = await GetPlayersInRoom(roomId) + + //find the index of the player that is after the current one let i = 0; players.forEach((player, j) => { if (player.id != currentPlayerId) return; if (j == (players.length - 1)) return; i = j + 1; }); + + //increment index until we find a player that is still active + while (!players[i].active) { + i++; + if (i >= players.length) i = 0; + } + + //return the id of the player const nextPlayerId = players[i].id; return nextPlayerId; } @@ -467,8 +493,8 @@ async function PassTurn(room, currentPlayerId) { return; } - const players = await GetPlayersInRoom(room.id); - const nextPlayerId = await GetNextPlayerId(players, currentPlayerId); + // const players = await GetPlayersInRoom(room.id); + const nextPlayerId = await GetNextPlayerId(room.id, currentPlayerId); await SetNextPlayerInRoom(room.id, nextPlayerId) await SetDeadlineIn2Days(room.id); const nextPlayer = await GetLoggedUserInfo(nextPlayerId); From a96b2a53ba56b8e9c27968936badcc2bb0bba5ba Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 23 Dec 2022 17:52:43 -0300 Subject: [PATCH 080/127] create ending works --- database/dbFunctions.js | 45 ++++++++++++++++++++++++++++----------- routers/scenarioRouter.js | 15 ++++++++++--- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index f4a85e8..66195f3 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -132,6 +132,20 @@ async function GetUserChars(roomId, userId) { if (query.rowCount != 0) return query.rows[0].char_count; else return null; } +async function GetScenarioCount(roomId) { + + const q = await db.query( + `SELECT COUNT(*) AS count + FROM scenarios + WHERE room_id = $1`, + [roomId] + ); + + const { count } = q.rows[0]; + + return count; + +} //CHECKS function MakeSurePlayerHasEnoughChars(players, scenario, userId) { @@ -153,18 +167,18 @@ function MakeSurePlayerIsActive(players, userId) { if (!playerFound) throw new Error('player is not in this room'); } async function MakeSureItsNotTheLastTurn(roomId) { - const scenariosQuery = await db.query( - `SELECT * - FROM scenarios - WHERE room_id = $1`, - [roomId] - ); - if (scenariosQuery.rowCount >= 39) - throw Error('Scenario limit reached! Must create ending'); + const count = await GetScenarioCount(roomId); + if (count >= 39) throw Error('Scenario limit reached! Must create ending'); } -function MakeSureItsNotFinished(room) { - if (room.finished) - throw new Error('the story has already been ended'); +async function MakeSureItsNotFinished(roomId) { + const q = await db.query(` + SELECT finished + FROM rooms + WHERE id = $1 + `, [roomId]); + + const finished = q.rows[0].finished; + if (finished) throw new Error('Story has already been finished'); } function MakeSureItsPlayersTurn(room, userId) { @@ -204,6 +218,12 @@ async function PlayerHasWrittenInRoom(roomID, userID) { return (query.rowCount > 0); +} +async function CanEnd(roomId) { + + const count = await GetScenarioCount(roomId); + return (count >= 30); //this value should be grabbed from a balancing sheet + } //SETTERS @@ -591,5 +611,6 @@ module.exports = { HandleDeadlinePassed, CheckRoomDeadline, EmailExists, - AddPasswordResetCode + AddPasswordResetCode, + CanEnd }; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index c6b62e4..a048ddc 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -12,7 +12,18 @@ const TryAddScenario = async (req, res, next) => { const userId = req.userId; const { roomId, text } = req.query - const isEnd = (req.query.end == true); + const isEnd = (req.query.end == "true"); + + await dbFunctions.MakeSureItsNotFinished(roomId); + + //checka om end vs scenario - att det verkligen är tillåtet + if (isEnd) { + const canEnd = await dbFunctions.CanEnd(roomId); + if (!canEnd) throw new Error(`Cant end the story yet! Not enough paragraphs written`); + } + else { + await dbFunctions.MakeSureItsNotTheLastTurn(roomId); + } //initial error checks ValidateChars(text); @@ -37,8 +48,6 @@ const TryAddScenario = async (req, res, next) => { dbFunctions.MakeSurePlayerIsActive(players, userId); dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); dbFunctions.MakeSureItsPlayersTurn(room, userId); - dbFunctions.MakeSureItsNotFinished(room); - if (!isEnd) await dbFunctions.MakeSureItsNotTheLastTurn(roomId); //transaction (things in here will be rolled back on error) await dbFunctions.BeginTransaction(); From 27a6473be89195d7d40481081748c9b9c4a99ba2 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 23 Dec 2022 20:27:31 -0300 Subject: [PATCH 081/127] Update scenarioRouter.js --- routers/scenarioRouter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index a048ddc..a6ceef7 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -72,7 +72,7 @@ const TryAddScenario = async (req, res, next) => { res.send({ ok: true, message: responseMessage }); } catch (error) { - if (transactionInitiated) Rollback(); + if (transactionInitiated) dbFunctions.Rollback(); console.error(error); res.status(400).send({ ok: false, message: error.message }); } From caf1db74354f36a8258c7c3f628f7f3ccc549989 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sun, 25 Dec 2022 12:04:00 -0300 Subject: [PATCH 082/127] remove 500 char limit on start scenario --- routers/roomRouter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 05c3c1d..d204051 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -181,7 +181,9 @@ const AttachCreateRoomTransaction = async (req, res, next) => { if (!scenario) throw new Error('Please provide a starting scenario'); if (scenario.length < 20) throw new Error('Starting scenario must be at least 20 characters'); - if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); + + // ++ add this back later. Now it's a problem since there is no feedback in the frontend about it + // if (scenario.length > 500) throw new Error('Starting scenario can be at max 500 characters'); ValidateChars(title); ValidateChars(description); From ddb8a415afa7633629f06155720fd52f4c8d0ab6 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 16 Jan 2023 17:49:07 -0300 Subject: [PATCH 083/127] room deadline request rout --- database/dbFunctions.js | 19 ++++++++++++++++++- routers/roomRouter.js | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 66195f3..b6aeb4b 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -145,6 +145,22 @@ async function GetScenarioCount(roomId) { return count; +} +async function GetDeadline(roomId) { + + console.log('getting deadline for room with id: ', roomId); + + const q = await db.query( + `SELECT turn_end + FROM rooms + WHERE id = $1`, + [roomId] + ); + + const deadline = q.rows[0].turn_end; + + return deadline; + } //CHECKS @@ -612,5 +628,6 @@ module.exports = { CheckRoomDeadline, EmailExists, AddPasswordResetCode, - CanEnd + CanEnd, + GetDeadline }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index d204051..2525223 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -159,6 +159,28 @@ const GetUserChars = async (req, res, next) => { }); } +} +const GetDeadline = async (req, res, next) => { + + try { + const { roomId } = req.query; + if (!roomId) throw new Error('must provide a roomId to get the deadline'); + + const deadline = await dbFunctions.GetDeadline(roomId); + if (!deadline) throw new Error('could not find any deadline for that room'); + res.status(200).send({ + ok: true, + message: 'successfully found deadline', + deadline: deadline + }) + } catch (error) { + console.error(error); + res.status(400).send({ + ok: false, + message: 'Failed to get deadline: ' + error.message + }); + } + } //POST TRANSACTION FUNCTIONS @@ -227,6 +249,7 @@ roomRouter.use(isAuth); roomRouter.get('/data/:id', GetRoomData); roomRouter.get('/user/chars', GetUserChars); +roomRouter.get('/deadline', GetDeadline); roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); From ae1c42c9936f1f8dc65252724582b57edfb53fc3 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 23 Jan 2023 16:22:22 -0300 Subject: [PATCH 084/127] + ongoing rooms query --- routers/roomRouter.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 2525223..1a85bc3 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -137,6 +137,32 @@ const AttachArchiveQuery = async (req, res, next) => { req.roomQueryParams = []; next(); +} +const AttachOngoingRoomsQuery = async (req, res, next) => { + + req.roomQuery = ( + `SELECT + rooms.id, + title, + description, + (SELECT name FROM users WHERE id = rooms.creator_id) AS creator, + (SELECT name FROM users WHERE id = rooms_users.user_id) AS user, + (SELECT COUNT(*) FROM scenarios WHERE room_id = rooms.id) AS scenario_count + FROM rooms + JOIN rooms_users ON rooms_users.room_id = rooms.id + WHERE NOT EXISTS ( + SELECT * FROM rooms_users + WHERE room_id = rooms.id + AND user_id = $1 + AND active = true + ) + AND rooms_users.active = true + AND rooms.finished = false + AND rooms.full = true;` + ); + req.roomQueryParams = [req.userId]; + next(); + } const GetUserChars = async (req, res, next) => { @@ -254,6 +280,7 @@ roomRouter.get('/deadline', GetDeadline); roomRouter.get('/available', AttachAvailableRoomsQuery, RetrieveRooms); roomRouter.get('/user', AttachUserRoomsQuery, RetrieveRooms); roomRouter.get('/archive', AttachArchiveQuery, RetrieveRooms); +roomRouter.get('/ongoing', AttachOngoingRoomsQuery, RetrieveRooms); roomRouter.post('/', AttachCreateRoomTransaction, dbFunctions.TryTransaction); roomRouter.post('/join', AttachJoinRoomTransaction, dbFunctions.TryTransaction); From c2518c717c895e94d01904a7764ec251766e30cf Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 24 Jan 2023 15:11:16 -0300 Subject: [PATCH 085/127] Added Feed Rout --- database/dbFunctions.js | 23 ++++++++++++++++++++++- routers/scenarioRouter.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index b6aeb4b..bd7717a 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -161,6 +161,26 @@ async function GetDeadline(roomId) { return deadline; +} +async function GetScenarioFeed() { + + const q = await db.query( + `SELECT + rooms.title AS story_title, + rooms.id AS room_id, + scenarios.creator_id AS creator_id, + users.name AS creator_name, + scenarios.id AS scenario_id, + scenarios.scenario, + scenarios.created_at + FROM scenarios + JOIN rooms ON rooms.id = scenarios.room_id + JOIN users ON users.id = scenarios.creator_id + ORDER BY scenarios.id DESC + LIMIT 25;`); + + return q.rows; + } //CHECKS @@ -629,5 +649,6 @@ module.exports = { EmailExists, AddPasswordResetCode, CanEnd, - GetDeadline + GetDeadline, + GetScenarioFeed }; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index a6ceef7..a086c7e 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -77,8 +77,37 @@ const TryAddScenario = async (req, res, next) => { res.status(400).send({ ok: false, message: error.message }); } } +const GetScenarioFeed = async (req, res, next) => { + + try { + + const feed = await dbFunctions.GetScenarioFeed(); + + if (!feed || feed.length < 1) { + console.error('could not get feed. backend threw back: ', feed); + throw new Error('could not get a feed'); + }; + + res.status(200).send({ + ok: true, + message: 'successfully retrieved scenario feed', + data: feed + }) + + } catch (error) { + + console.error(error); + res.status(400).send({ + ok: false, + message: error.message + }); + + } + +} scenarioRouter.use(isAuth); scenarioRouter.post('/', TryAddScenario); +scenarioRouter.get('/feed', GetScenarioFeed); module.exports = scenarioRouter; \ No newline at end of file From aaae9af33b6f7f08ded538ad89175053e6b6239e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 27 Jan 2023 15:19:17 -0300 Subject: [PATCH 086/127] added a random prompt route --- database/dbFunctions.js | 17 ++++++++++++++++- routers/scenarioRouter.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index bd7717a..7afe1c1 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -181,6 +181,20 @@ async function GetScenarioFeed() { return q.rows; +} +async function GetRandomPrompt() { + + const q = await db.query( + `SELECT prompt + FROM prompts + ORDER BY random() + LIMIT 1;` + ); + + const prompt = q.rows[0]; + + return prompt; + } //CHECKS @@ -650,5 +664,6 @@ module.exports = { AddPasswordResetCode, CanEnd, GetDeadline, - GetScenarioFeed + GetScenarioFeed, + GetRandomPrompt }; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index a086c7e..6ff1098 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -104,10 +104,38 @@ const GetScenarioFeed = async (req, res, next) => { } +} +const GetPrompt = async (req, res, next) => { + + try { + + //get prompt from db + const prompt = await dbFunctions.GetRandomPrompt(); + + //check to make sure you got a valid one + if (!prompt || prompt.length < 1) throw new Error('could not get prompt'); + + res.status(200).send({ + ok: true, + message: 'successfully retrieved a prompt', + data: prompt + }) + + } catch (error) { + + console.error(error); + res.status(400).send({ + ok: false, + message: error.message + }); + + } + } scenarioRouter.use(isAuth); scenarioRouter.post('/', TryAddScenario); scenarioRouter.get('/feed', GetScenarioFeed); +scenarioRouter.get('/prompt', GetPrompt); module.exports = scenarioRouter; \ No newline at end of file From 3bcaa2df7fbeef84383e427c07f731e078d95d7e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 1 Feb 2023 13:30:42 -0300 Subject: [PATCH 087/127] added user stat route --- database/dbFunctions.js | 36 ++++++++++++++++++++++++++++++++- routers/userRouter.js | 45 ++++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 7afe1c1..471cd77 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -195,6 +195,39 @@ async function GetRandomPrompt() { return prompt; +} +async function GetPlayerStats(id) { + + const q = await db.query( + `SELECT + COUNT(*) AS camps, + SUM (CASE + WHEN rooms.finished = TRUE THEN 1 + ELSE 0 + END + ) AS finished, + ( + SELECT COUNT(*) + FROM scenarios + WHERE creator_id = $1 + ) AS contributions, + ( + SELECT COUNT(*) + FROM users + WHERE id = $1 + ) AS user_exist + FROM rooms_users + JOIN rooms ON rooms.id = rooms_users.room_id + WHERE rooms_users.user_id = $1;`, + [id] + ); + + const stats = q.rows[0]; + + if (stats.user_exist == 0) return null; + + return stats; + } //CHECKS @@ -665,5 +698,6 @@ module.exports = { CanEnd, GetDeadline, GetScenarioFeed, - GetRandomPrompt + GetRandomPrompt, + GetPlayerStats }; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 8476dc7..fbae24a 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -50,41 +50,40 @@ const GetUserInfo = async (req, res, next) => { } } -// const RequestPasswordReset = async (req, res, next) => { +const GetUserStats = async (req, res, next) => { -// try { -// const { email } = req.query; -// if (!exists(email)) throw new Error('No email provided'); - -// //kolla att användare med email finns -// const userExists = await dbFunctions.EmailExists(email); -// if (!userExists) throw new Error('No user with that email registered'); + try { + + const { userId } = req.query; + if (!userId) throw new Error('no user id provided in query. Cannot return stats'); + const stats = await dbFunctions.GetPlayerStats(userId); -// //generera en kod -// const resetCode = makeid(8); + console.log(stats); -// //++ spara koden i backend, tillsammans med en timeout stamp -// dbFunctions.AddPasswordResetCode(resetCode, email); + if (!stats) throw new Error('found no user with that id'); -// //++ skicka koden i ett email -// //detta kräver ju uppenbarligen lite mer arbete. Du måste sätta dig in i hur du kan skicka email online -// //kanske ska man köra på en service såsom mailersend + res.status(200).send({ + ok: true, + message: 'successfully retrieved user stats', + data: stats + }) -// //skicka ett "ok" status -// res.status(200).send({ ok: true, message: 'email with reset code sent' }); -// } -// catch (error) { + } + catch (error) { -// res.status(400).send({ ok: false, message: error.message }); + res.status(400).send({ + ok: false, + message: error.message, + }) -// } + } -// } +} userRouter.get('/', isAuth, GetUserInfo); +userRouter.get('/stats', GetUserStats); userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); -// userRouter.post('/requestPasswordReset', RequestPasswordReset); module.exports = userRouter; From 7d8ed808ab1e1b8517948b30f5a2624e542229fc Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 3 Feb 2023 11:08:51 -0300 Subject: [PATCH 088/127] fixed db function for new db node structure --- database/dbFunctions.js | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 471cd77..e390c4e 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -75,8 +75,9 @@ const GetScenariosInRoom = async (roomId) => { const scenarioQuery = await db.query( `SELECT scenario, creator_id FROM scenarios - WHERE room_id = $1 - ORDER BY id`, + JOIN nodes ON scenarios.node_id = nodes.id + WHERE nodes.room_id = $1 + ORDER BY nodes.id;`, [roomId] ); @@ -136,7 +137,7 @@ async function GetScenarioCount(roomId) { const q = await db.query( `SELECT COUNT(*) AS count - FROM scenarios + FROM nodes WHERE room_id = $1`, [roomId] ); @@ -168,16 +169,18 @@ async function GetScenarioFeed() { `SELECT rooms.title AS story_title, rooms.id AS room_id, - scenarios.creator_id AS creator_id, + nodes.creator_id AS creator_id, users.name AS creator_name, - scenarios.id AS scenario_id, + nodes.id AS scenario_id, scenarios.scenario, - scenarios.created_at - FROM scenarios - JOIN rooms ON rooms.id = scenarios.room_id - JOIN users ON users.id = scenarios.creator_id - ORDER BY scenarios.id DESC - LIMIT 25;`); + nodes.created_at + FROM nodes + JOIN scenarios ON scenarios.node_id = nodes.id + JOIN rooms ON rooms.id = nodes.room_id + JOIN users ON users.id = nodes.creator_id + ORDER BY nodes.id DESC + LIMIT 25` + ); return q.rows; @@ -208,7 +211,7 @@ async function GetPlayerStats(id) { ) AS finished, ( SELECT COUNT(*) - FROM scenarios + FROM nodes WHERE creator_id = $1 ) AS contributions, ( @@ -295,7 +298,7 @@ async function PlayerHasWrittenInRoom(roomID, userID) { const query = await db.query(` SELECT * - FROM scenarios + FROM nodes WHERE room_id = $1 AND creator_id = $2 `, [roomID, userID]); @@ -312,10 +315,17 @@ async function CanEnd(roomId) { //SETTERS async function AddScenario(scenario, roomId, userId) { const scenarioQuery = await db.query( - 'INSERT INTO scenarios(scenario, creator_id, room_id) VALUES ($1, $2, $3) RETURNING *', + `WITH ins1 AS ( + INSERT INTO nodes(creator_id, room_id) + VALUES ($2, $3) + RETURNING * + ) + INSERT INTO scenarios (node_id, scenario) + SELECT id, $1 FROM ins1 + RETURNING *;`, [scenario, userId, roomId] ); - return scenarioQuery.rows[0].id; + return scenarioQuery.rows[0].node_id; } async function UpdateCharCount(scenario, roomId, userId) { await db.query( From ed72709413746131649ba5e197420650df170af4 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 3 Feb 2023 13:35:30 -0300 Subject: [PATCH 089/127] Updated routes to work when nodes can be empty Now "empty" nodes are created with a prompt each time to post Request routes takes this into account they only return filled nodes and creates new nodes when a scenario is post --- database/dbFunctions.js | 56 +++++++++++++++++++++++++++++++-------- routers/scenarioRouter.js | 1 + 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index e390c4e..d8ed82e 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -77,6 +77,7 @@ const GetScenariosInRoom = async (roomId) => { FROM scenarios JOIN nodes ON scenarios.node_id = nodes.id WHERE nodes.room_id = $1 + AND scenario IS NOT NULL ORDER BY nodes.id;`, [roomId] ); @@ -138,7 +139,8 @@ async function GetScenarioCount(roomId) { const q = await db.query( `SELECT COUNT(*) AS count FROM nodes - WHERE room_id = $1`, + WHERE room_id = $1 + AND creator_id IS NOT NULL`, [roomId] ); @@ -178,6 +180,7 @@ async function GetScenarioFeed() { JOIN scenarios ON scenarios.node_id = nodes.id JOIN rooms ON rooms.id = nodes.room_id JOIN users ON users.id = nodes.creator_id + WHERE scenarios.scenario IS NOT NULL ORDER BY nodes.id DESC LIMIT 25` ); @@ -315,18 +318,46 @@ async function CanEnd(roomId) { //SETTERS async function AddScenario(scenario, roomId, userId) { const scenarioQuery = await db.query( - `WITH ins1 AS ( - INSERT INTO nodes(creator_id, room_id) - VALUES ($2, $3) - RETURNING * - ) - INSERT INTO scenarios (node_id, scenario) - SELECT id, $1 FROM ins1 - RETURNING *;`, + `WITH node_set AS ( + UPDATE nodes + SET creator_id = $2, + created_at = NOW() + WHERE id = ( + SELECT id + FROM nodes + WHERE room_id = $3 + ORDER BY id DESC + LIMIT 1 + ) + RETURNING id + ) + UPDATE scenarios + SET scenario = $1 + WHERE node_id = (SELECT id FROM node_set) + RETURNING node_id;`, [scenario, userId, roomId] ); return scenarioQuery.rows[0].node_id; } +async function CreateNewNode(roomId) { + + const q = await db.query( + `WITH new_node AS ( + INSERT INTO nodes (room_id) + VALUES ($1) + RETURNING id + ) + INSERT INTO scenarios (node_id, prompt) + VALUES ( + (SELECT id FROM new_node), + (SELECT prompt FROM prompts ORDER BY random() LIMIT 1) + );`, + [roomId] + ); + + return q.rows[0].node_id; + +} async function UpdateCharCount(scenario, roomId, userId) { await db.query( 'UPDATE rooms_users SET char_count = (char_count - $1 + 500) WHERE user_id = $2 AND room_id = $3', @@ -382,7 +413,9 @@ async function AddUserToRoom(roomId, userId) { async function CreateNewRoom(title, description, scenario, creator_id) { const roomId = await AddRoom(title, description, creator_id); await AddUserToRoom(roomId, creator_id); - await AddScenario(scenario, roomId, creator_id) + await CreateNewNode(roomId); + await AddScenario(scenario, roomId, creator_id); + await CreateNewNode(roomId); return roomId; } async function AddRoom(title, description, creator_id) { @@ -709,5 +742,6 @@ module.exports = { GetDeadline, GetScenarioFeed, GetRandomPrompt, - GetPlayerStats + GetPlayerStats, + CreateNewNode }; \ No newline at end of file diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 6ff1098..baf9920 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -59,6 +59,7 @@ const TryAddScenario = async (req, res, next) => { await dbFunctions.EndStory(roomId); } else { + await dbFunctions.CreateNewNode(roomId); await dbFunctions.PassTurn(room, userId); await dbFunctions.UpdateCharCount(text, roomId, userId); } From d9f2ac76603ced9b10b181656f9ed770321ed96d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 3 Feb 2023 13:47:38 -0300 Subject: [PATCH 090/127] getting prompts back on room data request --- database/dbFunctions.js | 22 ++++++++++++++++++++-- routers/roomRouter.js | 15 ++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index d8ed82e..cc464b7 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -73,7 +73,7 @@ async function GetActivePlayers(roomId) { const GetScenariosInRoom = async (roomId) => { const scenarioQuery = await db.query( - `SELECT scenario, creator_id + `SELECT scenario, creator_id, prompt FROM scenarios JOIN nodes ON scenarios.node_id = nodes.id WHERE nodes.room_id = $1 @@ -85,6 +85,23 @@ const GetScenariosInRoom = async (roomId) => { return scenarioQuery.rows; } + +async function GetCurrentPrompt(roomId) { + + const q = await db.query( + `SELECT prompt + FROM scenarios + JOIN nodes ON scenarios.node_id = nodes.id + WHERE nodes.room_id = $1 + ORDER BY nodes.id DESC + LIMIT 1`, + [roomId] + ); + + return q.rows[0].prompt; + +} + async function GetNextPlayerId(roomId, currentPlayerId) { //get all the active players in order @@ -743,5 +760,6 @@ module.exports = { GetScenarioFeed, GetRandomPrompt, GetPlayerStats, - CreateNewNode + CreateNewNode, + GetCurrentPrompt }; \ No newline at end of file diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 1a85bc3..54f77a7 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -10,18 +10,23 @@ const GetRoomData = async (req, res, next) => { try { - let room = await dbFunctions.GetRoomInfo(req.params.id) - let players = await dbFunctions.GetPlayersInRoom(req.params.id); - const scenarios = await dbFunctions.GetScenariosInRoom(req.params.id); + const roomId = req.params.id; + + let room = await dbFunctions.GetRoomInfo(roomId) + let players = await dbFunctions.GetPlayersInRoom(roomId); + const scenarios = await dbFunctions.GetScenariosInRoom(roomId); const roomCorrected = await dbFunctions.CheckRoomInfo(room, players, scenarios); if (roomCorrected) { - room = await dbFunctions.GetRoomInfo(req.params.id); - players = await dbFunctions.GetPlayersInRoom(req.params.id); + room = await dbFunctions.GetRoomInfo(roomId); + players = await dbFunctions.GetPlayersInRoom(roomId); } + const prompt = await dbFunctions.GetCurrentPrompt(roomId); + room.players = players; room.scenarios = scenarios; + room.prompt = prompt; res.status(200).send(room); } From fed5a5e050337d4776facdea8da44b18187bc2ab Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Sat, 4 Feb 2023 16:14:20 -0300 Subject: [PATCH 091/127] worked on new node post route --- app.js | 10 +++--- database/dbChecks.js | 63 +++++++++++++++++++++++++++++++++++++ database/dbPosts.js | 48 +++++++++++++++++++++++++++++ routers/nodeRouter.js | 72 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 database/dbChecks.js create mode 100644 database/dbPosts.js create mode 100644 routers/nodeRouter.js diff --git a/app.js b/app.js index e2d2633..693ada6 100644 --- a/app.js +++ b/app.js @@ -18,16 +18,18 @@ if (process.env.NODE_ENV === 'development') { const roomRouter = require('./routers/roomRouter'); const userRouter = require('./routers/userRouter'); const scenarioRouter = require('./routers/scenarioRouter'); +const nodeRouter = require('./routers/nodeRouter'); //mount routers app.use('/room', roomRouter); app.use('/user', userRouter); app.use('/scenario', scenarioRouter); +app.use('/node', nodeRouter); -//test rout -app.get('/', async (req, res) => { - res.send(`unwritten server is running on ${PORT} :)`); -}) +// //test rout +// app.get('/', async (req, res) => { +// res.send(`unwritten server is running on ${PORT} :)`); +// }) //start server app.listen(PORT, () => { diff --git a/database/dbChecks.js b/database/dbChecks.js new file mode 100644 index 0000000..7668de8 --- /dev/null +++ b/database/dbChecks.js @@ -0,0 +1,63 @@ +//CHECKS STUFF IN THE DATABASE AND THROWS ERRORS IF THEY ARE FALSE +const db = require('./dbConnect.js'); + +//CONDITIONALS +async function EmptyNodeExists(campId) { + + const q = await db.query( + ` + SELECT finished_at + FROM nodes_0 + WHERE camp_id = $1 + ORDER BY id DESC + LIMIT 1; + `, + [campId] + ); + + const { finished_at } = q.rows[0]; + + return (!finished_at); + +} + +//ERROR CHECKS +async function CanAddNode(campId, userId) { + + const q = await db.query( + ` + SELECT + camps.finished, + nodes_0.creator_id AS last_node_creator, + nodes_0.created_at AS last_node_posted_at, + nodes_0.finished_at AS last_node_finished_at + FROM camps + JOIN nodes_0 on nodes_0.camp_id = camps.id + WHERE camps.id = $1 + ORDER BY nodes_0.id DESC + LIMIT 1; + `, + [campId] + ); + + if (q.rowCount == 0) throw new Error('there is no camp with that id'); + + const { finished, last_node_creator_id, last_node_posted_at, last_node_finished_at } = q.rows[0]; + + if (finished) throw new Error('Cant add node, story is already finished'); + if (last_node_creator_id == userId) throw new Error('Cant add node, because user added the last one'); + + //check if someone else is writing + if (last_node_finished_at) return; + const last_node_time = (new Date(last_node_posted_at)).getTime(); + const current_time = (new Date()).getTime(); + const diff = current_time - last_node_time; + const diffInMinutes = diff / 1000 / 60; + if (diffInMinutes < 20) throw new Error('Cant add node. Another player is currently writing'); + +} + +module.exports = { + CanAddNode, + EmptyNodeExists, +}; \ No newline at end of file diff --git a/database/dbPosts.js b/database/dbPosts.js new file mode 100644 index 0000000..b9bb904 --- /dev/null +++ b/database/dbPosts.js @@ -0,0 +1,48 @@ +//POST STUFF TO THE DATABASE +const db = require('./dbConnect.js'); + +//NODES +async function AddNode(campId, userId) { + + const q = await db.query( + ` + SELECT finished_at + FROM nodes_0 + WHERE camp_id = $1 + ORDER BY id DESC + LIMIT 1; + `, + [campId] + ); + + const { finished_at } = q.rows[0]; + + if (finished_at) { + await db.query( + ` + INSERT INTO nodes_0 (creator_id, camp_id) + VALUES ($1, $2) + `, + [userId, campId] + ); + } + else { + await db.query( + ` + UPDATE nodes_0 + SET + creator_id = $1, + created_at = NOW() + WHERE + finished_at IS NULL + AND camp_id = $2; + `, + [userId, campId] + ); + } + +} + +module.exports = { + AddNode +}; \ No newline at end of file diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js new file mode 100644 index 0000000..e700b1b --- /dev/null +++ b/routers/nodeRouter.js @@ -0,0 +1,72 @@ +const express = require('express'); +const nodeRouter = express.Router(); +const dbFunctions = require('../database/dbFunctions'); +const dbChecks = require('../database/dbChecks'); +const dbPosts = require('../database/dbPosts'); +const { isAuth } = require('../middleware/authentication'); +const { ValidateChars } = require('../middleware/validation'); + +const GetFeed = async (req, res, next) => { + + try { + + const feed = await dbFunctions.GetScenarioFeed(); + + if (!feed || feed.length < 1) { + console.error('could not get feed. backend threw back: ', feed); + throw new Error('could not get a feed'); + }; + + res.status(200).send({ + ok: true, + message: 'successfully retrieved scenario feed', + data: feed + }) + + } catch (error) { + + console.error(error); + res.status(400).send({ + ok: false, + message: error.message + }); + + } + +} + +const TryAddNode = async (req, res, next) => { + + try { + + const userId = req.userId; + const { campId } = req.query + + //initial error checks + if (!campId) throw new Error('Please provide a campId'); + if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly') + await dbChecks.CanAddNode(campId, userId); + + //post dat shit + await dbPosts.AddNode(campId, userId); + + //send response + res.send({ + ok: true, + message: 'Node added!' + }); + + } + catch (error) { + console.error(error); + res.status(400).send({ ok: false, message: error.message }); + } + +} + +nodeRouter.use(isAuth); +nodeRouter.post('/', TryAddNode); +// nodeRouter.post('/scenario', TryAddScenario); +nodeRouter.get('/feed', GetFeed); + +module.exports = nodeRouter; \ No newline at end of file From bdd7c7c4bb9b1a8c3957dae341af8654dc09fc4f Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 13:11:24 -0300 Subject: [PATCH 092/127] =?UTF-8?q?f=C3=A4rdigst=C3=A4lle=20post=20node=20?= =?UTF-8?q?route=20(buggfix)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/dbChecks.js | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/database/dbChecks.js b/database/dbChecks.js index 7668de8..6d68538 100644 --- a/database/dbChecks.js +++ b/database/dbChecks.js @@ -1,26 +1,6 @@ //CHECKS STUFF IN THE DATABASE AND THROWS ERRORS IF THEY ARE FALSE const db = require('./dbConnect.js'); -//CONDITIONALS -async function EmptyNodeExists(campId) { - - const q = await db.query( - ` - SELECT finished_at - FROM nodes_0 - WHERE camp_id = $1 - ORDER BY id DESC - LIMIT 1; - `, - [campId] - ); - - const { finished_at } = q.rows[0]; - - return (!finished_at); - -} - //ERROR CHECKS async function CanAddNode(campId, userId) { @@ -28,7 +8,7 @@ async function CanAddNode(campId, userId) { ` SELECT camps.finished, - nodes_0.creator_id AS last_node_creator, + nodes_0.creator_id AS last_node_creator_id, nodes_0.created_at AS last_node_posted_at, nodes_0.finished_at AS last_node_finished_at FROM camps @@ -44,6 +24,9 @@ async function CanAddNode(campId, userId) { const { finished, last_node_creator_id, last_node_posted_at, last_node_finished_at } = q.rows[0]; + console.log('user id is ', userId); + console.log('last node added by: ', last_node_creator_id); + if (finished) throw new Error('Cant add node, story is already finished'); if (last_node_creator_id == userId) throw new Error('Cant add node, because user added the last one'); @@ -53,11 +36,12 @@ async function CanAddNode(campId, userId) { const current_time = (new Date()).getTime(); const diff = current_time - last_node_time; const diffInMinutes = diff / 1000 / 60; - if (diffInMinutes < 20) throw new Error('Cant add node. Another player is currently writing'); + if (diffInMinutes < 20) { + throw new Error('Cant add node. Another player is currently writing'); + } } module.exports = { CanAddNode, - EmptyNodeExists, }; \ No newline at end of file From 87b9218fff3765a00160e662d5e9128e63786dce Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 13:52:17 -0300 Subject: [PATCH 093/127] scenario add route: checks done --- appInfo/balancing.js | 8 +++++ database/dbChecks.js | 35 ++++++++++++++++++++ routers/nodeRouter.js | 68 +++++++++++++++++++++++++++++++++++++++ routers/scenarioRouter.js | 1 + 4 files changed, 112 insertions(+) create mode 100644 appInfo/balancing.js diff --git a/appInfo/balancing.js b/appInfo/balancing.js new file mode 100644 index 0000000..dad23aa --- /dev/null +++ b/appInfo/balancing.js @@ -0,0 +1,8 @@ +const numbers = { + scenarioMaxCharacters: 600, + scenarioMinCharacter: 3, +} + +module.exports = { + numbers +} \ No newline at end of file diff --git a/database/dbChecks.js b/database/dbChecks.js index 6d68538..830656c 100644 --- a/database/dbChecks.js +++ b/database/dbChecks.js @@ -42,6 +42,41 @@ async function CanAddNode(campId, userId) { } +async function CanAddScenario(campId, userId, isEnd) { + + const q = await db.query( + ` + SELECT + nodes_0.creator_id AS node_creator_id, + finished_at AS node_finish_time, + scenario, + camps.finished AS camp_finished + FROM nodes_0 + LEFT JOIN scenarios_0 ON scenarios_0.node_id = nodes_0.id + JOIN camps ON camps.id = nodes_0.camp_id + WHERE camp_id = 59 + ORDER BY nodes_0.id; + `, + [campId] + ); + + const nodeCount = q.rowCount; + const lastNode = q.rows[q.rows.length - 1]; + const lastPosterId = lastNode.node_creator_id; + const lastNodeEmpty = (lastNode.node_finish_time == null) + const scenario = lastNode.scenario; + const campFinished = lastNode.camp_finished; + + if (!lastNodeEmpty) throw new Error('Cant add scenario, because the last node is marked as finished'); + if (!lastPosterId != userId) throw new Error('Cant add scenario, because someone else owns the node'); + if (isEnd && nodeCount < 30) throw new Error('Cant add end, because the story is not long enough. Current nodecount: ', nodeCount); + if (!isEnd && nodeCount == 40) throw new Error('Cant add scenario. Must add end, because the story has reached its max length: ', nodeCount); + if (campFinished) throw new Error('cant add scenario, story marked as finished'); + if (scenario) throw new Error('Cant add scenario, there already exist one linked to the last node'); + +} + module.exports = { CanAddNode, + CanAddScenario }; \ No newline at end of file diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index e700b1b..42655d6 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -5,6 +5,7 @@ const dbChecks = require('../database/dbChecks'); const dbPosts = require('../database/dbPosts'); const { isAuth } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); +const balancing = require('../appInfo/balancing'); const GetFeed = async (req, res, next) => { @@ -64,6 +65,73 @@ const TryAddNode = async (req, res, next) => { } +const TryAddScenario = async (req, res, next) => { + + let transactionInitiated = false; + + try { + + const userId = req.userId; + const { campId, text, end } = req.query + const isEnd = (end == "true"); + + //initial error checks + ValidateChars(text); + if (!campId) throw new Error('No room_id provided'); + if (!text) throw new Error('No text provided'); + if (text.length < balancing.numbers.scenarioMinCharacter) throw new Error('min chars in scenario: ', scenarioMinCharacters); + if (text.length > balancing.numbers.scenarioMaxCharacters) throw new Error('max chars in scenario: ', scenarioMaxCharacters); + if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); + await dbChecks.CanAddScenario(campId, userId, isEnd); + + //make queries + let room = await dbFunctions.GetRoomInfo(campId); + let players = await dbFunctions.GetPlayersInRoom(campId); + let scenarios = await dbFunctions.GetScenariosInRoom(campId); + + const correctionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); + if (correctionsMade) { + room = await dbFunctions.GetRoomInfo(campId); + players = await dbFunctions.GetPlayersInRoom(campId); + scenarios = await dbFunctions.GetScenariosInRoom(campId); + } + + //some db checks + dbFunctions.MakeSurePlayerIsActive(players, userId); + dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); + dbFunctions.MakeSureItsPlayersTurn(room, userId); + + //transaction (things in here will be rolled back on error) + await dbFunctions.BeginTransaction(); + transactionInitiated = true; + + const scenarioId = await dbFunctions.AddScenario(text, campId, userId); + + if (isEnd) { + await dbFunctions.EndStory(campId); + } + else { + await dbFunctions.CreateNewNode(campId); + await dbFunctions.PassTurn(room, userId); + await dbFunctions.UpdateCharCount(text, campId, userId); + } + + await dbFunctions.Commit(); + + //send response + let responseMessage; + if (!isEnd) responseMessage = 'new scenario added with id: ' + scenarioId + else responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + res.send({ ok: true, message: responseMessage }); + } + catch (error) { + if (transactionInitiated) dbFunctions.Rollback(); + console.error(error); + res.status(400).send({ ok: false, message: error.message }); + } + +} + nodeRouter.use(isAuth); nodeRouter.post('/', TryAddNode); // nodeRouter.post('/scenario', TryAddScenario); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index baf9920..0320218 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,6 +1,7 @@ const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); +const dbChecks = require('../database/dbChecks'); const { isAuth } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); From 86a2c5f0db34fe825dc5e44e9d30e88923410d12 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 14:47:23 -0300 Subject: [PATCH 094/127] create node route fix. now returns prompt --- database/dbData.js | 30 +++++++++++++++++ database/dbPosts.js | 64 +++++++++++++++++++++++++++++++++--- database/dbTransactions.js | 19 +++++++++++ routers/nodeRouter.js | 67 ++++++++++++++------------------------ 4 files changed, 134 insertions(+), 46 deletions(-) create mode 100644 database/dbData.js create mode 100644 database/dbTransactions.js diff --git a/database/dbData.js b/database/dbData.js new file mode 100644 index 0000000..d4e890c --- /dev/null +++ b/database/dbData.js @@ -0,0 +1,30 @@ +const db = require('./dbConnect.js'); + +async function Feed() { + + const q = await db.query( + `SELECT + camps.title AS story_title, + camps.id AS room_id, + nodes_0.creator_id AS creator_id, + users.name AS creator_name, + nodes_0.id AS scenario_id, + scenarios_0.scenario, + nodes_0.created_at + FROM nodes_0 + JOIN scenarios_0 ON scenarios_0.node_id = nodes_0.id + JOIN camps ON camps.id = nodes_0.camp_id + JOIN users ON users.id = nodes_0.creator_id + WHERE scenarios_0.scenario IS NOT NULL + ORDER BY nodes_0.id DESC + LIMIT 25` + ); + + return q.rows; + +} + +//EXPORT +module.exports = { + Feed +}; \ No newline at end of file diff --git a/database/dbPosts.js b/database/dbPosts.js index b9bb904..cf44564 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -6,7 +6,7 @@ async function AddNode(campId, userId) { const q = await db.query( ` - SELECT finished_at + SELECT finished_at, id FROM nodes_0 WHERE camp_id = $1 ORDER BY id DESC @@ -16,17 +16,38 @@ async function AddNode(campId, userId) { ); const { finished_at } = q.rows[0]; + const last_node_id = q.rows[0].id; if (finished_at) { - await db.query( + + //the last node is finished, and we need to add one + const nodeQ = await db.query( ` INSERT INTO nodes_0 (creator_id, camp_id) VALUES ($1, $2) + RETURNING id `, [userId, campId] ); + const newNodeId = nodeQ.rows[0].id; + const scenarioAddQ = await db.query( + ` + INSERT INTO scenarios_0 (node_id, prompt) + VALUES ( + $1, + (SELECT prompt FROM prompts ORDER BY random() LIMIT 1) + ) + RETURNING prompt; + `, + [newNodeId] + ) + const { prompt } = scenarioAddQ.rows[0]; + return prompt; + } else { + + //the last node is NOT finished, and we need to update it await db.query( ` UPDATE nodes_0 @@ -35,14 +56,49 @@ async function AddNode(campId, userId) { created_at = NOW() WHERE finished_at IS NULL - AND camp_id = $2; + AND camp_id = $2 `, [userId, campId] ); + const scenarioQ = await db.query( + ` + SELECT prompt + FROM scenarios_0 + WHERE node_id = $1 + `, + [last_node_id] + ) + const { prompt } = scenarioQ.rows[0]; + return prompt; + } + + +} + +async function AddScenario(campId, text, isEnd) { + + // const scenarioId = await dbFunctions.AddScenario(text, campId, userId); + + const q = await db.query( + ` + + ` + ) + + // if (isEnd) { + // await dbFunctions.EndStory(campId); + // } + // else { + // await dbFunctions.CreateNewNode(campId); + // await dbFunctions.PassTurn(room, userId); + // await dbFunctions.UpdateCharCount(text, campId, userId); + // } + } module.exports = { - AddNode + AddNode, + AddScenario }; \ No newline at end of file diff --git a/database/dbTransactions.js b/database/dbTransactions.js new file mode 100644 index 0000000..796fb08 --- /dev/null +++ b/database/dbTransactions.js @@ -0,0 +1,19 @@ +const db = require('./dbConnect.js'); + +//TRANSACTIONS +async function Begin() { + await db.query('BEGIN'); +} +function Rollback() { + db.query('ROLLBACK'); +} +async function Commit() { + await db.query('COMMIT'); +} + +//EXPORT +module.exports = { + Begin, + Rollback, + Commit +}; \ No newline at end of file diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index 42655d6..85ffe71 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -1,8 +1,11 @@ const express = require('express'); const nodeRouter = express.Router(); -const dbFunctions = require('../database/dbFunctions'); + const dbChecks = require('../database/dbChecks'); const dbPosts = require('../database/dbPosts'); +const dbTransactions = require('../database/dbTransactions'); +const dbData = require('../database/dbData'); + const { isAuth } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); const balancing = require('../appInfo/balancing'); @@ -11,7 +14,8 @@ const GetFeed = async (req, res, next) => { try { - const feed = await dbFunctions.GetScenarioFeed(); + + const feed = await dbData.Feed(); if (!feed || feed.length < 1) { console.error('could not get feed. backend threw back: ', feed); @@ -38,6 +42,8 @@ const GetFeed = async (req, res, next) => { const TryAddNode = async (req, res, next) => { + let transactionInitiated = false; + try { const userId = req.userId; @@ -49,16 +55,21 @@ const TryAddNode = async (req, res, next) => { await dbChecks.CanAddNode(campId, userId); //post dat shit - await dbPosts.AddNode(campId, userId); + await dbTransactions.Begin(); + transactionInitiated = true; + const prompt = await dbPosts.AddNode(campId, userId); + await dbTransactions.Commit(); //send response res.send({ ok: true, - message: 'Node added!' + message: 'Node added! Created a prompt', + data: { prompt } }); } catch (error) { + if (transactionInitiated) dbTransactions.Rollback(); console.error(error); res.status(400).send({ ok: false, message: error.message }); } @@ -71,11 +82,12 @@ const TryAddScenario = async (req, res, next) => { try { + //params const userId = req.userId; const { campId, text, end } = req.query const isEnd = (end == "true"); - //initial error checks + //checks ValidateChars(text); if (!campId) throw new Error('No room_id provided'); if (!text) throw new Error('No text provided'); @@ -84,48 +96,19 @@ const TryAddScenario = async (req, res, next) => { if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); await dbChecks.CanAddScenario(campId, userId, isEnd); - //make queries - let room = await dbFunctions.GetRoomInfo(campId); - let players = await dbFunctions.GetPlayersInRoom(campId); - let scenarios = await dbFunctions.GetScenariosInRoom(campId); - - const correctionsMade = await dbFunctions.CheckRoomInfo(room, players, scenarios); - if (correctionsMade) { - room = await dbFunctions.GetRoomInfo(campId); - players = await dbFunctions.GetPlayersInRoom(campId); - scenarios = await dbFunctions.GetScenariosInRoom(campId); - } - - //some db checks - dbFunctions.MakeSurePlayerIsActive(players, userId); - dbFunctions.MakeSurePlayerHasEnoughChars(players, text, userId); - dbFunctions.MakeSureItsPlayersTurn(room, userId); - - //transaction (things in here will be rolled back on error) - await dbFunctions.BeginTransaction(); + //transaction + await dbTransactions.Begin(); transactionInitiated = true; - - const scenarioId = await dbFunctions.AddScenario(text, campId, userId); - - if (isEnd) { - await dbFunctions.EndStory(campId); - } - else { - await dbFunctions.CreateNewNode(campId); - await dbFunctions.PassTurn(room, userId); - await dbFunctions.UpdateCharCount(text, campId, userId); - } - - await dbFunctions.Commit(); + await dbPosts.AddScenario(campId, text, isEnd); + await dbTransactions.Commit(); //send response - let responseMessage; - if (!isEnd) responseMessage = 'new scenario added with id: ' + scenarioId - else responseMessage = 'you ended the story! And got a key!: ' + scenarioId; + let responseMessage = 'new scenario added!'; + if (isEnd) responseMessage = 'you ended the story! And got a log!'; res.send({ ok: true, message: responseMessage }); } catch (error) { - if (transactionInitiated) dbFunctions.Rollback(); + if (transactionInitiated) dbTransactions.Rollback(); console.error(error); res.status(400).send({ ok: false, message: error.message }); } @@ -134,7 +117,7 @@ const TryAddScenario = async (req, res, next) => { nodeRouter.use(isAuth); nodeRouter.post('/', TryAddNode); -// nodeRouter.post('/scenario', TryAddScenario); +nodeRouter.post('/scenario', TryAddScenario); nodeRouter.get('/feed', GetFeed); module.exports = nodeRouter; \ No newline at end of file From 448296cb60dc07220354cd7c0bbf434d94363c70 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 15:24:33 -0300 Subject: [PATCH 095/127] post scenario transaction done --- database/dbData.js | 22 +++++++++++++++- database/dbPosts.js | 63 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index d4e890c..e878f65 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -1,5 +1,6 @@ const db = require('./dbConnect.js'); +//request to app async function Feed() { const q = await db.query( @@ -24,7 +25,26 @@ async function Feed() { } +//helpers +async function LastNodeInCamp(campId) { + + const last_node_q = await db.query( + ` + SELECT id + FROM nodes_0 + WHERE camp_id = $1 + ORDER BY id DESC + LIMIT 1; + `, + [campId] + ); + const { last_node_id } = last_node_q.rows[0]; + return last_node_id; + +} + //EXPORT module.exports = { - Feed + Feed, + LastNodeInCamp }; \ No newline at end of file diff --git a/database/dbPosts.js b/database/dbPosts.js index cf44564..639ee3d 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -1,5 +1,6 @@ //POST STUFF TO THE DATABASE const db = require('./dbConnect.js'); +const dbData = require('./dbData'); //NODES async function AddNode(campId, userId) { @@ -79,22 +80,58 @@ async function AddNode(campId, userId) { async function AddScenario(campId, text, isEnd) { - // const scenarioId = await dbFunctions.AddScenario(text, campId, userId); + const last_node_id = await dbData.LastNodeInCamp(campId); - const q = await db.query( + //insert text into scenarios + await db.query( + ` + UPDATE scenarios_0 + SET scenario = $1 + WHERE node_id = $2 + `, + [text, last_node_id] + ); + + //add a finished at stamp in the node + await db.query( ` - + UPDATE nodes_0 + SET finished_at = now() + WHERE id = $1 + `, + [last_node_id] + ); + + if (!isEnd) return; + + //if end + + //set camp to finished + await db.query( ` - ) - - // if (isEnd) { - // await dbFunctions.EndStory(campId); - // } - // else { - // await dbFunctions.CreateNewNode(campId); - // await dbFunctions.PassTurn(room, userId); - // await dbFunctions.UpdateCharCount(text, campId, userId); - // } + UPDATE camps + SET finished = 'true' + WHERE id = $1 + `, + [campId] + ); + + //hand out logs to every participant + await db.query( + ` + WITH players_in_camp AS ( + SELECT creator_id + FROM nodes_0 + WHERE camp_id = $1 + GROUP BY creator_id + ) + UPDATE users + SET room_keys = room_keys + 1 + FROM players_in_camp + WHERE users.id = players_in_camp.creator_id + `, + [campId] + ); } From cf9915bbfcbadd6aecc3064ac368d94269af2543 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 15:58:50 -0300 Subject: [PATCH 096/127] add scenario route now works --- database/dbChecks.js | 4 ++-- database/dbData.js | 4 ++-- routers/nodeRouter.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/database/dbChecks.js b/database/dbChecks.js index 830656c..0842cef 100644 --- a/database/dbChecks.js +++ b/database/dbChecks.js @@ -54,7 +54,7 @@ async function CanAddScenario(campId, userId, isEnd) { FROM nodes_0 LEFT JOIN scenarios_0 ON scenarios_0.node_id = nodes_0.id JOIN camps ON camps.id = nodes_0.camp_id - WHERE camp_id = 59 + WHERE camp_id = $1 ORDER BY nodes_0.id; `, [campId] @@ -68,7 +68,7 @@ async function CanAddScenario(campId, userId, isEnd) { const campFinished = lastNode.camp_finished; if (!lastNodeEmpty) throw new Error('Cant add scenario, because the last node is marked as finished'); - if (!lastPosterId != userId) throw new Error('Cant add scenario, because someone else owns the node'); + if (lastPosterId != userId) throw new Error('Cant add scenario, because someone else owns the node'); if (isEnd && nodeCount < 30) throw new Error('Cant add end, because the story is not long enough. Current nodecount: ', nodeCount); if (!isEnd && nodeCount == 40) throw new Error('Cant add scenario. Must add end, because the story has reached its max length: ', nodeCount); if (campFinished) throw new Error('cant add scenario, story marked as finished'); diff --git a/database/dbData.js b/database/dbData.js index e878f65..dc5983b 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -38,8 +38,8 @@ async function LastNodeInCamp(campId) { `, [campId] ); - const { last_node_id } = last_node_q.rows[0]; - return last_node_id; + const { id } = last_node_q.rows[0]; + return id; } diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index 85ffe71..b26aeaf 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -94,7 +94,7 @@ const TryAddScenario = async (req, res, next) => { if (text.length < balancing.numbers.scenarioMinCharacter) throw new Error('min chars in scenario: ', scenarioMinCharacters); if (text.length > balancing.numbers.scenarioMaxCharacters) throw new Error('max chars in scenario: ', scenarioMaxCharacters); if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); - await dbChecks.CanAddScenario(campId, userId, isEnd); + await dbChecks.CanAddScenario(campId, userId, isEnd); //transaction await dbTransactions.Begin(); From d22f7f8b953e41a650ca42a4539f23d8cea8af72 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 16:01:23 -0300 Subject: [PATCH 097/127] feed timestamp correction --- database/dbData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/dbData.js b/database/dbData.js index dc5983b..a78bb10 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -11,7 +11,7 @@ async function Feed() { users.name AS creator_name, nodes_0.id AS scenario_id, scenarios_0.scenario, - nodes_0.created_at + nodes_0.finished_at FROM nodes_0 JOIN scenarios_0 ON scenarios_0.node_id = nodes_0.id JOIN camps ON camps.id = nodes_0.camp_id From 8ab07034d79833026d02a399f04fe47e3696f410 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 16:40:20 -0300 Subject: [PATCH 098/127] new route to get camp data --- app.js | 12 ++++--- database/dbData.js | 78 ++++++++++++++++++++++++++++++++++++++--- database/dbFunctions.js | 7 +++- database/dbPosts.js | 7 ++-- routers/campRouter.js | 36 +++++++++++++++++++ 5 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 routers/campRouter.js diff --git a/app.js b/app.js index 693ada6..52f07a2 100644 --- a/app.js +++ b/app.js @@ -15,16 +15,18 @@ if (process.env.NODE_ENV === 'development') { } //import routers -const roomRouter = require('./routers/roomRouter'); +const roomRouter = require('./routers/roomRouter'); //legacy const userRouter = require('./routers/userRouter'); -const scenarioRouter = require('./routers/scenarioRouter'); +const scenarioRouter = require('./routers/scenarioRouter'); //legacy const nodeRouter = require('./routers/nodeRouter'); +const campRouter = require('./routers/campRouter'); //mount routers -app.use('/room', roomRouter); +app.use('/room', roomRouter); //legacy app.use('/user', userRouter); -app.use('/scenario', scenarioRouter); +app.use('/scenario', scenarioRouter); //legacy app.use('/node', nodeRouter); +app.use('/camp', campRouter); // //test rout // app.get('/', async (req, res) => { @@ -34,6 +36,6 @@ app.use('/node', nodeRouter); //start server app.listen(PORT, () => { console.log(`App is running on ${PORT}`) -}) +}) //test diff --git a/database/dbData.js b/database/dbData.js index a78bb10..42c13bf 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -23,6 +23,65 @@ async function Feed() { return q.rows; +} +async function CampData(campId) { + + const campQ = await db.query( + ` + SELECT * + FROM camps + WHERE id=$1 + `, + [campId] + ); + + if (campQ.rowCount < 1) throw new Error('there are no camps with that id'); + const camp = campQ.rows[0]; + + return camp; +} +async function PlayersInCamp(campId) { + + const playerQuery = await db.query( + ` + SELECT + users.id, + users.name + FROM nodes_0 + JOIN users ON creator_id = users.id + WHERE camp_id = $1 + GROUP BY users.id; + `, + [campId] + ) + + if (playerQuery.rowCount < 1) throw new Error('found no players in the camp with that id'); + const players = playerQuery.rows; + return players; + +} +async function ScenariosInCamp(campId) { + + const scenarioQ = await db.query( + ` + SELECT + scenario, + creator_id, + prompt + FROM scenarios_0 + JOIN nodes_0 + ON scenarios_0.node_id = nodes_0.id + WHERE nodes_0.camp_id = $1 + AND scenario IS NOT NULL + ORDER BY nodes_0.id; + `, + [campId] + ); + + if (scenarioQ.rowCount < 1) throw new Error('no scenarios in the camp witht that id'); + + return scenarioQ.rows; + } //helpers @@ -30,21 +89,30 @@ async function LastNodeInCamp(campId) { const last_node_q = await db.query( ` - SELECT id + SELECT + nodes_0.id as node_id, + creator_id, + created_at, + finished_at, + users.name as creator_name FROM nodes_0 + JOIN users on users.id = nodes_0.creator_id WHERE camp_id = $1 - ORDER BY id DESC + ORDER BY nodes_0.id DESC LIMIT 1; `, [campId] ); - const { id } = last_node_q.rows[0]; - return id; + const node = last_node_q.rows[0]; + return node; } //EXPORT module.exports = { Feed, - LastNodeInCamp + LastNodeInCamp, + CampData, + PlayersInCamp, + ScenariosInCamp }; \ No newline at end of file diff --git a/database/dbFunctions.js b/database/dbFunctions.js index cc464b7..3eaee2e 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -47,7 +47,12 @@ async function GetRoomInfo(roomId) { } async function GetPlayersInRoom(roomId) { const query = await db.query( - `SELECT users.id, users.name, rooms_users.char_count, active, strikes + `SELECT + users.id, + users.name, + rooms_users.char_count, + active, + strikes FROM rooms_users JOIN users ON rooms_users.user_id = users.id WHERE rooms_users.room_id = $1 diff --git a/database/dbPosts.js b/database/dbPosts.js index 639ee3d..ea478c3 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -80,7 +80,8 @@ async function AddNode(campId, userId) { async function AddScenario(campId, text, isEnd) { - const last_node_id = await dbData.LastNodeInCamp(campId); + const lastNode = await dbData.LastNodeInCamp(campId); + const lastNodeId = lastNode.node_id; //insert text into scenarios await db.query( @@ -89,7 +90,7 @@ async function AddScenario(campId, text, isEnd) { SET scenario = $1 WHERE node_id = $2 `, - [text, last_node_id] + [text, lastNodeId] ); //add a finished at stamp in the node @@ -99,7 +100,7 @@ async function AddScenario(campId, text, isEnd) { SET finished_at = now() WHERE id = $1 `, - [last_node_id] + [lastNodeId] ); if (!isEnd) return; diff --git a/routers/campRouter.js b/routers/campRouter.js new file mode 100644 index 0000000..4b8ec9c --- /dev/null +++ b/routers/campRouter.js @@ -0,0 +1,36 @@ +const express = require('express'); +const campRouter = express.Router(); +const { isAuth } = require('../middleware/authentication'); +const dbData = require('../database/dbData'); + +const GetCampData = async (req, res, next) => { + + try { + + const campId = req.params.id; + + const camp = await dbData.CampData(campId); + const players = await dbData.PlayersInCamp(campId); + const scenarios = await dbData.ScenariosInCamp(campId); + const lastNode = await dbData.LastNodeInCamp(campId); + + camp.players = players; + camp.scenarios = scenarios; + camp.lastNode = lastNode; + + res.status(200).send(camp); + + } + catch (error) { + + console.error(error); + res.status(400).send('Failed to get room: ' + error.message); + + } + +} + +campRouter.use(isAuth); +campRouter.get('/data/:id', GetCampData); + +module.exports = campRouter; \ No newline at end of file From 2540f30b226935d38c238a31f4241e82c3d9ef9a Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 17:38:25 -0300 Subject: [PATCH 099/127] get active camps route --- appInfo/balancing.js | 1 + database/dbData.js | 38 +++++++++++++++++++++++++++++++++++++- routers/campRouter.js | 23 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/appInfo/balancing.js b/appInfo/balancing.js index dad23aa..5e5d9f6 100644 --- a/appInfo/balancing.js +++ b/appInfo/balancing.js @@ -1,6 +1,7 @@ const numbers = { scenarioMaxCharacters: 600, scenarioMinCharacter: 3, + maxPlayersForQueueTop: 3 } module.exports = { diff --git a/database/dbData.js b/database/dbData.js index 42c13bf..ec57bcd 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -1,4 +1,5 @@ const db = require('./dbConnect.js'); +const balancing = require('../appInfo/balancing'); //request to app async function Feed() { @@ -82,6 +83,40 @@ async function ScenariosInCamp(campId) { return scenarioQ.rows; +} +async function ActiveCamps(userId) { + + const campQuery = await db.query( + ` + SELECT + camps.id, + camps.title, + camps.description, + users.name AS creator_name, + COUNT(DISTINCT nodes_0.id) AS node_count, + COUNT(DISTINCT nodes_0.creator_id) AS contributor_count, + camps.created_at + FROM camps + JOIN users ON users.id = camps.creator_id + JOIN nodes_0 ON nodes_0.camp_id = camps.id + WHERE NOT EXISTS( + SELECT * + FROM nodes_0 + WHERE creator_id = $1 + AND camp_id = camps.id + ) + AND finished = 'false' + GROUP BY camps.id, users.name, camps.title, camps.description + ORDER BY (COUNT(DISTINCT nodes_0.creator_id) > $2), created_at + ; + `, + [userId, balancing.numbers.maxPlayersForQueueTop] + ); + + // console.log('camp count found: ', campQuery); + + return campQuery.rows; + } //helpers @@ -114,5 +149,6 @@ module.exports = { LastNodeInCamp, CampData, PlayersInCamp, - ScenariosInCamp + ScenariosInCamp, + ActiveCamps }; \ No newline at end of file diff --git a/routers/campRouter.js b/routers/campRouter.js index 4b8ec9c..b215dd9 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -30,7 +30,30 @@ const GetCampData = async (req, res, next) => { } +const GetActiveCamps = async (req, res, next) => { + + try { + + const { userId } = req; + const camps = await dbData.ActiveCamps(userId); + res.status(200).send({ + ok: true, + message: 'found camps', + data: camps + }); + + } + catch (error) { + + console.error(error); + res.status(400).send('Failed to get active camps: ' + error.message); + + } + +} + campRouter.use(isAuth); campRouter.get('/data/:id', GetCampData); +campRouter.get('/active', GetActiveCamps); module.exports = campRouter; \ No newline at end of file From 6efe1ef2dd5b9c0a65f9e0f0cd7a4332e349b097 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 17:42:06 -0300 Subject: [PATCH 100/127] get player camps route --- database/dbData.js | 36 +++++++++++++++++++++++++++++++++++- routers/campRouter.js | 23 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/database/dbData.js b/database/dbData.js index ec57bcd..7ef3492 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -118,6 +118,39 @@ async function ActiveCamps(userId) { return campQuery.rows; } +async function PlayerCamps(userId) { + + const campQuery = await db.query( + ` + SELECT + camps.id, + camps.title, + camps.description, + users.name AS creator_name, + COUNT(DISTINCT nodes_0.id) AS node_count, + COUNT(DISTINCT nodes_0.creator_id) AS contributor_count, + camps.created_at + FROM camps + JOIN users ON users.id = camps.creator_id + JOIN nodes_0 ON nodes_0.camp_id = camps.id + WHERE EXISTS( + SELECT * + FROM nodes_0 + WHERE creator_id = $1 + AND camp_id = camps.id + ) + AND finished = 'false' + GROUP BY camps.id, users.name, camps.title, camps.description + ORDER BY (COUNT(DISTINCT nodes_0.creator_id) > $2), created_at + ; + `, + [userId, balancing.numbers.maxPlayersForQueueTop] + ); + + return campQuery.rows; + +} + //helpers async function LastNodeInCamp(campId) { @@ -150,5 +183,6 @@ module.exports = { CampData, PlayersInCamp, ScenariosInCamp, - ActiveCamps + ActiveCamps, + PlayerCamps }; \ No newline at end of file diff --git a/routers/campRouter.js b/routers/campRouter.js index b215dd9..e29ce14 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -52,8 +52,31 @@ const GetActiveCamps = async (req, res, next) => { } +const GetPlayerCamps = async (req, res, next) => { + + try { + + const { userId } = req; + const camps = await dbData.PlayerCamps(userId); + res.status(200).send({ + ok: true, + message: 'found camps', + data: camps + }); + + } + catch (error) { + + console.error(error); + res.status(400).send('Failed to get active camps: ' + error.message); + + } + +} + campRouter.use(isAuth); campRouter.get('/data/:id', GetCampData); campRouter.get('/active', GetActiveCamps); +campRouter.get('/player', GetPlayerCamps); module.exports = campRouter; \ No newline at end of file From d4bd202ac3f2d85ea005867e86849843bf3b807f Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 6 Feb 2023 18:21:27 -0300 Subject: [PATCH 101/127] create camp query, began work --- appInfo/balancing.js | 8 ++++++-- database/dbPosts.js | 30 ++++++++++++++++++++++++++-- routers/campRouter.js | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/appInfo/balancing.js b/appInfo/balancing.js index 5e5d9f6..65edcf6 100644 --- a/appInfo/balancing.js +++ b/appInfo/balancing.js @@ -1,7 +1,11 @@ const numbers = { - scenarioMaxCharacters: 600, + maxPlayersForQueueTop: 3, + titleMinChars: 3, + titleMaxChars: 50, + descriptionMinChars: 3, + descriptionMaxChars: 200, scenarioMinCharacter: 3, - maxPlayersForQueueTop: 3 + scenarioMaxCharacters: 600, } module.exports = { diff --git a/database/dbPosts.js b/database/dbPosts.js index ea478c3..8058941 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -77,7 +77,6 @@ async function AddNode(campId, userId) { } - async function AddScenario(campId, text, isEnd) { const lastNode = await dbData.LastNodeInCamp(campId); @@ -136,7 +135,34 @@ async function AddScenario(campId, text, isEnd) { } +//CAMPS +async function Camp(title, description, scenario, creator_id) { + + const campQ = await db.query( + ` + WITH new_camp AS( + INSERT INTO camps(title, description, creator_id) + VALUES($1, $2, $3) + RETURNING id + ), new_node AS ( + INSERT INTO nodes_0 (creator_id, camp_id, finished_at) + VALUES ($3, (SELECT id FROM new_camp), now()) + RETURNING id + ) + INSERT INTO scenarios_0 (scenario, node_id) + VALUES ($4, (SELECT id FROM new_node)) + RETURNING (SELECT id FROM new_camp); + ` + , + [title, description, creator_id, scenario] + ); + const campId = campQ.rows[0].id; + return campId; + +} + module.exports = { AddNode, - AddScenario + AddScenario, + Camp }; \ No newline at end of file diff --git a/routers/campRouter.js b/routers/campRouter.js index e29ce14..1d7879e 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -2,6 +2,52 @@ const express = require('express'); const campRouter = express.Router(); const { isAuth } = require('../middleware/authentication'); const dbData = require('../database/dbData'); +const dbTransactions = require('../database/dbTransactions'); +const dbPosts = require('../database/dbPosts'); +const balancing = require('../appInfo/balancing'); +const { ValidateChars } = require('../middleware/validation'); + +const CreateCamp = async (req, res, next) => { + + let transactionInitiated = false; + + try { + + const { userId } = req; + const { title, description, scenario } = req.query; + const balanceSheet = balancing.numbers; + + //ERROR CHECKS + if (!title) throw new Error('Please provide a title'); + if (title.length < balanceSheet.titleMinChars) throw new Error('Title is too short. Minimum chars: ', titleMinChars); + if (title.length > balanceSheet.titleMaxChars) throw new Error('Title is too long. Maximum chars: ', titleMaxChars); + if (!description) throw new Error('Please provide a description'); + if (description.length < balanceSheet.descriptionMinChars) throw new Error('Description is too short. Minimum chars: ', descriptionMinChars); + if (description.length > balanceSheet.descriptionMaxChars) throw new Error('Description is too long. Maximum chars: ', descriptionMaxChars); + if (!scenario) throw new Error('Please provide a starting scenario'); + if (scenario.length < balanceSheet.scenarioMinCharacter) throw new Error('Scenario is too short. Minimum chars: ', scenarioMinCharacter); + if (scenario.length > balanceSheet.scenarioMaxCharacters) throw new Error('Scenario is too long. Maximum chars: ', scenarioMaxCharacters); + ValidateChars(title); + ValidateChars(description); + ValidateChars(scenario); + + //TRY ADD TO DATABASE + await dbTransactions.Begin(); + transactionInitiated = true; + await dbPosts.Camp(title, description, scenario, userId); + //remove room key + + await dbTransactions.Commit(); + + // req.responseMessage = { success: true, message: 'new room added!', roomId: newRoomId }; + } + catch (error) { + + + + } + +} const GetCampData = async (req, res, next) => { From 8df982e0e5d84f6455b26f872f19dc96002c6e92 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Feb 2023 13:03:05 -0300 Subject: [PATCH 102/127] finalized camp post route --- database/dbPosts.js | 19 +++++++++++++++---- database/dbUpdates.js | 31 +++++++++++++++++++++++++++++++ routers/campRouter.js | 31 +++++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 database/dbUpdates.js diff --git a/database/dbPosts.js b/database/dbPosts.js index 8058941..89f7c3d 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -3,7 +3,7 @@ const db = require('./dbConnect.js'); const dbData = require('./dbData'); //NODES -async function AddNode(campId, userId) { +async function Node(campId, userId) { const q = await db.query( ` @@ -77,7 +77,7 @@ async function AddNode(campId, userId) { } -async function AddScenario(campId, text, isEnd) { +async function Scenario(campId, text, isEnd) { const lastNode = await dbData.LastNodeInCamp(campId); const lastNodeId = lastNode.node_id; @@ -138,6 +138,17 @@ async function AddScenario(campId, text, isEnd) { //CAMPS async function Camp(title, description, scenario, creator_id) { + const campTitleQ = await db.query( + ` + SELECT id + FROM camps + WHERE title = $1 + `, + [title] + ); + + if (campTitleQ.rowCount > 0) throw new Error('Story title already exists.'); + const campQ = await db.query( ` WITH new_camp AS( @@ -162,7 +173,7 @@ async function Camp(title, description, scenario, creator_id) { } module.exports = { - AddNode, - AddScenario, + AddNode: Node, + AddScenario: Scenario, Camp }; \ No newline at end of file diff --git a/database/dbUpdates.js b/database/dbUpdates.js new file mode 100644 index 0000000..39df7e2 --- /dev/null +++ b/database/dbUpdates.js @@ -0,0 +1,31 @@ +//Update rows in the database +const db = require('./dbConnect.js'); + +//users +async function RemoveLog(userId) { + + const keyQ = await db.query( + ` + SELECT room_keys + FROM users + WHERE id = $1 + `, + [userId] + ); + + if (keyQ.rows[0].room_keys == 0) throw new Error('User has no logs left!'); + + await db.query( + ` + UPDATE users + SET room_keys = room_keys - 1 + WHERE id = $1 + `, + [userId] + ); + +} + +module.exports = { + RemoveLog, +} \ No newline at end of file diff --git a/routers/campRouter.js b/routers/campRouter.js index 1d7879e..4a63816 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -1,12 +1,18 @@ +//IMPORTS const express = require('express'); const campRouter = express.Router(); + const { isAuth } = require('../middleware/authentication'); +const { ValidateChars } = require('../middleware/validation'); + const dbData = require('../database/dbData'); const dbTransactions = require('../database/dbTransactions'); const dbPosts = require('../database/dbPosts'); +const dbUpdates = require('../database/dbUpdates'); + const balancing = require('../appInfo/balancing'); -const { ValidateChars } = require('../middleware/validation'); +//FUNCTIONS const CreateCamp = async (req, res, next) => { let transactionInitiated = false; @@ -34,16 +40,26 @@ const CreateCamp = async (req, res, next) => { //TRY ADD TO DATABASE await dbTransactions.Begin(); transactionInitiated = true; - await dbPosts.Camp(title, description, scenario, userId); - //remove room key - + await dbUpdates.RemoveLog(userId); + const campId = await dbPosts.Camp(title, description, scenario, userId); await dbTransactions.Commit(); - // req.responseMessage = { success: true, message: 'new room added!', roomId: newRoomId }; + res.status(200).send({ + ok: true, + message: 'New camp created successfully!', + campId: campId + }); + } catch (error) { - + if (transactionInitiated) dbTransactions.Rollback(); + const message = 'Failed to create camp: ' + error.message; + console.error(message); + res.status(400).send({ + ok: false, + message: message + }); } @@ -120,9 +136,12 @@ const GetPlayerCamps = async (req, res, next) => { } +//ROUTES campRouter.use(isAuth); campRouter.get('/data/:id', GetCampData); campRouter.get('/active', GetActiveCamps); campRouter.get('/player', GetPlayerCamps); +campRouter.post('/', CreateCamp); +//ROUTER EXPORT module.exports = campRouter; \ No newline at end of file From a6a9de9095585c31dd1e2192000e3a960fc90223 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Feb 2023 13:05:02 -0300 Subject: [PATCH 103/127] camp routes done --- routers/roomRouter.js | 1 + routers/scenarioRouter.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/routers/roomRouter.js b/routers/roomRouter.js index 54f77a7..498731c 100644 --- a/routers/roomRouter.js +++ b/routers/roomRouter.js @@ -1,3 +1,4 @@ +//LEGACY - will be replaced by camp router on update const express = require('express'); const roomRouter = express.Router(); const db = require('../database/dbConnect.js'); diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 0320218..27bfc3d 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -1,3 +1,5 @@ +//LEGACY - will be replaced by node router on update + const express = require('express'); const scenarioRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); From 1f72f4fb22239b01ed32484109f7c01cee6d0045 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Feb 2023 13:26:57 -0300 Subject: [PATCH 104/127] get stats route updated for new node db structure --- database/dbData.js | 34 +++++++++++++++++++++++++++++++++- routers/userRouter.js | 12 ++++-------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index 7ef3492..a4fbf20 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -150,6 +150,37 @@ async function PlayerCamps(userId) { return campQuery.rows; } +async function PlayerStats(userId) { + + const q = await db.query( + ` + SELECT + COUNT(DISTINCT camps.id) AS camps, + SUM (CASE + WHEN camps.finished = TRUE THEN 1 + ELSE 0 + END + ) AS finished, + COUNT(DISTINCT nodes_0.id) AS nodes_0, + ( + SELECT COUNT(*) + FROM users + WHERE id = $1 + ) AS user_exist + FROM nodes_0 + JOIN camps ON camps.id = nodes_0.camp_id + WHERE nodes_0.creator_id = $1; + `, + [userId] + ); + + const stats = q.rows[0]; + + if (stats.user_exist == 0) throw new Error('No user with that ID exists'); + + return stats; + +} //helpers @@ -184,5 +215,6 @@ module.exports = { PlayersInCamp, ScenariosInCamp, ActiveCamps, - PlayerCamps + PlayerCamps, + PlayerStats }; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index fbae24a..9282ff9 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,9 +1,9 @@ const express = require('express'); const userRouter = express.Router(); -const dbFunctions = require('../database/dbFunctions'); +const dbFunctions = require('../database/dbFunctions'); //should be replaced +const dbData = require('../database/dbData'); const { isAuth, Login } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); -// const { makeid } = require('../helpers/generateString'); const AddNewUser = async (req, res, next) => { @@ -56,11 +56,7 @@ const GetUserStats = async (req, res, next) => { const { userId } = req.query; if (!userId) throw new Error('no user id provided in query. Cannot return stats'); - const stats = await dbFunctions.GetPlayerStats(userId); - - console.log(stats); - - if (!stats) throw new Error('found no user with that id'); + const stats = await dbData.PlayerStats(userId); res.status(200).send({ ok: true, @@ -73,7 +69,7 @@ const GetUserStats = async (req, res, next) => { res.status(400).send({ ok: false, - message: error.message, + message: 'Could not retrieve stats: ' + error.message, }) } From 6fe3701dea44d66ce5c47c62341ac2e7e1279d20 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Feb 2023 13:51:36 -0300 Subject: [PATCH 105/127] old room function fix --- database/dbFunctions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/dbFunctions.js b/database/dbFunctions.js index 3eaee2e..50ddde9 100644 --- a/database/dbFunctions.js +++ b/database/dbFunctions.js @@ -373,7 +373,8 @@ async function CreateNewNode(roomId) { VALUES ( (SELECT id FROM new_node), (SELECT prompt FROM prompts ORDER BY random() LIMIT 1) - );`, + ) + RETURNING scenarios.node_id;`, [roomId] ); From 76527d4f80f24c54e6e9438bdd0c34e9aacb8983 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Thu, 9 Feb 2023 12:26:16 -0300 Subject: [PATCH 106/127] added new archive route --- database/dbData.js | 87 +++++++++++++++++++++++++++++-------------- routers/campRouter.js | 24 ++++++++++++ 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index a4fbf20..a88f05a 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -1,7 +1,9 @@ const db = require('./dbConnect.js'); const balancing = require('../appInfo/balancing'); -//request to app +//REQUEST TO APP + +//menu content async function Feed() { const q = await db.query( @@ -25,6 +27,39 @@ async function Feed() { return q.rows; } +async function PlayerStats(userId) { + + const q = await db.query( + ` + SELECT + COUNT(DISTINCT camps.id) AS camps, + SUM (CASE + WHEN camps.finished = TRUE THEN 1 + ELSE 0 + END + ) AS finished, + COUNT(DISTINCT nodes_0.id) AS nodes_0, + ( + SELECT COUNT(*) + FROM users + WHERE id = $1 + ) AS user_exist + FROM nodes_0 + JOIN camps ON camps.id = nodes_0.camp_id + WHERE nodes_0.creator_id = $1; + `, + [userId] + ); + + const stats = q.rows[0]; + + if (stats.user_exist == 0) throw new Error('No user with that ID exists'); + + return stats; + +} + +//camp data async function CampData(campId) { const campQ = await db.query( @@ -84,6 +119,8 @@ async function ScenariosInCamp(campId) { return scenarioQ.rows; } + +//camps async function ActiveCamps(userId) { const campQuery = await db.query( @@ -150,40 +187,35 @@ async function PlayerCamps(userId) { return campQuery.rows; } -async function PlayerStats(userId) { +async function FinishedStories() { - const q = await db.query( + const campQuery = await db.query( ` SELECT - COUNT(DISTINCT camps.id) AS camps, - SUM (CASE - WHEN camps.finished = TRUE THEN 1 - ELSE 0 - END - ) AS finished, - COUNT(DISTINCT nodes_0.id) AS nodes_0, - ( - SELECT COUNT(*) - FROM users - WHERE id = $1 - ) AS user_exist - FROM nodes_0 - JOIN camps ON camps.id = nodes_0.camp_id - WHERE nodes_0.creator_id = $1; - `, - [userId] + camps.id, + camps.title, + camps.description, + users.name AS creator_name, + COUNT(DISTINCT nodes_0.id) AS node_count, + COUNT(DISTINCT nodes_0.creator_id) AS contributor_count, + camps.created_at + FROM camps + JOIN users ON users.id = camps.creator_id + JOIN nodes_0 ON nodes_0.camp_id = camps.id + WHERE finished = 'true' + GROUP BY camps.id, users.name, camps.title, camps.description + ORDER BY created_at + ; + ` ); - const stats = q.rows[0]; - - if (stats.user_exist == 0) throw new Error('No user with that ID exists'); - - return stats; + return campQuery.rows; } -//helpers + +//HELPERS async function LastNodeInCamp(campId) { const last_node_q = await db.query( @@ -216,5 +248,6 @@ module.exports = { ScenariosInCamp, ActiveCamps, PlayerCamps, - PlayerStats + PlayerStats, + FinishedStories }; \ No newline at end of file diff --git a/routers/campRouter.js b/routers/campRouter.js index 4a63816..6d203b0 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -136,11 +136,35 @@ const GetPlayerCamps = async (req, res, next) => { } +const GetFinishedStories = async (req, res, next) => { + + try { + + const camps = await dbData.FinishedStories(); + res.status(200).send({ + ok: true, + message: 'found camps', + data: camps + }); + + } + catch (error) { + + console.error(error); + res.status(400).send('Failed to get active camps: ' + error.message); + + } + +} + + + //ROUTES campRouter.use(isAuth); campRouter.get('/data/:id', GetCampData); campRouter.get('/active', GetActiveCamps); campRouter.get('/player', GetPlayerCamps); +campRouter.get('/finished', GetFinishedStories); campRouter.post('/', CreateCamp); //ROUTER EXPORT From 0eabd3b2591976f3a3bc1539b932532a5a709736 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 10 Feb 2023 13:28:38 -0300 Subject: [PATCH 107/127] bugs in node posting order --- database/dbChecks.js | 31 ++++++++++++++++++++----------- database/dbData.js | 4 +++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/database/dbChecks.js b/database/dbChecks.js index 0842cef..2a485a0 100644 --- a/database/dbChecks.js +++ b/database/dbChecks.js @@ -4,31 +4,40 @@ const db = require('./dbConnect.js'); //ERROR CHECKS async function CanAddNode(campId, userId) { - const q = await db.query( + const lastFinishedNodeQ = await db.query( ` SELECT camps.finished, - nodes_0.creator_id AS last_node_creator_id, - nodes_0.created_at AS last_node_posted_at, - nodes_0.finished_at AS last_node_finished_at + nodes_0.creator_id AS last_scenario_creator_id FROM camps JOIN nodes_0 on nodes_0.camp_id = camps.id WHERE camps.id = $1 + AND nodes_0.finished_at IS NOT NULL ORDER BY nodes_0.id DESC LIMIT 1; `, [campId] ); - if (q.rowCount == 0) throw new Error('there is no camp with that id'); - - const { finished, last_node_creator_id, last_node_posted_at, last_node_finished_at } = q.rows[0]; + if (lastFinishedNodeQ.rowCount == 0) throw new Error('there is no camp with that id'); + const { finished, last_scenario_creator_id } = lastFinishedNodeQ.rows[0]; + if (finished) throw new Error('Cant add node, story is already finished'); + if (last_scenario_creator_id == userId) throw new Error('Cant add node, because user added the last scenario'); - console.log('user id is ', userId); - console.log('last node added by: ', last_node_creator_id); + const lastNodeQ = await db.query( + ` + SELECT + nodes_0.created_at AS last_node_posted_at, + nodes_0.finished_at AS last_node_finished_at + FROM nodes_0 + WHERE nodes_0.camp_id = $1 + ORDER BY nodes_0.id DESC + LIMIT 1; + `, + [campId] + ); - if (finished) throw new Error('Cant add node, story is already finished'); - if (last_node_creator_id == userId) throw new Error('Cant add node, because user added the last one'); + const { last_node_posted_at, last_node_finished_at } = lastNodeQ.rows[0]; //check if someone else is writing if (last_node_finished_at) return; diff --git a/database/dbData.js b/database/dbData.js index a88f05a..eab9277 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -225,8 +225,10 @@ async function LastNodeInCamp(campId) { creator_id, created_at, finished_at, - users.name as creator_name + users.name as creator_name, + scenarios_0.prompt FROM nodes_0 + JOIN scenarios_0 ON scenarios_0.node_id = nodes_0.id JOIN users on users.id = nodes_0.creator_id WHERE camp_id = $1 ORDER BY nodes_0.id DESC From b776a883061e8680f616b3291730eed58fd37cdb Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 10 Feb 2023 14:50:44 -0300 Subject: [PATCH 108/127] fix player stats query --- database/dbData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/dbData.js b/database/dbData.js index eab9277..2072024 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -38,7 +38,7 @@ async function PlayerStats(userId) { ELSE 0 END ) AS finished, - COUNT(DISTINCT nodes_0.id) AS nodes_0, + COUNT(DISTINCT nodes_0.id) AS contributions, ( SELECT COUNT(*) FROM users From fc51054c032fe3def304f2ec38d5b4386450da4e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Mon, 13 Feb 2023 14:14:29 -0300 Subject: [PATCH 109/127] added route to retrieve latest news --- app.js | 12 +++++------- database/dbData.js | 18 +++++++++++++++++- routers/infoRouter.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 routers/infoRouter.js diff --git a/app.js b/app.js index 52f07a2..bda653e 100644 --- a/app.js +++ b/app.js @@ -16,22 +16,20 @@ if (process.env.NODE_ENV === 'development') { //import routers const roomRouter = require('./routers/roomRouter'); //legacy -const userRouter = require('./routers/userRouter'); const scenarioRouter = require('./routers/scenarioRouter'); //legacy + +const userRouter = require('./routers/userRouter'); const nodeRouter = require('./routers/nodeRouter'); const campRouter = require('./routers/campRouter'); +const infoRouter = require('./routers/infoRouter'); //mount routers app.use('/room', roomRouter); //legacy -app.use('/user', userRouter); app.use('/scenario', scenarioRouter); //legacy +app.use('/user', userRouter); app.use('/node', nodeRouter); app.use('/camp', campRouter); - -// //test rout -// app.get('/', async (req, res) => { -// res.send(`unwritten server is running on ${PORT} :)`); -// }) +app.use('/info', infoRouter); //start server app.listen(PORT, () => { diff --git a/database/dbData.js b/database/dbData.js index 2072024..a8eb02a 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -213,6 +213,21 @@ async function FinishedStories() { } +//info +async function LatestNews() { + + const campQuery = await db.query( + ` + SELECT message, author, created_at + FROM news + ORDER BY created_at DESC + LIMIT 1; + ` + ); + + return campQuery.rows[0]; + +} //HELPERS @@ -251,5 +266,6 @@ module.exports = { ActiveCamps, PlayerCamps, PlayerStats, - FinishedStories + FinishedStories, + LatestNews }; \ No newline at end of file diff --git a/routers/infoRouter.js b/routers/infoRouter.js new file mode 100644 index 0000000..865354a --- /dev/null +++ b/routers/infoRouter.js @@ -0,0 +1,31 @@ +const express = require('express'); +const infoRouter = express.Router(); +const dbData = require('../database/dbData'); + +const GetLatestNews = async (req, res, next) => { + + try { + + const news = await dbData.LatestNews(); + + res.status(200).send({ + ok: true, + message: 'successfully retrieved latest news', + data: news + }) + + } + catch (error) { + + res.status(400).send({ + ok: false, + message: 'Could not retrieve latest news: ' + error.message, + }) + + } + +} + +infoRouter.get('/news', GetLatestNews); + +module.exports = infoRouter; \ No newline at end of file From 83f1fd9303805e34900c9ffe6efe35ae1fce9b42 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 14 Feb 2023 12:31:09 -0300 Subject: [PATCH 110/127] new notifications when someone uploads a scenario --- database/dbData.js | 57 +++++++++++++++++++++++++- notifications/notifications.js | 75 ++++++++++++++++++++++++++++------ routers/nodeRouter.js | 8 +++- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index a8eb02a..e0bb761 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -1,3 +1,5 @@ +//warning, starting to get bloated AGAIN! can break into more scripts + const db = require('./dbConnect.js'); const balancing = require('../appInfo/balancing'); @@ -59,6 +61,22 @@ async function PlayerStats(userId) { } +//players +async function PlayerName(playerId) { + + const nameQ = await db.query( + ` + SELECT name + FROM users + WHERE id = $1; + `, + [playerId] + ) + + return nameQ.rows[0].name; + +} + //camp data async function CampData(campId) { @@ -118,6 +136,40 @@ async function ScenariosInCamp(campId) { return scenarioQ.rows; +} +async function GetCampPlayersExpoTokens(campId, playerExceptionId) { + + const tokenQ = await db.query( + ` + SELECT + users.expo_push_token + FROM nodes_0 + JOIN users ON creator_id = users.id + WHERE camp_id = $1 + AND finished_at > '2023-02-03T14:13:18.424666' + AND users.id != $2 + GROUP BY users.id; + `, + [campId, playerExceptionId] + ); + + const tokens = tokenQ.rows.map(row => row.expo_push_token); + return tokens; + +} +async function StoryTitle(campId) { + + const titleQ = await db.query( + ` + SELECT title + FROM camps + WHERE id = $1; + `, + [campId] + ) + + return titleQ.rows[0].title; + } //camps @@ -267,5 +319,8 @@ module.exports = { PlayerCamps, PlayerStats, FinishedStories, - LatestNews + LatestNews, + GetCampPlayersExpoTokens, + PlayerName, + StoryTitle }; \ No newline at end of file diff --git a/notifications/notifications.js b/notifications/notifications.js index 03dcaef..6d5c86c 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -1,8 +1,9 @@ const { Expo } = require('expo-server-sdk'); -const dbFunctions = require('../database/dbFunctions'); +const dbData = require('../database/dbData'); let expo = new Expo(); +//ACTIVE const SendNotification = async (pushToken, title, body, data) => { if (!Expo.isExpoPushToken(pushToken)) { @@ -29,6 +30,60 @@ const SendNotification = async (pushToken, title, body, data) => { } +const SendTestNotification = () => { + + SendNotification( + 'ExponentPushToken[ZeNN1xHXaxNE3Nl0NBQxMT]', + 'Scheduled notification', + 'Hello smoggy! you should receive this notification when heroku runs its script :)', + {} + ) + +} + +const SendScenarioNotifications = async (campId, creatorId, creatorName, storyTitle) => { + + //get the expo tokens for the given camp id EXCEPT for the poster + const tokens = await dbData.GetCampPlayersExpoTokens(campId, creatorId); + + //create the messages + let messages = []; + for (let token of tokens) { + + if (!Expo.isExpoPushToken(token)) { + console.error(`Push token ${token} is not a valid Expo push token`); + continue; + } + + messages.push({ + to: token, + sound: 'default', + title: `${creatorName} updated "${storyTitle}"!`, + body: 'Jump into Unwritten to keep writing', + data: { + type: 'scenario', + roomId: campId, + }, + }) + } + + //chunk and send them + const chunks = expo.chunkPushNotifications(messages); + let tickets = []; + (async () => { + for (let chunk of chunks) { + try { + let ticketChunk = await expo.sendPushNotificationsAsync(chunk); + tickets.push(...ticketChunk); + } catch (error) { + console.error(error); + } + } + })(); + +} + +//LEGACY const SendTurnNotification = (pushToken, roomId, storyTitle, userId) => { SendNotification( @@ -72,15 +127,11 @@ const SendKickNotification = async (pushToken, storyTitle) => { } -const SendTestNotification = () => { - - SendNotification( - 'ExponentPushToken[ZeNN1xHXaxNE3Nl0NBQxMT]', - 'Scheduled notification', - 'Hello smoggy! you should receive this notification when heroku runs its script :)', - {} - ) - -} -module.exports = { SendTurnNotification, SendStrikeNotification, SendKickNotification, SendTestNotification }; \ No newline at end of file +module.exports = { + SendTurnNotification, + SendStrikeNotification, + SendKickNotification, + SendTestNotification, + SendScenarioNotifications +}; \ No newline at end of file diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index b26aeaf..1336c0f 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -9,6 +9,7 @@ const dbData = require('../database/dbData'); const { isAuth } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); const balancing = require('../appInfo/balancing'); +const notifications = require('../notifications/notifications'); const GetFeed = async (req, res, next) => { @@ -94,7 +95,7 @@ const TryAddScenario = async (req, res, next) => { if (text.length < balancing.numbers.scenarioMinCharacter) throw new Error('min chars in scenario: ', scenarioMinCharacters); if (text.length > balancing.numbers.scenarioMaxCharacters) throw new Error('max chars in scenario: ', scenarioMaxCharacters); if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); - await dbChecks.CanAddScenario(campId, userId, isEnd); + await dbChecks.CanAddScenario(campId, userId, isEnd); //transaction await dbTransactions.Begin(); @@ -102,6 +103,11 @@ const TryAddScenario = async (req, res, next) => { await dbPosts.AddScenario(campId, text, isEnd); await dbTransactions.Commit(); + //notify everyone in a story! + const creatorName = await dbData.PlayerName(userId); + const storyTitle = await dbData.StoryTitle(campId); + notifications.SendScenarioNotifications(campId, userId, creatorName, storyTitle); + //send response let responseMessage = 'new scenario added!'; if (isEnd) responseMessage = 'you ended the story! And got a log!'; From 8d0cbbdfd89d7c4d65f8e6930ee2092764cace6e Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 14 Feb 2023 13:17:50 -0300 Subject: [PATCH 111/127] tog bort dubblering av expo tokens vid push --- database/dbData.js | 5 +++-- notifications/notifications.js | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index e0bb761..7145859 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -142,13 +142,14 @@ async function GetCampPlayersExpoTokens(campId, playerExceptionId) { const tokenQ = await db.query( ` SELECT - users.expo_push_token + users.expo_push_token FROM nodes_0 JOIN users ON creator_id = users.id WHERE camp_id = $1 AND finished_at > '2023-02-03T14:13:18.424666' AND users.id != $2 - GROUP BY users.id; + AND expo_push_token IS NOT NULL + GROUP BY users.id, users.expo_push_token; `, [campId, playerExceptionId] ); diff --git a/notifications/notifications.js b/notifications/notifications.js index 6d5c86c..718fe89 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -58,8 +58,7 @@ const SendScenarioNotifications = async (campId, creatorId, creatorName, storyTi messages.push({ to: token, sound: 'default', - title: `${creatorName} updated "${storyTitle}"!`, - body: 'Jump into Unwritten to keep writing', + body: `${creatorName} updated "${storyTitle}"!`, data: { type: 'scenario', roomId: campId, From bda162d893851aaed72c154c037e729d9c2b3fc4 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 15 Feb 2023 12:11:29 -0300 Subject: [PATCH 112/127] like/dislike routes added --- database/dbPosts.js | 36 ++++++++++++++++++++++++- routers/nodeRouter.js | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/database/dbPosts.js b/database/dbPosts.js index 89f7c3d..66bd363 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -135,6 +135,38 @@ async function Scenario(campId, text, isEnd) { } +//LIKES +async function Like(nodeId, userId) { + + const likeQ = await db.query( + ` + INSERT INTO likes (node_id, user_id) + VALUES ($1, $2) + ON CONFLICT DO NOTHING; + `, + [nodeId, userId] + ); + + if (likeQ.rowCount == 0) throw Error('user already liked that node'); + return; + +} +async function Dislike(nodeId, userId) { + + const dislikeQ = await db.query( + ` + DELETE FROM likes + WHERE node_id = $1 + AND user_id = $2 + `, + [nodeId, userId] + ); + + if (dislikeQ.rowCount == 0) throw Error('user doesnt like that node'); + return; + +} + //CAMPS async function Camp(title, description, scenario, creator_id) { @@ -175,5 +207,7 @@ async function Camp(title, description, scenario, creator_id) { module.exports = { AddNode: Node, AddScenario: Scenario, - Camp + Camp, + Like, + Dislike }; \ No newline at end of file diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index 1336c0f..dca1a55 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -121,9 +121,71 @@ const TryAddScenario = async (req, res, next) => { } +const Like = async (req, res, next) => { + + try { + //params + const userId = req.userId; + const { nodeId } = req.query + + //checks + if (!nodeId) throw new Error('No nodeId provided'); + if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); + + //transaction + await dbPosts.Like(nodeId, userId); + + //send response + res.send({ + ok: true, + message: 'liked node successfully' + }); + } + catch (error) { + console.error(error); + res.status(400).send({ + ok: false, + message: ('unable to add like: ' + error.message) + }); + } + +} + +const Dislike = async (req, res, next) => { + + try { + //params + const userId = req.userId; + const { nodeId } = req.query + + //checks + if (!nodeId) throw new Error('No nodeId provided'); + if (!userId) throw new Error('no userId. Make sure you have a valid token and are logged correctly'); + + //transaction + await dbPosts.Dislike(nodeId, userId); + + //send response + res.send({ + ok: true, + message: 'removed like successfully' + }); + } + catch (error) { + console.error(error); + res.status(400).send({ + ok: false, + message: ('unable to remove like: ' + error.message) + }); + } + +} + nodeRouter.use(isAuth); nodeRouter.post('/', TryAddNode); nodeRouter.post('/scenario', TryAddScenario); +nodeRouter.post('/like', Like); +nodeRouter.post('/dislike', Dislike); nodeRouter.get('/feed', GetFeed); module.exports = nodeRouter; \ No newline at end of file From a9825b46676811d286ccbc6a0b16f60c44030b73 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 15 Feb 2023 13:37:09 -0300 Subject: [PATCH 113/127] get node likes with camp data --- database/dbData.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/database/dbData.js b/database/dbData.js index 7145859..924bd99 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -116,9 +116,12 @@ async function PlayersInCamp(campId) { } async function ScenariosInCamp(campId) { + console.log('starting scenario q'); + const scenarioQ = await db.query( ` SELECT + nodes_0.id as node_id, scenario, creator_id, prompt @@ -133,10 +136,32 @@ async function ScenariosInCamp(campId) { ); if (scenarioQ.rowCount < 1) throw new Error('no scenarios in the camp witht that id'); + const scenarios = scenarioQ.rows; - return scenarioQ.rows; + const likesQ = await db.query( + ` + SELECT node_id, user_id, name + FROM likes + JOIN nodes_0 ON likes.node_id = nodes_0.id + JOIN users on users.id = user_id + WHERE nodes_0.camp_id = $1; + `, + [campId] + ); + + const likes = likesQ.rows; + + scenarios.forEach(scenario => { + const scenarioLikes = likes.filter(like => (like.node_id == scenario.node_id)); + scenario.likes = scenarioLikes; + }); + + return scenarios; } + +ScenariosInCamp(60); + async function GetCampPlayersExpoTokens(campId, playerExceptionId) { const tokenQ = await db.query( From fa8e8a97070adaa705e0d03deb56c054fbfff229 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 17 Feb 2023 15:01:03 -0300 Subject: [PATCH 114/127] new login route using google token --- database/dbData.js | 18 ++++- database/dbPosts.js | 52 +++++++++++++- middleware/authentication.js | 40 +++++------ package-lock.json | 26 +++---- package.json | 2 +- routers/userRouter.js | 131 ++++++++++++++++++++++++++++------- 6 files changed, 208 insertions(+), 61 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index 924bd99..882af7f 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -75,6 +75,21 @@ async function PlayerName(playerId) { return nameQ.rows[0].name; +} +async function Player(googleId) { + + const playerQ = await db.query( + ` + SELECT * + FROM users + WHERE google_id = $1 + `, + [googleId] + ); + + if (playerQ.rowCount < 1) return null; + else return playerQ.rows[0]; + } //camp data @@ -160,8 +175,6 @@ async function ScenariosInCamp(campId) { } -ScenariosInCamp(60); - async function GetCampPlayersExpoTokens(campId, playerExceptionId) { const tokenQ = await db.query( @@ -348,5 +361,6 @@ module.exports = { LatestNews, GetCampPlayersExpoTokens, PlayerName, + Player, StoryTitle }; \ No newline at end of file diff --git a/database/dbPosts.js b/database/dbPosts.js index 66bd363..ac94086 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -2,6 +2,54 @@ const db = require('./dbConnect.js'); const dbData = require('./dbData'); +//PLAYERS +async function NewPlayer(googleId, googleToken) { + + const addPlayerQ = await db.query( + ` + INSERT INTO users (google_id, google_token) + VALUES ($1, $2) + RETURNING * + `, + [googleId, googleToken] + ); + + if (addPlayerQ.rowCount > 0) { + const player = addPlayerQ.rows[0]; + console.log('added new player with id: ', player.id); + return player; + } + else { + console.error('failed to add new player. query returned null'); + return null; + } + +} + +async function UpdateGoogleToken(googleId, googleToken) { + + const updatePlayerQ = await db.query( + ` + UPDATE users + SET google_token = $1 + WHERE google_id = $2 + RETURNING * + `, + [googleToken, googleId] + ); + + if (updatePlayerQ.rowCount > 0) { + const player = updatePlayerQ.rows[0]; + console.log('updatet google token for player with id: ', player.id); + return player; + } + else { + console.error('failed to update google token for player. query returned null'); + return null; + }; + +} + //NODES async function Node(campId, userId) { @@ -209,5 +257,7 @@ module.exports = { AddScenario: Scenario, Camp, Like, - Dislike + Dislike, + NewPlayer, + UpdateGoogleToken }; \ No newline at end of file diff --git a/middleware/authentication.js b/middleware/authentication.js index a464a6f..6bd34c5 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -24,32 +24,32 @@ const isAuth = async (req, res, next) => { } -const Login = async (req, res, next) => { - try { +// const Login = async (req, res, next) => { +// try { - const email = req.query.email; - const password = req.query.password; +// const email = req.query.email; +// const password = req.query.password; - if (!email) throw new Error('no email provided'); - if (!password) throw new Error('no password provided'); - ValidateChars(email); - ValidateChars(password); +// if (!email) throw new Error('no email provided'); +// if (!password) throw new Error('no password provided'); +// ValidateChars(email); +// ValidateChars(password); - const user = await dbFunctions.Login(email, password); +// const user = await dbFunctions.Login(email, password); - const token = jwt.sign( - { userId: user.id }, - process.env.JWT_SECRET, - { expiresIn: process.env.JWT_EXPIRES_IN } - ); +// const token = jwt.sign( +// { userId: user.id }, +// process.env.JWT_SECRET, +// { expiresIn: process.env.JWT_EXPIRES_IN } +// ); - res.status(201).send({ message: 'logged in as ' + user.name, token: token }); +// res.status(201).send({ message: 'logged in as ' + user.name, token: token }); - } catch (error) { +// } catch (error) { - res.status(400).send('Cant login: ' + error.message); +// res.status(400).send('Cant login: ' + error.message); - } -} +// } +// } -module.exports = { isAuth, Login }; \ No newline at end of file +module.exports = { isAuth }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f55ebfd..bd526f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-cron": "^3.0.2", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "node-schedule": "^2.1.0", "nodemailer": "^6.8.0", "passport": "^0.6.0", @@ -885,9 +885,9 @@ } }, "node_modules/luxon": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", - "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", + "version": "1.28.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", + "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", "engines": { "node": "*" } @@ -1055,9 +1055,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2405,9 +2405,9 @@ } }, "luxon": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", - "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" + "version": "1.28.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", + "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==" }, "make-dir": { "version": "3.1.0", @@ -2523,9 +2523,9 @@ } }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "requires": { "whatwg-url": "^5.0.0" } diff --git a/package.json b/package.json index eb260b9..0210f0f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", "node-cron": "^3.0.2", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "node-schedule": "^2.1.0", "nodemailer": "^6.8.0", "passport": "^0.6.0", diff --git a/routers/userRouter.js b/routers/userRouter.js index 9282ff9..1a06773 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -2,37 +2,39 @@ const express = require('express'); const userRouter = express.Router(); const dbFunctions = require('../database/dbFunctions'); //should be replaced const dbData = require('../database/dbData'); -const { isAuth, Login } = require('../middleware/authentication'); +const dbPosts = require('../database/dbPosts'); +const { isAuth } = require('../middleware/authentication'); const { ValidateChars } = require('../middleware/validation'); +const fetch = require('node-fetch'); -const AddNewUser = async (req, res, next) => { +// const AddNewUser = async (req, res, next) => { - try { - const { name, email, password, pushToken } = req.query; +// try { +// const { name, email, password, pushToken } = req.query; - if (!exists(email)) throw new Error('No email provided') - if (!exists(password)) throw new Error('No password provided') - if (password.length < 6) throw new Error('Password must be at least 6 characters') - if (!exists(name)) throw new Error('No name provided') - if (name.length < 4) throw new Error('Name must be at least 4 characters') - if (name.length > 20) throw new Error('Name must be max 20 characters') +// if (!exists(email)) throw new Error('No email provided') +// if (!exists(password)) throw new Error('No password provided') +// if (password.length < 6) throw new Error('Password must be at least 6 characters') +// if (!exists(name)) throw new Error('No name provided') +// if (name.length < 4) throw new Error('Name must be at least 4 characters') +// if (name.length > 20) throw new Error('Name must be max 20 characters') - ValidateChars(email); - ValidateChars(password); - ValidateChars(name); +// ValidateChars(email); +// ValidateChars(password); +// ValidateChars(name); - await dbFunctions.CreateUser(name, email, password, pushToken); +// await dbFunctions.CreateUser(name, email, password, pushToken); - next(); +// next(); - } - catch (error) { +// } +// catch (error) { - res.status(400).send({ ok: false, message: error.message }); +// res.status(400).send({ ok: false, message: error.message }); - } +// } -} +// } const GetUserInfo = async (req, res, next) => { try { @@ -75,15 +77,96 @@ const GetUserStats = async (req, res, next) => { } } +const Login = async (req, res, next) => { + try { + + const googleToken = req.headers['authorization']; + if (!googleToken) throw new Error('no bearer token in auth header'); + + // console.log('trying to log in with auth token: ', googleToken); + + //fetch user from google using the token + let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { + headers: { Authorization: `Bearer ${googleToken}` } + }); + const userInfo = await response.json(); + // console.log('fetched user and got info: ', userInfo); + + if (userInfo.error) { + console.error('user tried to log in with invalid token. Error: ', userInfo.error); + throw new Error('Invalid google token'); + } + + //token is valid! and we got some info from the google api + const googleId = userInfo.id; + + //use it to get player from the db + let player = await dbData.Player(googleId); + + //correct anything that is missing in the user db + if (!player) { + player = await dbPosts.NewPlayer(googleId, googleToken); + if (!player) throw new Error('unable to add new player to the database'); + } + else if (player.google_token != googleToken) { + player = await dbPosts.UpdateGoogleToken(googleId, googleToken); + if (!player) throw new Error('unable to update google token in database'); + } + + console.log('player logged in. ID: ', player.id); + + //and return the player! + res.status(201).send({ + ok: true, + message: 'succesfully logged in!', + data: { + player: { + id: player.id, + displayName: player.name, + keys: player.room_keys, + } + } + }); + + } + catch (error) { + res.status(400).send({ + ok: false, + message: 'Cant login: ' + error.message, + }); + + } +} userRouter.get('/', isAuth, GetUserInfo); userRouter.get('/stats', GetUserStats); -userRouter.post('/create', AddNewUser, Login); +// userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); module.exports = userRouter; //HELPERS -const exists = text => { - return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); -} \ No newline at end of file +// const exists = text => { +// return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); +// } + + + + + +//TEST CODE HERE! + +const testFunc = async () => { + + // let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { + // headers: { Authorization: `Bearer ${'faketokenoanfoa'}` } + // }); + // const userInfo = await response.json(); + + // if (userInfo.error) { + // console.log('cant fetch user, invalid token!'); + // } + +} + +testFunc(); \ No newline at end of file From 7eeaa9f16a538ff98a5e482da9c08cb308bbf500 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 17 Feb 2023 15:42:55 -0300 Subject: [PATCH 115/127] added route to set display name --- database/dbPosts.js | 34 ++++++++++++++++++++++++++++++++-- middleware/validation.js | 21 ++++++++++++++++++--- routers/userRouter.js | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/database/dbPosts.js b/database/dbPosts.js index ac94086..d186af0 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -25,7 +25,6 @@ async function NewPlayer(googleId, googleToken) { } } - async function UpdateGoogleToken(googleId, googleToken) { const updatePlayerQ = await db.query( @@ -48,6 +47,36 @@ async function UpdateGoogleToken(googleId, googleToken) { return null; }; +} +async function Name(googleToken, name) { + + //Check if the name is already taken + const nameExistsQ = await db.query( + ` + SELECT google_token + FROM users + WHERE name = $1; + `, + [name] + ); + + if (nameExistsQ.rowCount > 0) { + if (nameExistsQ.rows[0].google_token == googleToken) throw new Error('User already has that name'); + else throw new Error('Name is already taken'); + } + + //if not, set it! + const nameUpdateQ = await db.query( + ` + UPDATE users + SET name = $1 + WHERE google_token = $2 + RETURNING * + `, + [name, googleToken] + ) + if (nameUpdateQ.rowCount == 0) throw new Error('found no user with that token'); + } //NODES @@ -259,5 +288,6 @@ module.exports = { Like, Dislike, NewPlayer, - UpdateGoogleToken + UpdateGoogleToken, + Name }; \ No newline at end of file diff --git a/middleware/validation.js b/middleware/validation.js index 5c31767..1c05863 100644 --- a/middleware/validation.js +++ b/middleware/validation.js @@ -1,12 +1,27 @@ -function ValidateChars(text){ +function ValidateCharsNoEmojis(text) { const arr = text.split(""); arr.forEach(char => { - if (!CharAllowed(char)){ + if (!CharAllowedNoEmoji(char)) { throw new Error(`The following character is not allowed: ${char}`); } }); } +function ValidateChars(text) { + const arr = text.split(""); + arr.forEach(char => { + if (!CharAllowed(char)) { + throw new Error(`The following character is not allowed: ${char}`); + } + }); +} + +function CharAllowedNoEmoji(char) { + const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) @ # % / ! ? * = ½ -]/; + const allowed = re.test(char); + return allowed; +} + function CharAllowed(char) { const re = /[ A-Z a-z 0-9 . , ; : ' " ( ) @ # % / ! ? * = ½ \u00a9 \u00ae \u2000-\u3300 \ud83c \ud000-\udfff \ud83e \ud000-\udfff -]/; const allowed = re.test(char); @@ -19,4 +34,4 @@ function CharsAllowed(str) { return allowed; } -module.exports = { CharsAllowed, ValidateChars }; \ No newline at end of file +module.exports = { CharsAllowed, ValidateChars, ValidateCharsNoEmojis }; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 1a06773..6150fc5 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -4,7 +4,7 @@ const dbFunctions = require('../database/dbFunctions'); //should be replaced const dbData = require('../database/dbData'); const dbPosts = require('../database/dbPosts'); const { isAuth } = require('../middleware/authentication'); -const { ValidateChars } = require('../middleware/validation'); +const { ValidateChars, ValidateCharsNoEmojis } = require('../middleware/validation'); const fetch = require('node-fetch'); // const AddNewUser = async (req, res, next) => { @@ -137,11 +137,47 @@ const Login = async (req, res, next) => { } } +const SetDisplayName = async (req, res, next) => { + + try { + + const { name } = req.query; + const googleToken = req.headers['authorization']; + + //checks + if (name.length < 3) throw new Error('Name must be at least 4 chars long'); + ValidateCharsNoEmojis(name); + + //post it + await dbPosts.Name(googleToken, name); + + //response + res.status(201).send({ + ok: true, + message: 'succesfully updated name!', + data: { + name: name + } + }); + + } + catch (error) { + + //fail response + res.status(400).send({ + ok: false, + message: 'Cant update name: ' + error.message, + }); + + } + +} userRouter.get('/', isAuth, GetUserInfo); userRouter.get('/stats', GetUserStats); // userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); +userRouter.post('/name', SetDisplayName); module.exports = userRouter; @@ -155,7 +191,6 @@ module.exports = userRouter; //TEST CODE HERE! - const testFunc = async () => { // let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { From fd8a9cfa5dda9f68205c508f7e56faa6fd4a6af8 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 21 Feb 2023 12:52:06 -0300 Subject: [PATCH 116/127] route for updating expo token --- database/dbPosts.js | 20 +++++++++++- notifications/notifications.js | 17 +++++++--- routers/userRouter.js | 57 ++++++++++++++++++++++++++++------ 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/database/dbPosts.js b/database/dbPosts.js index d186af0..ce86245 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -1,6 +1,7 @@ //POST STUFF TO THE DATABASE const db = require('./dbConnect.js'); const dbData = require('./dbData'); +const notifications = require('../notifications/notifications'); //PLAYERS async function NewPlayer(googleId, googleToken) { @@ -77,6 +78,22 @@ async function Name(googleToken, name) { ) if (nameUpdateQ.rowCount == 0) throw new Error('found no user with that token'); +} +async function ExpoToken(googleToken, expoToken) { + + notifications.IsExpoToken(expoToken); + + //if not, set it! + const tokenUpdateQ = await db.query( + ` + UPDATE users + SET expo_push_token = $1 + WHERE google_token = $2 + `, + [expoToken, googleToken] + ) + if (tokenUpdateQ.rowCount == 0) throw new Error('found no user with that google token'); + } //NODES @@ -289,5 +306,6 @@ module.exports = { Dislike, NewPlayer, UpdateGoogleToken, - Name + Name, + ExpoToken }; \ No newline at end of file diff --git a/notifications/notifications.js b/notifications/notifications.js index 718fe89..5f0c2c6 100644 --- a/notifications/notifications.js +++ b/notifications/notifications.js @@ -3,13 +3,19 @@ const dbData = require('../database/dbData'); let expo = new Expo(); +//CHECK +const IsExpoToken = token => { + + if (!Expo.isExpoPushToken(token)) { + console.error(`Push token ${token} is not a valid Expo push token`); + } + +} + //ACTIVE const SendNotification = async (pushToken, title, body, data) => { - if (!Expo.isExpoPushToken(pushToken)) { - console.error(`Push token ${pushToken} is not a valid Expo push token`); - return; - } + IsExpoToken(pushToken); const message = { to: pushToken, @@ -132,5 +138,6 @@ module.exports = { SendStrikeNotification, SendKickNotification, SendTestNotification, - SendScenarioNotifications + SendScenarioNotifications, + IsExpoToken }; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 6150fc5..3c027d0 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -171,6 +171,42 @@ const SetDisplayName = async (req, res, next) => { } +} +const SetExpoToken = async (req, res, next) => { + + try { + + const { expoToken } = req.query; + const googleToken = req.headers['authorization']; + + //checks + if (!expoToken) throw new Error('No expo token provided'); + if (!googleToken) throw new Error('No auth header provided'); + ValidateCharsNoEmojis(expoToken); + + //post it + await dbPosts.ExpoToken(googleToken, expoToken); + + //response + res.status(201).send({ + ok: true, + message: 'succesfully updated expo token for notifications!', + data: { + expoToken: expoToken + } + }); + + } + catch (error) { + + //fail response + res.status(400).send({ + ok: false, + message: 'Cant update expo token: ' + error.message, + }); + + } + } userRouter.get('/', isAuth, GetUserInfo); @@ -178,6 +214,7 @@ userRouter.get('/stats', GetUserStats); // userRouter.post('/create', AddNewUser, Login); userRouter.post('/login', Login); userRouter.post('/name', SetDisplayName); +userRouter.post('/expoToken', SetExpoToken); module.exports = userRouter; @@ -191,17 +228,17 @@ module.exports = userRouter; //TEST CODE HERE! -const testFunc = async () => { +// const testFunc = async () => { - // let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { - // headers: { Authorization: `Bearer ${'faketokenoanfoa'}` } - // }); - // const userInfo = await response.json(); +// // let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { +// // headers: { Authorization: `Bearer ${'faketokenoanfoa'}` } +// // }); +// // const userInfo = await response.json(); - // if (userInfo.error) { - // console.log('cant fetch user, invalid token!'); - // } +// // if (userInfo.error) { +// // console.log('cant fetch user, invalid token!'); +// // } -} +// } -testFunc(); \ No newline at end of file +// testFunc(); \ No newline at end of file From 0bb6f8f0f31c30683fc0c41daef8acaa0b9ed729 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 21 Feb 2023 13:24:48 -0300 Subject: [PATCH 117/127] auth middleware uppdaterad on tillagd --- database/dbChecks.js | 18 ++++++++- database/dbData.js | 18 ++++++++- middleware/authentication.js | 57 ++++++++------------------- routers/infoRouter.js | 3 +- routers/userRouter.js | 74 ++++-------------------------------- 5 files changed, 60 insertions(+), 110 deletions(-) diff --git a/database/dbChecks.js b/database/dbChecks.js index 2a485a0..64e3625 100644 --- a/database/dbChecks.js +++ b/database/dbChecks.js @@ -83,9 +83,25 @@ async function CanAddScenario(campId, userId, isEnd) { if (campFinished) throw new Error('cant add scenario, story marked as finished'); if (scenario) throw new Error('Cant add scenario, there already exist one linked to the last node'); +} +async function GoogleTokenExists(googleToken) { + + const tokenQ = await db.query( + ` + SELECT COUNT(*) + FROM users + WHERE google_token = $1 + `, + [googleToken] + ); + + if (tokenQ.rows[0].count == 0) return false; + else return true; + } module.exports = { CanAddNode, - CanAddScenario + CanAddScenario, + GoogleTokenExists }; \ No newline at end of file diff --git a/database/dbData.js b/database/dbData.js index 882af7f..8975c9a 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -90,6 +90,21 @@ async function Player(googleId) { if (playerQ.rowCount < 1) return null; else return playerQ.rows[0]; +} +async function PlayerWithGoogleToken(googleToken) { + + const playerQ = await db.query( + ` + SELECT * + FROM users + WHERE google_token = $1 + `, + [googleToken] + ); + + if (playerQ.rowCount == 0) return null; + else return playerQ.rows[0]; + } //camp data @@ -362,5 +377,6 @@ module.exports = { GetCampPlayersExpoTokens, PlayerName, Player, - StoryTitle + StoryTitle, + PlayerWithGoogleToken }; \ No newline at end of file diff --git a/middleware/authentication.js b/middleware/authentication.js index 6bd34c5..b174830 100644 --- a/middleware/authentication.js +++ b/middleware/authentication.js @@ -1,55 +1,30 @@ -const jwt = require('jsonwebtoken'); -const dbFunctions = require('../database/dbFunctions'); -const { ValidateChars } = require('./validation'); +const dbData = require('../database/dbData'); const isAuth = async (req, res, next) => { - const authToken = req.headers['authorization']; + try { - if (typeof authToken == 'undefined' || !authToken) { - res.status(403).send('no authorization header provided'); - return; - } + const googleToken = req.headers['authorization']; - jwt.verify(authToken, process.env.JWT_SECRET, (err, data) => { - if (err) { - res.status(403).send('invalid auth token'); - } - else { - req.userId = data.id || data.user_id || data.userId; - console.log('attaching user id: ', req.userId); - next(); + if (typeof googleToken == 'undefined' || !googleToken) { + throw new Error('no google token provided in auth header'); } - }) - -} - -// const Login = async (req, res, next) => { -// try { -// const email = req.query.email; -// const password = req.query.password; + const user = await dbData.PlayerWithGoogleToken(googleToken); + if (!user) throw new Error('no player with that google token found'); -// if (!email) throw new Error('no email provided'); -// if (!password) throw new Error('no password provided'); -// ValidateChars(email); -// ValidateChars(password); + req.loggedUser = user; -// const user = await dbFunctions.Login(email, password); + next(); -// const token = jwt.sign( -// { userId: user.id }, -// process.env.JWT_SECRET, -// { expiresIn: process.env.JWT_EXPIRES_IN } -// ); + } catch (error) { -// res.status(201).send({ message: 'logged in as ' + user.name, token: token }); + res.status(403).send({ + ok: false, + message: 'cant authorize user: ' + error.message + }); -// } catch (error) { - -// res.status(400).send('Cant login: ' + error.message); - -// } -// } + } +} module.exports = { isAuth }; \ No newline at end of file diff --git a/routers/infoRouter.js b/routers/infoRouter.js index 865354a..169fda8 100644 --- a/routers/infoRouter.js +++ b/routers/infoRouter.js @@ -1,6 +1,7 @@ const express = require('express'); const infoRouter = express.Router(); const dbData = require('../database/dbData'); +const { isAuth } = require('../middleware/authentication'); const GetLatestNews = async (req, res, next) => { @@ -26,6 +27,6 @@ const GetLatestNews = async (req, res, next) => { } -infoRouter.get('/news', GetLatestNews); +infoRouter.get('/news', isAuth, GetLatestNews); module.exports = infoRouter; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 3c027d0..6577835 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -1,40 +1,11 @@ const express = require('express'); const userRouter = express.Router(); -const dbFunctions = require('../database/dbFunctions'); //should be replaced const dbData = require('../database/dbData'); const dbPosts = require('../database/dbPosts'); const { isAuth } = require('../middleware/authentication'); -const { ValidateChars, ValidateCharsNoEmojis } = require('../middleware/validation'); +const { ValidateCharsNoEmojis } = require('../middleware/validation'); const fetch = require('node-fetch'); -// const AddNewUser = async (req, res, next) => { - -// try { -// const { name, email, password, pushToken } = req.query; - -// if (!exists(email)) throw new Error('No email provided') -// if (!exists(password)) throw new Error('No password provided') -// if (password.length < 6) throw new Error('Password must be at least 6 characters') -// if (!exists(name)) throw new Error('No name provided') -// if (name.length < 4) throw new Error('Name must be at least 4 characters') -// if (name.length > 20) throw new Error('Name must be max 20 characters') - -// ValidateChars(email); -// ValidateChars(password); -// ValidateChars(name); - -// await dbFunctions.CreateUser(name, email, password, pushToken); - -// next(); - -// } -// catch (error) { - -// res.status(400).send({ ok: false, message: error.message }); - -// } - -// } const GetUserInfo = async (req, res, next) => { try { @@ -43,8 +14,8 @@ const GetUserInfo = async (req, res, next) => { id: user.id, name: user.name, room_keys: user.room_keys, - email: user.email, - premium: user.premium + // email: user.email, + // premium: user.premium }); } catch (error) { @@ -81,16 +52,13 @@ const Login = async (req, res, next) => { try { const googleToken = req.headers['authorization']; - if (!googleToken) throw new Error('no bearer token in auth header'); - - // console.log('trying to log in with auth token: ', googleToken); + if (!googleToken) throw new Error('no google token in auth header'); //fetch user from google using the token let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { headers: { Authorization: `Bearer ${googleToken}` } }); const userInfo = await response.json(); - // console.log('fetched user and got info: ', userInfo); if (userInfo.error) { console.error('user tried to log in with invalid token. Error: ', userInfo.error); @@ -210,35 +178,9 @@ const SetExpoToken = async (req, res, next) => { } userRouter.get('/', isAuth, GetUserInfo); -userRouter.get('/stats', GetUserStats); -// userRouter.post('/create', AddNewUser, Login); +userRouter.get('/stats', isAuth, GetUserStats); userRouter.post('/login', Login); -userRouter.post('/name', SetDisplayName); -userRouter.post('/expoToken', SetExpoToken); - -module.exports = userRouter; - -//HELPERS -// const exists = text => { -// return !(!text || text == null || text == 'null' || text == 'undefined' || text == ''); -// } - - - - - -//TEST CODE HERE! -// const testFunc = async () => { - -// // let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { -// // headers: { Authorization: `Bearer ${'faketokenoanfoa'}` } -// // }); -// // const userInfo = await response.json(); - -// // if (userInfo.error) { -// // console.log('cant fetch user, invalid token!'); -// // } - -// } +userRouter.post('/name', isAuth, SetDisplayName); +userRouter.post('/expoToken', isAuth, SetExpoToken); -// testFunc(); \ No newline at end of file +module.exports = userRouter; \ No newline at end of file From c72b73c05e55b083fbbaa707439acc7072ea6af9 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 21 Feb 2023 13:44:24 -0300 Subject: [PATCH 118/127] expo token check buggfix --- routers/userRouter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/userRouter.js b/routers/userRouter.js index 6577835..f228583 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -150,7 +150,7 @@ const SetExpoToken = async (req, res, next) => { //checks if (!expoToken) throw new Error('No expo token provided'); if (!googleToken) throw new Error('No auth header provided'); - ValidateCharsNoEmojis(expoToken); + // ValidateCharsNoEmojis(expoToken); //post it await dbPosts.ExpoToken(googleToken, expoToken); From 1d6ab3611ce6f40ad5c3b7b2f39cba527374c203 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 22 Feb 2023 11:12:07 -0300 Subject: [PATCH 119/127] Update userRouter.js --- routers/userRouter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/userRouter.js b/routers/userRouter.js index f228583..854f0d8 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -9,7 +9,9 @@ const fetch = require('node-fetch'); const GetUserInfo = async (req, res, next) => { try { - const user = await dbFunctions.GetLoggedUserInfo(req.userId); + const googleToken = req.headers['authorization']; + // const user = await dbFunctions.GetLoggedUserInfo(req.userId); + const user = await dbData.PlayerWithGoogleToken(req.headers['authorization']) res.json({ id: user.id, name: user.name, From 00136d82f66489709bee528f9e2a7b002dc6c57c Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 24 Feb 2023 11:20:41 -0300 Subject: [PATCH 120/127] bugfixes with user id in auth --- routers/nodeRouter.js | 8 ++++---- routers/scenarioRouter.js | 2 +- routers/userRouter.js | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/routers/nodeRouter.js b/routers/nodeRouter.js index dca1a55..fd7335a 100644 --- a/routers/nodeRouter.js +++ b/routers/nodeRouter.js @@ -47,7 +47,7 @@ const TryAddNode = async (req, res, next) => { try { - const userId = req.userId; + const userId = req.loggedUser.id; const { campId } = req.query //initial error checks @@ -84,7 +84,7 @@ const TryAddScenario = async (req, res, next) => { try { //params - const userId = req.userId; + const userId = req.loggedUser.id; const { campId, text, end } = req.query const isEnd = (end == "true"); @@ -125,7 +125,7 @@ const Like = async (req, res, next) => { try { //params - const userId = req.userId; + const userId = req.loggedUser.id; const { nodeId } = req.query //checks @@ -155,7 +155,7 @@ const Dislike = async (req, res, next) => { try { //params - const userId = req.userId; + const userId = req.loggedUser.id; const { nodeId } = req.query //checks diff --git a/routers/scenarioRouter.js b/routers/scenarioRouter.js index 27bfc3d..e4c2dc7 100644 --- a/routers/scenarioRouter.js +++ b/routers/scenarioRouter.js @@ -13,7 +13,7 @@ const TryAddScenario = async (req, res, next) => { try { - const userId = req.userId; + const userId = req.loggedUser.id; const { roomId, text } = req.query const isEnd = (req.query.end == "true"); diff --git a/routers/userRouter.js b/routers/userRouter.js index 854f0d8..5dde895 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -10,7 +10,6 @@ const GetUserInfo = async (req, res, next) => { try { const googleToken = req.headers['authorization']; - // const user = await dbFunctions.GetLoggedUserInfo(req.userId); const user = await dbData.PlayerWithGoogleToken(req.headers['authorization']) res.json({ id: user.id, From 64d299bb346993fd85e791c382bcf349c5a27dad Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Fri, 24 Feb 2023 13:37:51 -0300 Subject: [PATCH 121/127] stamp logins in db --- database/dbPosts.js | 23 ++++++++++++++++++++++- routers/userRouter.js | 4 ++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/database/dbPosts.js b/database/dbPosts.js index ce86245..47dc985 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -94,6 +94,26 @@ async function ExpoToken(googleToken, expoToken) { ) if (tokenUpdateQ.rowCount == 0) throw new Error('found no user with that google token'); +} +async function StampLogin(userId) { + + try { + + await db.query( + ` + INSERT INTO logins (user_id) + VALUES ($1) + `, + [userId] + ); + + } + catch (error) { + + console.error('failed to stamp user login: ', error.message); + + } + } //NODES @@ -307,5 +327,6 @@ module.exports = { NewPlayer, UpdateGoogleToken, Name, - ExpoToken + ExpoToken, + StampLogin }; \ No newline at end of file diff --git a/routers/userRouter.js b/routers/userRouter.js index 5dde895..b88229e 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -5,6 +5,7 @@ const dbPosts = require('../database/dbPosts'); const { isAuth } = require('../middleware/authentication'); const { ValidateCharsNoEmojis } = require('../middleware/validation'); const fetch = require('node-fetch'); +const { StampLogin } = require('../database/dbPosts'); const GetUserInfo = async (req, res, next) => { @@ -82,6 +83,9 @@ const Login = async (req, res, next) => { if (!player) throw new Error('unable to update google token in database'); } + //add a login row in db for tracking + await StampLogin(player.id); + console.log('player logged in. ID: ', player.id); //and return the player! From 77b242d2f2d35c2b60a9f735216916452e14640d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 28 Feb 2023 15:12:33 -0300 Subject: [PATCH 122/127] fixed route for fetching player rooms --- database/dbData.js | 7 +++++++ routers/campRouter.js | 5 +++-- routers/userRouter.js | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index 8975c9a..86b766f 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -263,6 +263,9 @@ async function ActiveCamps(userId) { } async function PlayerCamps(userId) { + console.log('with user id: ', userId); + console.log('and maxplayerforquetop: ', balancing.numbers.maxPlayersForQueueTop); + const campQuery = await db.query( ` SELECT @@ -290,9 +293,13 @@ async function PlayerCamps(userId) { [userId, balancing.numbers.maxPlayersForQueueTop] ); + console.log('asked for user rooms and got count: ', campQuery.rowCount); + return campQuery.rows; } + +// PlayerCamps(95); async function FinishedStories() { const campQuery = await db.query( diff --git a/routers/campRouter.js b/routers/campRouter.js index 6d203b0..6f5986f 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -118,8 +118,9 @@ const GetPlayerCamps = async (req, res, next) => { try { - const { userId } = req; - const camps = await dbData.PlayerCamps(userId); + const { id } = req.loggedUser; + if (!id) throw new Error('cant fetch player camps because no userId was attached in the req'); + const camps = await dbData.PlayerCamps(id); res.status(200).send({ ok: true, message: 'found camps', diff --git a/routers/userRouter.js b/routers/userRouter.js index b88229e..2842300 100644 --- a/routers/userRouter.js +++ b/routers/userRouter.js @@ -103,6 +103,7 @@ const Login = async (req, res, next) => { } catch (error) { + console.log('player failed to login: ', error.message); res.status(400).send({ ok: false, message: 'Cant login: ' + error.message, From b6fa2bd8374bbcc1f187c2dddd0fea7664d27c83 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 28 Feb 2023 15:17:33 -0300 Subject: [PATCH 123/127] player stats only count finished nodes --- database/dbData.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index 86b766f..abb6d1d 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -34,21 +34,22 @@ async function PlayerStats(userId) { const q = await db.query( ` SELECT - COUNT(DISTINCT camps.id) AS camps, - SUM (CASE - WHEN camps.finished = TRUE THEN 1 - ELSE 0 - END - ) AS finished, - COUNT(DISTINCT nodes_0.id) AS contributions, - ( - SELECT COUNT(*) - FROM users - WHERE id = $1 - ) AS user_exist + COUNT(DISTINCT camps.id) AS camps, + SUM (CASE + WHEN camps.finished = TRUE THEN 1 + ELSE 0 + END + ) AS finished, + COUNT(DISTINCT nodes_0.id) AS contributions, + ( + SELECT COUNT(*) + FROM users + WHERE id = $1 + ) AS user_exist FROM nodes_0 JOIN camps ON camps.id = nodes_0.camp_id - WHERE nodes_0.creator_id = $1; + WHERE nodes_0.creator_id = $1 + AND nodes_0.finished_at IS NOT NULL; `, [userId] ); From 9eaff1641e324cc8fd3039a8d411f9e6a9ccf88a Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 28 Feb 2023 15:20:49 -0300 Subject: [PATCH 124/127] my camps route fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit returnerar nu endast camps där spelaren har FÄRDIGSTÄLLT noder --- database/dbData.js | 1 + 1 file changed, 1 insertion(+) diff --git a/database/dbData.js b/database/dbData.js index abb6d1d..3b9742e 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -285,6 +285,7 @@ async function PlayerCamps(userId) { FROM nodes_0 WHERE creator_id = $1 AND camp_id = camps.id + AND finished_at IS NOT NULL ) AND finished = 'false' GROUP BY camps.id, users.name, camps.title, camps.description From 0ac010bdea510d9af804e1b182d55be1907fd97d Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Tue, 28 Feb 2023 15:31:59 -0300 Subject: [PATCH 125/127] open rooms route fix now only returns rooms that player is not already a part of --- database/dbData.js | 6 +++++- routers/campRouter.js | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/database/dbData.js b/database/dbData.js index 3b9742e..b1b037e 100644 --- a/database/dbData.js +++ b/database/dbData.js @@ -230,6 +230,9 @@ async function StoryTitle(campId) { //camps async function ActiveCamps(userId) { + console.log('userid: ', userId); + console.log('balancing number: ', balancing.numbers.maxPlayersForQueueTop); + const campQuery = await db.query( ` SELECT @@ -248,6 +251,7 @@ async function ActiveCamps(userId) { FROM nodes_0 WHERE creator_id = $1 AND camp_id = camps.id + AND finished_at IS NOT NULL ) AND finished = 'false' GROUP BY camps.id, users.name, camps.title, camps.description @@ -257,7 +261,7 @@ async function ActiveCamps(userId) { [userId, balancing.numbers.maxPlayersForQueueTop] ); - // console.log('camp count found: ', campQuery); + console.log('camp count found: ', campQuery.rowCount); return campQuery.rows; diff --git a/routers/campRouter.js b/routers/campRouter.js index 6f5986f..6fffdb0 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -19,7 +19,7 @@ const CreateCamp = async (req, res, next) => { try { - const { userId } = req; + const userId = req.loggedUser.id; const { title, description, scenario } = req.query; const balanceSheet = balancing.numbers; @@ -96,8 +96,8 @@ const GetActiveCamps = async (req, res, next) => { try { - const { userId } = req; - const camps = await dbData.ActiveCamps(userId); + if (!req.loggedUser.id) throw new Error('no user ID attached in req'); + const camps = await dbData.ActiveCamps(req.loggedUser.id); res.status(200).send({ ok: true, message: 'found camps', From 18c68dd3d01e8cbdce00f57a4d25c3c2f6e26742 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Mar 2023 15:21:14 -0300 Subject: [PATCH 126/127] accepts camps without a description --- routers/campRouter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routers/campRouter.js b/routers/campRouter.js index 6fffdb0..c519260 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -27,14 +27,14 @@ const CreateCamp = async (req, res, next) => { if (!title) throw new Error('Please provide a title'); if (title.length < balanceSheet.titleMinChars) throw new Error('Title is too short. Minimum chars: ', titleMinChars); if (title.length > balanceSheet.titleMaxChars) throw new Error('Title is too long. Maximum chars: ', titleMaxChars); - if (!description) throw new Error('Please provide a description'); - if (description.length < balanceSheet.descriptionMinChars) throw new Error('Description is too short. Minimum chars: ', descriptionMinChars); - if (description.length > balanceSheet.descriptionMaxChars) throw new Error('Description is too long. Maximum chars: ', descriptionMaxChars); + // if (!description) throw new Error('Please provide a description'); + // if (description.length < balanceSheet.descriptionMinChars) throw new Error('Description is too short. Minimum chars: ', descriptionMinChars); + // if (description.length > balanceSheet.descriptionMaxChars) throw new Error('Description is too long. Maximum chars: ', descriptionMaxChars); if (!scenario) throw new Error('Please provide a starting scenario'); if (scenario.length < balanceSheet.scenarioMinCharacter) throw new Error('Scenario is too short. Minimum chars: ', scenarioMinCharacter); if (scenario.length > balanceSheet.scenarioMaxCharacters) throw new Error('Scenario is too long. Maximum chars: ', scenarioMaxCharacters); ValidateChars(title); - ValidateChars(description); + // ValidateChars(description); ValidateChars(scenario); //TRY ADD TO DATABASE From 21eeceeaf5d96d492ca6d9cc7cdbe39abcad4991 Mon Sep 17 00:00:00 2001 From: douglasdriving Date: Wed, 8 Mar 2023 15:33:09 -0300 Subject: [PATCH 127/127] bugfix when uploading camp without description --- database/dbPosts.js | 12 ++++++------ routers/campRouter.js | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/database/dbPosts.js b/database/dbPosts.js index 47dc985..3d93894 100644 --- a/database/dbPosts.js +++ b/database/dbPosts.js @@ -282,7 +282,7 @@ async function Dislike(nodeId, userId) { } //CAMPS -async function Camp(title, description, scenario, creator_id) { +async function Camp(title, /*description,*/ scenario, creator_id) { const campTitleQ = await db.query( ` @@ -298,20 +298,20 @@ async function Camp(title, description, scenario, creator_id) { const campQ = await db.query( ` WITH new_camp AS( - INSERT INTO camps(title, description, creator_id) - VALUES($1, $2, $3) + INSERT INTO camps(title, creator_id) + VALUES($1, $2) RETURNING id ), new_node AS ( INSERT INTO nodes_0 (creator_id, camp_id, finished_at) - VALUES ($3, (SELECT id FROM new_camp), now()) + VALUES ($2, (SELECT id FROM new_camp), now()) RETURNING id ) INSERT INTO scenarios_0 (scenario, node_id) - VALUES ($4, (SELECT id FROM new_node)) + VALUES ($3, (SELECT id FROM new_node)) RETURNING (SELECT id FROM new_camp); ` , - [title, description, creator_id, scenario] + [title, creator_id, scenario] ); const campId = campQ.rows[0].id; return campId; diff --git a/routers/campRouter.js b/routers/campRouter.js index c519260..c52807a 100644 --- a/routers/campRouter.js +++ b/routers/campRouter.js @@ -41,7 +41,8 @@ const CreateCamp = async (req, res, next) => { await dbTransactions.Begin(); transactionInitiated = true; await dbUpdates.RemoveLog(userId); - const campId = await dbPosts.Camp(title, description, scenario, userId); + // const campId = await dbPosts.Camp(title, description, scenario, userId); + const campId = await dbPosts.Camp(title, scenario, userId); await dbTransactions.Commit(); res.status(200).send({