diff --git a/.env b/.env new file mode 100644 index 0000000..4ac545b --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +DB_USER=postgres +DB_PASS=123 +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=postgres diff --git a/app.js b/app.js index 0c52da1..8fa2d3f 100644 --- a/app.js +++ b/app.js @@ -4,13 +4,14 @@ const session = require('express-session'); const responseTime = require('response-time'); const types = require('pg').types; const validator = require('validator'); +const path = require('path'); + +require('dotenv').config(); const app = express(); +const db = pgp(`postgres://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`); -// TODO: Replace with some environmental variables which store real password / host / database name -const db = pgp('postgres://postgres:99postgres11@localhost:5432/skrytka'); - -const port = 3001; +const port = 5000; class Point { x; @@ -32,7 +33,7 @@ class Rectangle { } } -types.setTypeParser(603, function(rectangleStr) { +types.setTypeParser(603, function (rectangleStr) { rectangleStr = rectangleStr.slice(1, rectangleStr.length - 1); const pointStrs = rectangleStr.split('),('); const fp = pointStrs[0].split(','); @@ -44,6 +45,12 @@ types.setTypeParser(603, function(rectangleStr) { return new Rectangle(lowerBottomPoint, size); }); +// Hosting frontend: +app.use(express.static(path.join(__dirname, 'client/build'))); + +// Hosting images (trucks etc): +app.use(express.static(path.join(__dirname, 'media'))); + app.use(session({ store: new (require('connect-pg-simple')(session))({ pgPromise: db, @@ -55,15 +62,17 @@ app.use(session({ cookie: { sameSite: 'lax', // Age of cookie is counted from last access, so here 30 days of *not using* = deletion - maxAge: 30 * 24 * 60 * 60 * 1000 + maxAge: 30 * 24 * 60 * 60 * 1000 } })); +console.log(process.env.DB_NAME); + let latencies = [0]; const MAX_LATENCIES = 25; -app.use(responseTime(function(req, res, time) { - if(latencies.length > MAX_LATENCIES) { +app.use(responseTime(function (req, res, time) { + if (latencies.length > MAX_LATENCIES) { latencies.shift(); } latencies.push(time); @@ -84,13 +93,13 @@ app.get('/ping', (req, res) => { * e.g. https://skrytka.app/osp-units?prefix=Gda * Returns 3 matching results sorted by locality, name.*/ app.get('/osp-units', async (req, res) => { - const localityPrefix = req.query['locality-prefix']; + const locality = req.query['locality']; - if(!localityPrefix) { + if (!locality) { res.status(400); res.json({ queryErrors: { - 'locality-prefix': ['Nie podano parametru!'] + 'locality': ['Nie podano parametru!'] }, otherErrors: [] }); @@ -101,10 +110,10 @@ app.get('/osp-units', async (req, res) => { // Also are these database constraints reasonable? try { - const unitsList = await db.any('SELECT id AS "ID", name, locality FROM get_units_list($1)', [localityPrefix]); + const unitsList = await db.any('SELECT id AS "ID", name, locality FROM get_units_list($1)', [locality]); res.status(200); res.json(unitsList); - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -122,7 +131,7 @@ app.get('/osp-units', async (req, res) => { app.get('/fire-trucks', async (req, res) => { const ospUnit = req.query['osp-unit']; - if(!ospUnit) { + if (!ospUnit) { res.status(400); res.json({ queryErrors: { @@ -137,7 +146,7 @@ app.get('/fire-trucks', async (req, res) => { // But it is better to know if result is empty because it is just answer to our question // and without validation before query we can get empty result because of some undetermined error - if(!validator.isNumeric(ospUnit, { no_symbols: true })) { + if (!validator.isNumeric(ospUnit, { no_symbols: true })) { res.status(400); res.json({ queryErrors: { @@ -148,7 +157,7 @@ app.get('/fire-trucks', async (req, res) => { return; } - if(ospUnit <= 0) { + if (ospUnit <= 0) { res.status(400); res.json({ queryErrors: { @@ -161,7 +170,7 @@ app.get('/fire-trucks', async (req, res) => { try { const unitExists = (await db.any('SELECT id FROM osp_unit WHERE id = $1', [ospUnit])).length != 0; - if(!unitExists) { + if (!unitExists) { res.status(404); res.json({ queryErrors: { @@ -171,7 +180,7 @@ app.get('/fire-trucks', async (req, res) => { }); return; } - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -179,16 +188,16 @@ app.get('/fire-trucks', async (req, res) => { }); return; } - + let user = req.session['accountNickname']; const loggedIn = user ?? false; - if(loggedIn) { + if (loggedIn) { try { const trucksList = await db.func('get_trucks_list_with_scores', [ospUnit, user]); res.status(200); res.json(trucksList); - } catch(error) { + } catch (error) { console.log(error); res.status(500); res.json({ @@ -209,19 +218,19 @@ app.get('/fire-trucks', async (req, res) => { // 3. Map scores so that each entry becomes average of scores divided by 10 (percents) const quizResults = req.session.quizResults ?? []; - for(let i = quizResults.length - 1; i >= 0; i--) { - if(!avgPercents[quizResults[i].fireTruckID]) { + for (let i = quizResults.length - 1; i >= 0; i--) { + if (!avgPercents[quizResults[i].fireTruckID]) { avgPercents[quizResults[i].fireTruckID] = []; } - if(avgPercents[quizResults[i].fireTruckID].length < 3) { + if (avgPercents[quizResults[i].fireTruckID].length < 3) { avgPercents[quizResults[i].fireTruckID].push(quizResults[i].points); } } console.log(avgPercents); - Object.keys(avgPercents).forEach(function(fireTruckID, index) { + Object.keys(avgPercents).forEach(function (fireTruckID, index) { let sum = 0; let numberOfResults = 0; @@ -231,7 +240,7 @@ app.get('/fire-trucks', async (req, res) => { console.log(sum, numberOfResults); }); - let averagePercent = (sum/numberOfResults)/10; + let averagePercent = (sum / numberOfResults) / 10; avgPercents[fireTruckID] = averagePercent; }); @@ -245,7 +254,7 @@ app.get('/fire-trucks', async (req, res) => { res.status(200); res.json(trucksList); - } catch(error) { + } catch (error) { console.log(error); res.status(500); res.json({ @@ -259,7 +268,7 @@ app.get('/fire-trucks', async (req, res) => { app.get('/quiz-pages', async (req, res) => { const fireTruck = req.query['fire-truck']; - if(!fireTruck) { + if (!fireTruck) { res.status(400); res.json({ queryErrors: { @@ -270,7 +279,7 @@ app.get('/quiz-pages', async (req, res) => { return; } - if(!validator.isNumeric(fireTruck, { no_symbols: true })) { + if (!validator.isNumeric(fireTruck, { no_symbols: true })) { res.status(400); res.json({ queryErrors: { @@ -281,7 +290,7 @@ app.get('/quiz-pages', async (req, res) => { return; } - if(fireTruck <= 0) { + if (fireTruck <= 0) { res.status(400); res.json({ queryErrors: { @@ -294,7 +303,7 @@ app.get('/quiz-pages', async (req, res) => { try { const truckExists = (await db.any('SELECT id FROM fire_truck WHERE id = $1', [fireTruck])).length != 0; - if(!truckExists) { + if (!truckExists) { res.status(404); res.json({ queryErrors: { @@ -304,7 +313,7 @@ app.get('/quiz-pages', async (req, res) => { }); return; } - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -321,7 +330,7 @@ app.get('/quiz-pages', async (req, res) => { quizData = quizData.reduce((acc, curr) => { const sideID = curr.sideID; - if(!acc[sideID]) { + if (!acc[sideID]) { acc[sideID] = { sideImagePath: curr.sideImagePath, caches: [] @@ -341,7 +350,7 @@ app.get('/quiz-pages', async (req, res) => { res.status(200); res.send(quizData); - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -353,7 +362,7 @@ app.get('/quiz-pages', async (req, res) => { app.get('/random-question', async (req, res) => { const fireTruck = req.query['fire-truck']; - if(!fireTruck) { + if (!fireTruck) { res.status(400); res.json({ queryErrors: { @@ -364,7 +373,7 @@ app.get('/random-question', async (req, res) => { return; } - if(!validator.isNumeric(fireTruck, { no_symbols: true })) { + if (!validator.isNumeric(fireTruck, { no_symbols: true })) { res.status(400); res.json({ queryErrors: { @@ -375,7 +384,7 @@ app.get('/random-question', async (req, res) => { return; } - if(fireTruck <= 0) { + if (fireTruck <= 0) { res.status(400); res.json({ queryErrors: { @@ -388,7 +397,7 @@ app.get('/random-question', async (req, res) => { try { const truckExists = (await db.any('SELECT id FROM fire_truck WHERE id = $1', [fireTruck])).length != 0; - if(!truckExists) { + if (!truckExists) { res.status(404); res.json({ queryErrors: { @@ -398,7 +407,7 @@ app.get('/random-question', async (req, res) => { }); return; } - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -411,7 +420,7 @@ app.get('/random-question', async (req, res) => { const randomQuestion = await db.one('SELECT cache_id AS "cacheID", equipment_name AS "equipmentName" FROM get_random_question($1)', [fireTruck]); res.status(200); res.send(randomQuestion); - } catch(error) { + } catch (error) { res.status(500); res.json({ queryErrors: {}, @@ -425,7 +434,7 @@ app.post('/quiz-results', async (req, res) => { const seconds = req.body.seconds.toString(); const points = req.body.points.toString(); - if(!validator.isNumeric(fireTruck, { no_symbols: true })) { + if (!validator.isNumeric(fireTruck, { no_symbols: true })) { res.status(400); res.json({ fieldErrors: { @@ -436,7 +445,7 @@ app.post('/quiz-results', async (req, res) => { return; } - if(fireTruck <= 0) { + if (fireTruck <= 0) { res.status(400); res.json({ fieldErrors: { @@ -449,7 +458,7 @@ app.post('/quiz-results', async (req, res) => { try { const truckExists = (await db.any('SELECT id FROM fire_truck WHERE id = $1', [fireTruck])).length != 0; - if(!truckExists) { + if (!truckExists) { res.status(404); res.json({ fieldErrors: { @@ -459,7 +468,7 @@ app.post('/quiz-results', async (req, res) => { }); return; } - } catch(error) { + } catch (error) { res.status(500); res.json({ fieldErrors: {}, @@ -470,7 +479,7 @@ app.post('/quiz-results', async (req, res) => { /* Walidacje dla seconds */ - if(!validator.isNumeric(seconds, { no_symbols: true })) { + if (!validator.isNumeric(seconds, { no_symbols: true })) { res.status(400); res.json({ fieldErrors: { @@ -481,7 +490,7 @@ app.post('/quiz-results', async (req, res) => { return; } - if(seconds <= 0) { + if (seconds <= 0) { res.status(400); res.json({ fieldErrors: { @@ -490,11 +499,11 @@ app.post('/quiz-results', async (req, res) => { otherErrors: [] }); return; - } + } /* Walidacje dla points */ - if(!validator.isNumeric(seconds, { no_symbols: true })) { + if (!validator.isNumeric(seconds, { no_symbols: true })) { res.status(400); res.json({ fieldErrors: { @@ -505,7 +514,7 @@ app.post('/quiz-results', async (req, res) => { return; } - if(points <= 0 || points >= 10) { + if (points <= 0 || points >= 10) { res.status(400); res.json({ fieldErrors: { @@ -519,10 +528,10 @@ app.post('/quiz-results', async (req, res) => { // TODO: Zapis do sesji lub do bazy console.log('Zapis', req.body); - - if(!req.session.accountNickname) { + + if (!req.session.accountNickname) { // Zapis do sesji - if(!req.session.quizResults) { + if (!req.session.quizResults) { req.session.quizResults = []; } @@ -532,7 +541,7 @@ app.post('/quiz-results', async (req, res) => { try { await db.none('INSERT INTO score (account_nickname, fire_truck_id, points, seconds)\ VALUES ($1, $2, $3, $4)', [req.session.accountNickname, fireTruck, points, seconds]); - } catch(error) { + } catch (error) { console.error(error); res.status(500); res.json({ @@ -552,7 +561,6 @@ app.post('/quiz-results', async (req, res) => { res.status(200); res.send('Added!'); }); - app.get('/cookiegetvalue', (req, res) => { const val = req.session.counter || 0; res.status(200); @@ -564,7 +572,7 @@ app.get('/cookiegetvalue', (req, res) => { app.get('/simulate-login', (req, res) => { const nickToAuth = req.query.nickname; - if(!nickToAuth) { + if (!nickToAuth) { res.status(400); res.send('You must pass nickname as query parameter to simulate login'); return; @@ -576,7 +584,7 @@ app.get('/simulate-login', (req, res) => { }); app.get('/simulate-logout', (req, res) => { - if(!req.session.accountNickname) { + if (!req.session.accountNickname) { res.status(401); res.json({ fieldErrors: {}, @@ -592,6 +600,12 @@ app.get('/simulate-logout', (req, res) => { res.send(`Logged out!`); }); +// This should remain at the end of all routes +// It will direct every not matched route to index.html file +app.get('/*', function (req, res) { + res.sendFile(path.join(__dirname, 'client/build', 'index.html')); +}); + app.listen(port, () => { console.log(`Skrytka.app słucha na porcie ${port}...`); -}); +}); \ No newline at end of file diff --git a/client/public/img/trucks/woz_jakis_tam11.png b/client/public/img/trucks/woz_jakis_tam11.png new file mode 100644 index 0000000..d95c0e3 Binary files /dev/null and b/client/public/img/trucks/woz_jakis_tam11.png differ diff --git a/client/src/components/AppContext.js b/client/src/components/AppContext.js index e71b4a5..6d92f13 100644 --- a/client/src/components/AppContext.js +++ b/client/src/components/AppContext.js @@ -11,7 +11,7 @@ const AppProvider = ({ children }) => { const [isContainerActive, setIsContainerActive] = useState(true); const [localization, setLocalization] = useState(localizationFromDBArr); const [isPopUpActive, setIsPopUpActive] = useState(true); - + const [idUnit, setId] = useState(1); return ( { localizationFromDBArr, isPopUpActive, setIsPopUpActive, + idUnit, + setId }}> diff --git a/client/src/components/SelectBox.jsx b/client/src/components/SelectBox.jsx index 054de56..3b4cbf3 100644 --- a/client/src/components/SelectBox.jsx +++ b/client/src/components/SelectBox.jsx @@ -1,28 +1,31 @@ import React, {useContext, useState ,useRef, useEffect} from 'react'; import { AppContext } from './AppContext'; +export let idUnits = ""; const SearchBox = () => { - const {unitOsp, isContainerActive, setunitOsp,setIsContainerActive,localization} = useContext(AppContext); + const {unitOsp, isContainerActive, setunitOsp,setIsContainerActive,localization,setId} = useContext(AppContext); const [value, setValue] = useState(""); const inputSearchRef = useRef(true); - + const handleChangeInput = (e) => { if(e.target.value === "") return fetch(`/osp-units?locality=${e.target.value}`) .then(data => data.json()) - .then(data1=> { - console.log(data1) - data1.map(locality => { - console.log(locality.locality); + .then(res=> { + + res.map(locality => { + let localityArray = []; + localityArray.push(locality.locality); let tasks = localityArray.filter(localization => localization.toLowerCase().includes(e.target.value.toLowerCase())); - console.log(tasks); setIsContainerActive(true); setValue(tasks); + + idUnits = locality.ID }) }) diff --git a/client/src/components/Truck.jsx b/client/src/components/Truck.jsx index af45f2f..ed58c1a 100644 --- a/client/src/components/Truck.jsx +++ b/client/src/components/Truck.jsx @@ -1,11 +1,10 @@ -import React, {useRef,useEffect} from 'react'; +import React, {useRef,useEffect,useContext} from 'react'; import { useState } from 'react'; import { useParams } from 'react-router-dom'; import {Link} from 'react-router-dom'; - - - +import { AppContext } from './AppContext'; +import { idUnits } from './SelectBox'; export let InformationTrackFromDB = [ { img: '../img/fire-truck/track1.jpg', @@ -30,13 +29,11 @@ const Truck = () => { const {id} = useParams(); const [trackName, setTrackName] = useState(''); const textChooseTrack = useRef(true); - + const handleImageClick = (e) => { - - const imgWithoutExtends = e.target.id.split(/\.(?=[^\.]+$)/); - const TrackName = imgWithoutExtends[0].slice(18); - setTrackName(TrackName); + let nameofTrack = e.target.getAttribute('name'); + setTrackName(nameofTrack); textChooseTrack.current.style.color= 'black'; @@ -54,41 +51,51 @@ const Truck = () => { const BoxComponent = () => { - const track = InformationTrackFromDB.map(({img,name,progress},index)=> { - - return ( - <> - - -
- - - img - - -
-

{name}

- - - -
-

{progress / 10}/10

-

{progress}%

-
- -
- -
- - - - - ) - - }); + let [truck, setTruck] = useState(''); + useEffect(() => { + fetch(`/fire-trucks?osp-unit=${idUnits}`) + .then(response => response.json()) + .then(data => { + setTruck(data.map(({name,imagePath,avgPercent},index) => { + console.log(name) return ( <> - {track} + + +
handleImageClick(e)} > + + + img + + +
+

{name}

+ + + +
+

{avgPercent / 10}/10

+

{avgPercent}%

+
+ +
+ +
+ + + + + ) + }) + )} + + + ) + }, []); + + return ( + <> + {truck} diff --git a/package-lock.json b/package-lock.json index 596ade8..349e75d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "skrytka", "version": "0.0.1", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "connect-pg-simple": "^8.0.0", + "dotenv": "^16.0.3", "express": "^4.18.2", "express-session": "^1.17.3", "pg": "^8.9.0", @@ -178,6 +179,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "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", @@ -878,5 +887,645 @@ "node": ">=0.4" } } + }, + "dependencies": { + "@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + }, + "@types/pg": { + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz", + "integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==", + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "accepts": { + "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.34", + "negotiator": "0.6.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "assert-options": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz", + "integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA==" + }, + "body-parser": { + "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.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "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.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" + } + }, + "connect-pg-simple": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-8.0.0.tgz", + "integrity": "sha512-pBDa23RA1LCkwvRrPOh5xevB+Nknh1UDuhFOKsUrkUDodYqfiQT18P2qXc4lk/TqCMB6hI06B8KNncHh91bZMQ==", + "requires": { + "@types/pg": "^8.6.5", + "pg": "^8.8.0" + } + }, + "content-disposition": { + "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.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "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", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "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==" + }, + "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", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "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.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "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.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "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" + } + }, + "express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "requires": { + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + } + } + }, + "finalhandler": { + "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.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "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": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "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": "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" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "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", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "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.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.52.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "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" + } + }, + "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", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "pg": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz", + "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==", + "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.6.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-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==" + }, + "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-promise": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.2.0.tgz", + "integrity": "sha512-aAYW0yWvwS02VdIrzZApRaIrZL71ckCl0/biZPXFvueWGMXzvRbDf4G2l1yU+pA2fiYxu+hYZIFztCuloK1MDQ==", + "requires": { + "assert-options": "0.8.0", + "pg": "8.9.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + } + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "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.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.2.0", + "ipaddr.js": "1.9.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" + } + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" + }, + "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==" + }, + "raw-body": { + "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.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "response-time": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", + "integrity": "sha512-MUIDaDQf+CVqflfTdQ5yam+aYCkXj1PY8fjlPDQ6ppxJlmgZb864pHtA750mayywNg8tx4rS7qH9JXd/OF+3gw==", + "requires": { + "depd": "~1.1.0", + "on-headers": "~1.0.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + } + } + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "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": "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": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "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.18.0" + } + }, + "setprototypeof": { + "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", + "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" + } + }, + "spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==" + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, + "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==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "validator": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "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 6474fd7..80294fe 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,17 @@ -{ +{ "name": "skrytka", "version": "0.0.1", "description": "Potężna aplikacja Skrytka.App!", "main": "app.js", "scripts": { "start": "node app.js" + }, "author": "Zespół Skrytka.App", "license": "ISC", "dependencies": { "connect-pg-simple": "^8.0.0", + "dotenv": "^16.0.3", "express": "^4.18.2", "express-session": "^1.17.3", "pg": "^8.9.0", @@ -17,4 +19,4 @@ "response-time": "^2.3.2", "validator": "^13.9.0" } -} +} \ No newline at end of file diff --git a/skrytka_create_db.sql b/skrytka_create_db.sql index eef6c14..f5312c5 100644 --- a/skrytka_create_db.sql +++ b/skrytka_create_db.sql @@ -1,11 +1,23 @@ +SET client_encoding TO 'UTF8'; + DROP TABLE IF EXISTS osp_unit CASCADE; +DROP TABLE IF EXISTS fire_truck CASCADE; +DROP TABLE IF EXISTS truck_side CASCADE; +DROP TABLE IF EXISTS cache CASCADE; +DROP TABLE IF EXISTS account CASCADE; +DROP TABLE IF EXISTS equipment CASCADE; +DROP TABLE IF EXISTS score CASCADE; +DROP TABLE IF EXISTS permission CASCADE; +DROP TABLE IF EXISTS accounts_permissions CASCADE; +DROP TABLE IF EXISTS event CASCADE; +DROP TABLE IF EXISTS user_session; + CREATE TABLE osp_unit ( id SERIAL PRIMARY KEY, name VARCHAR(128) NOT NULL, locality VARCHAR(64) NOT NULL CHECK(locality SIMILAR TO '[A-ZĄĆĘŁŃÓŚŹŻ][A-zĄĆĘŁŃÓŚŹŻząćęłńóśźż. ]+') ); -DROP TABLE IF EXISTS fire_truck CASCADE; CREATE TABLE fire_truck ( id SERIAL PRIMARY KEY, name VARCHAR(64) NOT NULL, @@ -13,7 +25,6 @@ CREATE TABLE fire_truck ( osp_unit_id INTEGER NOT NULL REFERENCES osp_unit ON DELETE CASCADE ON UPDATE CASCADE ); -DROP TABLE IF EXISTS truck_side CASCADE; CREATE TABLE truck_side ( id SERIAL PRIMARY KEY, image_path VARCHAR(128) NOT NULL CHECK(image_path ~ '^([0-9a-z_]+/?)*[0-9a-z_]+\.((png)|(jpg)|(jpeg)|(webp))$'), @@ -21,7 +32,6 @@ CREATE TABLE truck_side ( fire_truck_id INTEGER REFERENCES fire_truck ON DELETE CASCADE ON UPDATE CASCADE ); -DROP TABLE IF EXISTS cache CASCADE; CREATE TABLE cache ( id SERIAL PRIMARY KEY, name VARCHAR(64), @@ -34,7 +44,6 @@ CREATE TABLE cache ( truck_side INTEGER NOT NULL REFERENCES truck_side ON DELETE CASCADE ON UPDATE CASCADE ); -DROP TABLE IF EXISTS account CASCADE; CREATE TABLE account ( nickname VARCHAR(32) CHECK(nickname SIMILAR TO '[0-9A-z_żółćęśąźńŻÓŁĆĘŚĄŹŃ]{3,}') PRIMARY KEY, name VARCHAR(32) CHECK(name SIMILAR TO '[A-ZŻÓŁĆĘŚĄŹŃ][a-zżółćęśąźń]+'), @@ -43,14 +52,12 @@ CREATE TABLE account ( default_osp INTEGER REFERENCES osp_unit ON DELETE SET NULL ON UPDATE CASCADE ); -DROP TABLE IF EXISTS equipment CASCADE; CREATE TABLE equipment ( id SERIAL PRIMARY KEY, name VARCHAR(64) NOT NULL CHECK(LENGTH(name) > 0), cache_id INTEGER NOT NULL REFERENCES cache ON DELETE CASCADE ON UPDATE CASCADE ); -DROP TABLE IF EXISTS score; CREATE TABLE score ( id SERIAL PRIMARY KEY, account_nickname VARCHAR(32) REFERENCES account ON DELETE CASCADE ON UPDATE CASCADE, @@ -60,7 +67,6 @@ CREATE TABLE score ( seconds INTEGER NOT NULL CHECK(seconds > 0) ); -DROP TABLE IF EXISTS permission CASCADE; CREATE TABLE permission ( code VARCHAR(64) CHECK(code ~ '^[0-9A-Z_]{3,}$') PRIMARY KEY, name VARCHAR(64) CHECK(LENGTH(name) >= 3), @@ -68,7 +74,6 @@ CREATE TABLE permission ( ); -- PYTANIE: Czy aktualizować permission code przy zmianie czy na NULL? -DROP TABLE IF EXISTS accounts_permissions CASCADE; CREATE TABLE accounts_permissions ( account_nickname VARCHAR(32) REFERENCES account ON DELETE CASCADE ON UPDATE CASCADE, permission_code VARCHAR(64) REFERENCES permission ON DELETE CASCADE ON UPDATE CASCADE, @@ -76,7 +81,6 @@ CREATE TABLE accounts_permissions ( CONSTRAINT pk_users_permissions PRIMARY KEY (account_nickname, permission_code, osp_unit_id) ); -DROP TABLE IF EXISTS event CASCADE; CREATE TABLE event ( id SERIAL PRIMARY KEY, name VARCHAR(64) NOT NULL CHECK(LENGTH(name) >= 3), @@ -87,3 +91,14 @@ CREATE TABLE event ( account_nickname VARCHAR(32) REFERENCES account ON DELETE SET NULL ON UPDATE CASCADE, osp_unit_id INTEGER REFERENCES osp_unit ON DELETE CASCADE ON UPDATE CASCADE ); + +CREATE TABLE user_session ( + "sid" varchar NOT NULL COLLATE "default", + "sess" json NOT NULL, + "expire" timestamp(6) NOT NULL +) +WITH (OIDS=FALSE); + +ALTER TABLE user_session ADD CONSTRAINT "session_pkey" PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE; + +CREATE INDEX "IDX_session_expire" ON user_session ("expire"); \ No newline at end of file diff --git a/skrytka_create_utils.sql b/skrytka_create_utils.sql index 32c5856..584176f 100644 --- a/skrytka_create_utils.sql +++ b/skrytka_create_utils.sql @@ -1,12 +1,15 @@ DROP VIEW IF EXISTS units_list; CREATE VIEW units_list AS SELECT id, name, locality FROM osp_unit; - + DROP FUNCTION IF EXISTS get_units_list; CREATE FUNCTION get_units_list(prefix VARCHAR) RETURNS TABLE (id INTEGER, name VARCHAR, locality VARCHAR) AS $func$ - SELECT * FROM units_list WHERE locality LIKE prefix || '%' OR name LIKE prefix || '%' ORDER BY locality, name LIMIT 3; + SELECT * FROM units_list + WHERE LOWER(locality) LIKE LOWER('%' || prefix || '%') + OR LOWER(name) LIKE LOWER('%' || prefix || '%') + ORDER BY locality, name LIMIT 3; $func$ LANGUAGE SQL; @@ -62,4 +65,4 @@ LANGUAGE SQL; DROP VIEW IF EXISTS trucks_list; CREATE VIEW trucks_list AS - SELECT id, name, image_path FROM fire_truck; \ No newline at end of file + SELECT id, name, image_path FROM fire_truck; \ No newline at end of file