From 14b29eb3bee69c94aed71ab63bdeecb0c6e33906 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 14 Feb 2024 13:37:25 +0100 Subject: [PATCH 01/29] [Task] #43 create color pallette via atmos --- src/httpdocs/color-table.svg | 110 +++++++++++++++++++++++++++++++++++ src/httpdocs/css/colors.css | 52 +++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/httpdocs/color-table.svg create mode 100644 src/httpdocs/css/colors.css diff --git a/src/httpdocs/color-table.svg b/src/httpdocs/color-table.svg new file mode 100644 index 0000000..32ab8a2 --- /dev/null +++ b/src/httpdocs/color-table.svg @@ -0,0 +1,110 @@ + + + + + main + + + + + info + + + + + alert + + + + + success + + + + + neutral + + + + + + + 900 + + + + + 750 + + + + + 625 + + + + + 500 + + + + + 375 + + + + + 250 + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/httpdocs/css/colors.css b/src/httpdocs/css/colors.css new file mode 100644 index 0000000..8714fd9 --- /dev/null +++ b/src/httpdocs/css/colors.css @@ -0,0 +1,52 @@ +/* +Main: #f90 +Info: #231aee +Danger: #ff0000 +Success: #59ec04 +Neutral: #131211 +*/ + +/* main */ +--main-900: oklch(10% 0.02 64.55); +--main-750: oklch(25% 0.056 64.55); +--main-625: oklch(37.5% 0.085 64.55); +--main-500: oklch(50% 0.114 64.55); +--main-375: oklch(62.5% 0.142 64.55); +--main-250: oklch(77.2% 0.1738 64.55); /* base */ +--main-100: oklch(90% 0.06 64.55); + +/* info */ +--info-900: oklch(10% 0.055 268.01); +--info-750: oklch(25% 0.158 268.01); +--info-625: oklch(37.5% 0.237 268.01); +--info-500: oklch(44.87% 0.2838 268.01); /* base */ +--info-375: oklch(62.5% 0.19 268.01); +--info-250: oklch(77.2% 0.109 268.01); +--info-100: oklch(90% 0.04 268.01); + +/* alert */ +--alert-900: oklch(10% 0.036 29.23); +--alert-750: oklch(25% 0.103 29.23); +--alert-625: oklch(37.5% 0.154 29.23); +--alert-500: oklch(47.4% 0.195 29.23); +--alert-375: oklch(62.8% 0.2577 29.23); /* base */ +--alert-250: oklch(77.2% 0.133 29.23); +--alert-100: oklch(90% 0.045 29.23); + +/* success */ +--success-900: oklch(10% 0.029 138.96); +--success-750: oklch(25% 0.083 138.96); +--success-625: oklch(37.5% 0.124 138.96); +--success-500: oklch(47.4% 0.157 138.96); +--success-375: oklch(62.5% 0.208 138.96); +--success-250: oklch(83% 0.2607 138.96); /* base */ +--success-100: oklch(90% 0.201 138.96); + +/* neutral */ +--neutral-900: oklch(10% 0.001 67.66); +--neutral-750: oklch(18.3% 0.0026 67.66); /* base */ +--neutral-625: oklch(37.5% 0.006 67.66); +--neutral-500: oklch(47.4% 0.007 67.66); +--neutral-375: oklch(62.5% 0.009 67.66); +--neutral-250: oklch(77.2% 0.011 67.66); +--neutral-100: oklch(90% 0.004 67.66); \ No newline at end of file From 513315d21cf1ff764975d19539108f74a8606cdc Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 14 Feb 2024 13:37:25 +0100 Subject: [PATCH 02/29] [Task] #43 create color pallette via atmos --- httpdocs/color-table.svg | 104 +++++++++++++++++++++++++++++++++++++++ httpdocs/css/colors.css | 48 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 httpdocs/color-table.svg create mode 100644 httpdocs/css/colors.css diff --git a/httpdocs/color-table.svg b/httpdocs/color-table.svg new file mode 100644 index 0000000..da42a97 --- /dev/null +++ b/httpdocs/color-table.svg @@ -0,0 +1,104 @@ + + + + + + main + + + + + info + + + + + alert + + + + + success + + + + + neutral + + + + + + + 900 + + + + + 750 + + + + + 625 + + + + + 500 + + + + + 375 + + + + + 250 + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/httpdocs/css/colors.css b/httpdocs/css/colors.css new file mode 100644 index 0000000..715d846 --- /dev/null +++ b/httpdocs/css/colors.css @@ -0,0 +1,48 @@ +/* +created by atmos https://app.atmos.style/65cc9eaec76d443c0a796d4b +*/ + +/* main */ +--main-900: oklch(10% 0.02 64.55); +--main-750: oklch(25% 0.056 64.55); +--main-625: oklch(37.5% 0.085 64.55); +--main-500: oklch(50% 0.114 64.55); +--main-375: oklch(62.5% 0.142 64.55); +--main-250: oklch(77.2% 0.1738 64.55); /* base */ +--main-100: oklch(90% 0.06 64.55); + +/* info */ +--info-900: oklch(10% 0.055 268.01); +--info-750: oklch(25% 0.158 268.01); +--info-625: oklch(37.5% 0.237 268.01); +--info-500: oklch(44.87% 0.2838 268.01); /* base */ +--info-375: oklch(62.5% 0.19 268.01); +--info-250: oklch(77.2% 0.109 268.01); +--info-100: oklch(90% 0.04 268.01); + +/* alert */ +--alert-900: oklch(10% 0.036 29.23); +--alert-750: oklch(25% 0.103 29.23); +--alert-625: oklch(37.5% 0.154 29.23); +--alert-500: oklch(47.4% 0.195 29.23); +--alert-375: oklch(62.8% 0.2577 29.23); /* base */ +--alert-250: oklch(77.2% 0.133 29.23); +--alert-100: oklch(90% 0.045 29.23); + +/* success */ +--success-900: oklch(10% 0.029 138.96); +--success-750: oklch(25% 0.083 138.96); +--success-625: oklch(37.5% 0.124 138.96); +--success-500: oklch(47.4% 0.157 138.96); +--success-375: oklch(62.5% 0.208 138.96); +--success-250: oklch(83% 0.2607 138.96); /* base */ +--success-100: oklch(90% 0.201 138.96); + +/* neutral */ +--neutral-900: oklch(10% 0.001 67.66); +--neutral-750: oklch(18.3% 0.0026 67.66); /* base */ +--neutral-625: oklch(37.5% 0.006 67.66); +--neutral-500: oklch(47.4% 0.007 67.66); +--neutral-375: oklch(62.5% 0.009 67.66); +--neutral-250: oklch(77.2% 0.011 67.66); +--neutral-100: oklch(90% 0.004 67.66); \ No newline at end of file From b4f99c5ce2fab15efd65a73319c91967f1fc8e9c Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 14 Feb 2024 16:52:54 +0100 Subject: [PATCH 03/29] [Task] #43 cleanup colors and svg --- httpdocs/color-table.svg | 253 ++++++++++++++++++++++++--------------- httpdocs/css/colors.css | 98 ++++++++------- 2 files changed, 207 insertions(+), 144 deletions(-) diff --git a/httpdocs/color-table.svg b/httpdocs/color-table.svg index da42a97..d299941 100644 --- a/httpdocs/color-table.svg +++ b/httpdocs/color-table.svg @@ -1,104 +1,159 @@ - - - - - main - - - - - info - - - - - alert - - - - - success - - - - - neutral - - + + + + + HEX + + + main + + + info + + + alert + + + success + + + neutral + + + + OKLCH + - - - - 900 - - - - - 750 - - - - - 625 - - - - - 500 - - - - - 375 - - - - - 250 - - - - - 100 - - + + + 900 + + + + 750 + + + + 625 + + + 500 + + + 375 + + + 250 + + + 100 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/httpdocs/css/colors.css b/httpdocs/css/colors.css index 715d846..86bcf1d 100644 --- a/httpdocs/css/colors.css +++ b/httpdocs/css/colors.css @@ -1,48 +1,56 @@ /* created by atmos https://app.atmos.style/65cc9eaec76d443c0a796d4b -*/ - -/* main */ ---main-900: oklch(10% 0.02 64.55); ---main-750: oklch(25% 0.056 64.55); ---main-625: oklch(37.5% 0.085 64.55); ---main-500: oklch(50% 0.114 64.55); ---main-375: oklch(62.5% 0.142 64.55); ---main-250: oklch(77.2% 0.1738 64.55); /* base */ ---main-100: oklch(90% 0.06 64.55); - -/* info */ ---info-900: oklch(10% 0.055 268.01); ---info-750: oklch(25% 0.158 268.01); ---info-625: oklch(37.5% 0.237 268.01); ---info-500: oklch(44.87% 0.2838 268.01); /* base */ ---info-375: oklch(62.5% 0.19 268.01); ---info-250: oklch(77.2% 0.109 268.01); ---info-100: oklch(90% 0.04 268.01); -/* alert */ ---alert-900: oklch(10% 0.036 29.23); ---alert-750: oklch(25% 0.103 29.23); ---alert-625: oklch(37.5% 0.154 29.23); ---alert-500: oklch(47.4% 0.195 29.23); ---alert-375: oklch(62.8% 0.2577 29.23); /* base */ ---alert-250: oklch(77.2% 0.133 29.23); ---alert-100: oklch(90% 0.045 29.23); - -/* success */ ---success-900: oklch(10% 0.029 138.96); ---success-750: oklch(25% 0.083 138.96); ---success-625: oklch(37.5% 0.124 138.96); ---success-500: oklch(47.4% 0.157 138.96); ---success-375: oklch(62.5% 0.208 138.96); ---success-250: oklch(83% 0.2607 138.96); /* base */ ---success-100: oklch(90% 0.201 138.96); - -/* neutral */ ---neutral-900: oklch(10% 0.001 67.66); ---neutral-750: oklch(18.3% 0.0026 67.66); /* base */ ---neutral-625: oklch(37.5% 0.006 67.66); ---neutral-500: oklch(47.4% 0.007 67.66); ---neutral-375: oklch(62.5% 0.009 67.66); ---neutral-250: oklch(77.2% 0.011 67.66); ---neutral-100: oklch(90% 0.004 67.66); \ No newline at end of file +** base configuration colors ** +Main: #f90 +Info: #231aee +Danger: #ff0000 +Success: #59ec04 +Neutral: #131211 +*/ +:root { + /* main */ + --main-900: oklch(10% 0.02 64.55); + --main-750: oklch(25% 0.056 64.55); + --main-625: oklch(37.5% 0.085 64.55); + --main-500: oklch(50% 0.114 64.55); + --main-375: oklch(62.5% 0.142 64.55); + --main-250: oklch(77.2% 0.1738 64.55); /* base */ + --main-100: oklch(90% 0.06 64.55); + + /* info */ + --info-900: oklch(10% 0.055 268.01); + --info-750: oklch(25% 0.158 268.01); + --info-625: oklch(37.5% 0.237 268.01); + --info-500: oklch(44.87% 0.2838 268.01); /* base */ + --info-375: oklch(62.5% 0.19 268.01); + --info-250: oklch(77.2% 0.109 268.01); + --info-100: oklch(90% 0.04 268.01); + + /* alert */ + --alert-900: oklch(10% 0.036 29.23); + --alert-750: oklch(25% 0.103 29.23); + --alert-625: oklch(37.5% 0.154 29.23); + --alert-500: oklch(47.4% 0.195 29.23); + --alert-375: oklch(62.8% 0.2577 29.23); /* base */ + --alert-250: oklch(77.2% 0.133 29.23); + --alert-100: oklch(90% 0.045 29.23); + + /* success */ + --success-900: oklch(10% 0.029 138.96); + --success-750: oklch(25% 0.083 138.96); + --success-625: oklch(37.5% 0.124 138.96); + --success-500: oklch(47.4% 0.157 138.96); + --success-375: oklch(62.5% 0.208 138.96); + --success-250: oklch(83% 0.2607 138.96); /* base */ + --success-100: oklch(90% 0.201 138.96); + + /* neutral */ + --neutral-900: oklch(10% 0.001 67.66); + --neutral-750: oklch(18.3% 0.0026 67.66); /* base */ + --neutral-625: oklch(37.5% 0.006 67.66); + --neutral-500: oklch(47.4% 0.007 67.66); + --neutral-375: oklch(62.5% 0.009 67.66); + --neutral-250: oklch(77.2% 0.011 67.66); + --neutral-100: oklch(90% 0.004 67.66); + } \ No newline at end of file From cc6b8c3a7b2eb7d0fabae8a39394a7d96f0a35af Mon Sep 17 00:00:00 2001 From: Type-Style Date: Thu, 15 Feb 2024 18:19:05 +0100 Subject: [PATCH 04/29] [Task] #41 remove test code --- src/app.ts | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/app.ts b/src/app.ts index 1e59d96..7ac8fa1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,8 +2,6 @@ require('module-alias/register'); import { config } from 'dotenv'; import express from 'express'; import toobusy from 'toobusy-js'; -// import { rateLimit } from 'express-rate-limit'; -// import { slowDown } from 'express-slow-down'; import compression from 'compression'; import helmet from 'helmet'; import hpp from 'hpp'; @@ -15,9 +13,6 @@ import readRouter from '@src/controller/read'; import path from 'path'; import logger from '@src/scripts/logger'; -// console.log({ "status": 403, "name": "Error", "message": { "errors": [{ "type": "field", "msg": "Invalid value", "path": "user", "location": "query" }, { "type": "field", "msg": "is required", "path": "lat", "location": "query" }]}}); -// console.log(JSON.stringify({ "status": 403, "name": "Error", "message": { "errors": [{ "type": "field", "msg": "Invalid value", "path": "user", "location": "query" }, { "type": "field", "msg": "is required", "path": "lat", "location": "query" }]}}, null, 2)); - // configurations config(); // dotenv @@ -37,22 +32,8 @@ app.use((req, res, next) => { // clean up IPv6 Addresses logger.error(message); res.status(400).send(message); } - }) -// const slowDownLimiter = slowDown({ -// windowMs: 1 * 60 * 1000, -// delayAfter: 5, // Allow 5 requests per 15 minutes. -// delayMs: (used) => (used - 5) * 1000, // Add delay after delayAfter is reached -// }) - -// const rateLimiter = rateLimit({ -// windowMs: 1 * 60 * 1000, -// limit: 10, // Limit each IP per `window` -// standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers -// legacyHeaders: false, // Disable the `X-RateLimit-*` headers -// }) - app.use(helmet({ contentSecurityPolicy: { directives: { "default-src": "'self'", "img-src": "*" } } })); app.use(cache); app.use(compression()) @@ -60,10 +41,7 @@ app.use(hpp()); app.use(function (req, res, next) { // limit request size limit when recieving data if (!['POST', 'PUT', 'DELETE'].includes(req.method)) { return next(); } getRawBody(req, { length: req.headers['content-length'], limit: '1mb', encoding: true }, - function (err) { - if (err) { return next(err) } - next() - } + function (err) { if (err) { return next(err) } next() } ) }) From 14ca99df1f57beb7ad73f0dc606b3545a60a0f5a Mon Sep 17 00:00:00 2001 From: Type-Style Date: Thu, 15 Feb 2024 18:21:15 +0100 Subject: [PATCH 05/29] [CHANGE] #3 reconfigured nodemon to copy static files --- httpdocs/css/login.css | 0 httpdocs/login.html | 19 +++ nodemon-static.json | 8 ++ nodemon.json => nodemon-ts.json | 0 package-lock.json | 223 ++++++++++++++++++++++++++------ package.json | 11 +- src/httpdocs/color-table.svg | 110 ---------------- src/httpdocs/css/colors.css | 52 -------- src/middleware/limit.ts | 2 - 9 files changed, 219 insertions(+), 206 deletions(-) create mode 100644 httpdocs/css/login.css create mode 100644 httpdocs/login.html create mode 100644 nodemon-static.json rename nodemon.json => nodemon-ts.json (100%) delete mode 100644 src/httpdocs/color-table.svg delete mode 100644 src/httpdocs/css/colors.css diff --git a/httpdocs/css/login.css b/httpdocs/css/login.css new file mode 100644 index 0000000..e69de29 diff --git a/httpdocs/login.html b/httpdocs/login.html new file mode 100644 index 0000000..6fff199 --- /dev/null +++ b/httpdocs/login.html @@ -0,0 +1,19 @@ + + + + + + Login Form - Lorex + + + + + + + + + \ No newline at end of file diff --git a/nodemon-static.json b/nodemon-static.json new file mode 100644 index 0000000..221cf6c --- /dev/null +++ b/nodemon-static.json @@ -0,0 +1,8 @@ +{ + "watch": [ + "httpdocs" + ], + "ext": "*", + "ignore": [], + "exec": "cp -R httpdocs/ dist/" +} \ No newline at end of file diff --git a/nodemon.json b/nodemon-ts.json similarity index 100% rename from nodemon.json rename to nodemon-ts.json diff --git a/package-lock.json b/package-lock.json index 085305f..89a109c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "@tsconfig/node20": "^20.1.2", "@types/bcrypt": "^5.0.2", "@types/compression": "^1.7.5", - "@types/dotenv": "^8.2.0", "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", @@ -34,6 +33,7 @@ "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "axios": "^1.6.5", + "concurrently": "^8.2.2", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-plugin-jest": "^27.6.3", @@ -657,6 +657,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -1538,16 +1550,6 @@ "@types/node": "*" } }, - "node_modules/@types/dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==", - "deprecated": "This is a stub types definition. dotenv provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "dotenv": "*" - } - }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -2432,13 +2434,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2678,6 +2685,57 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2757,6 +2815,22 @@ "node": ">= 8" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2795,16 +2869,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { @@ -2942,6 +3019,25 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3691,15 +3787,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3825,11 +3925,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3858,9 +3958,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dependencies": { "function-bind": "^1.1.2" }, @@ -5662,6 +5762,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5775,6 +5881,21 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5857,14 +5978,16 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5896,6 +6019,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -5961,6 +6093,12 @@ "source-map": "^0.6.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6164,6 +6302,15 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", diff --git a/package.json b/package.json index 8b59750..2862435 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,13 @@ "main": "index.js", "scripts": { "prebuild": "rm -rf dist/*", - "build": "npx tsc && cp -R httpdocs/ dist/", - "build:prod": "npx tsc -p ./tsconfig.prod.json && cp -R httpdocs/ dist/", + "build": "npx tsc", + "build:prod": "npx tsc -p ./tsconfig.prod.json", + "postbuild": "cp -R httpdocs/ dist/", "start": "node dist/app.js", - "dev": "rm -rf dist/* && cp -R httpdocs/ dist/ && nodemon src/app.ts", + "dev": "npm run prebuild && npm run postbuild && concurrently \"npm:dev:*\"", + "dev:ts": "nodemon --config nodemon-ts.json", + "dev:static": "nodemon --config nodemon-static.json", "lint": "eslint . --fix", "test": "jest" }, @@ -19,7 +22,6 @@ "@tsconfig/node20": "^20.1.2", "@types/bcrypt": "^5.0.2", "@types/compression": "^1.7.5", - "@types/dotenv": "^8.2.0", "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", @@ -28,6 +30,7 @@ "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "axios": "^1.6.5", + "concurrently": "^8.2.2", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-plugin-jest": "^27.6.3", diff --git a/src/httpdocs/color-table.svg b/src/httpdocs/color-table.svg deleted file mode 100644 index 32ab8a2..0000000 --- a/src/httpdocs/color-table.svg +++ /dev/null @@ -1,110 +0,0 @@ - - - - - main - - - - - info - - - - - alert - - - - - success - - - - - neutral - - - - - - - 900 - - - - - 750 - - - - - 625 - - - - - 500 - - - - - 375 - - - - - 250 - - - - - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/httpdocs/css/colors.css b/src/httpdocs/css/colors.css deleted file mode 100644 index 8714fd9..0000000 --- a/src/httpdocs/css/colors.css +++ /dev/null @@ -1,52 +0,0 @@ -/* -Main: #f90 -Info: #231aee -Danger: #ff0000 -Success: #59ec04 -Neutral: #131211 -*/ - -/* main */ ---main-900: oklch(10% 0.02 64.55); ---main-750: oklch(25% 0.056 64.55); ---main-625: oklch(37.5% 0.085 64.55); ---main-500: oklch(50% 0.114 64.55); ---main-375: oklch(62.5% 0.142 64.55); ---main-250: oklch(77.2% 0.1738 64.55); /* base */ ---main-100: oklch(90% 0.06 64.55); - -/* info */ ---info-900: oklch(10% 0.055 268.01); ---info-750: oklch(25% 0.158 268.01); ---info-625: oklch(37.5% 0.237 268.01); ---info-500: oklch(44.87% 0.2838 268.01); /* base */ ---info-375: oklch(62.5% 0.19 268.01); ---info-250: oklch(77.2% 0.109 268.01); ---info-100: oklch(90% 0.04 268.01); - -/* alert */ ---alert-900: oklch(10% 0.036 29.23); ---alert-750: oklch(25% 0.103 29.23); ---alert-625: oklch(37.5% 0.154 29.23); ---alert-500: oklch(47.4% 0.195 29.23); ---alert-375: oklch(62.8% 0.2577 29.23); /* base */ ---alert-250: oklch(77.2% 0.133 29.23); ---alert-100: oklch(90% 0.045 29.23); - -/* success */ ---success-900: oklch(10% 0.029 138.96); ---success-750: oklch(25% 0.083 138.96); ---success-625: oklch(37.5% 0.124 138.96); ---success-500: oklch(47.4% 0.157 138.96); ---success-375: oklch(62.5% 0.208 138.96); ---success-250: oklch(83% 0.2607 138.96); /* base */ ---success-100: oklch(90% 0.201 138.96); - -/* neutral */ ---neutral-900: oklch(10% 0.001 67.66); ---neutral-750: oklch(18.3% 0.0026 67.66); /* base */ ---neutral-625: oklch(37.5% 0.006 67.66); ---neutral-500: oklch(47.4% 0.007 67.66); ---neutral-375: oklch(62.5% 0.009 67.66); ---neutral-250: oklch(77.2% 0.011 67.66); ---neutral-100: oklch(90% 0.004 67.66); \ No newline at end of file diff --git a/src/middleware/limit.ts b/src/middleware/limit.ts index a73e6c8..37100f8 100644 --- a/src/middleware/limit.ts +++ b/src/middleware/limit.ts @@ -3,11 +3,9 @@ import { rateLimit, Options as rateLimiterOptions } from 'express-rate-limit'; import { slowDown, Options as slowDownOptions } from 'express-slow-down'; import logger from '@src/scripts/logger'; - /* ** configurations */ - const baseOptions: Partial = { windowMs: 30 * 60 * 1000, skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") From d0774149d38f32b50d8b6147a2ef0e9c640372aa Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 16 Feb 2024 15:53:21 +0100 Subject: [PATCH 06/29] [Task] #18 replaced getRawBody with builtIn express urlEncoded --- package-lock.json | 87 +++++++++++++++++++++++++++++++++++------------ package.json | 2 +- src/app.ts | 15 ++++---- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89a109c..05d2c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "chalk": "^4.1.2", "compression": "^1.7.4", + "ejs": "^3.1.9", "express": "^4.18.2", "express-rate-limit": "^7.1.5", "express-slow-down": "^2.0.1", @@ -17,7 +18,6 @@ "helmet": "^7.1.0", "hpp": "^0.2.3", "module-alias": "^2.2.3", - "raw-body": "^2.5.2", "toobusy-js": "^0.5.1" }, "devDependencies": { @@ -2184,6 +2184,11 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2295,8 +2300,7 @@ "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==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -2348,7 +2352,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2682,8 +2685,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concurrently": { "version": "8.2.2", @@ -2978,6 +2980,20 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.640", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.640.tgz", @@ -3625,6 +3641,33 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4335,6 +4378,23 @@ "node": ">=8" } }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -5177,7 +5237,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5730,20 +5789,6 @@ "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", diff --git a/package.json b/package.json index 2862435..23940e3 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "dependencies": { "chalk": "^4.1.2", "compression": "^1.7.4", + "ejs": "^3.1.9", "express": "^4.18.2", "express-rate-limit": "^7.1.5", "express-slow-down": "^2.0.1", @@ -50,7 +51,6 @@ "helmet": "^7.1.0", "hpp": "^0.2.3", "module-alias": "^2.2.3", - "raw-body": "^2.5.2", "toobusy-js": "^0.5.1" }, "_moduleAliases": { diff --git a/src/app.ts b/src/app.ts index 7ac8fa1..bea26ab 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,7 +5,6 @@ import toobusy from 'toobusy-js'; import compression from 'compression'; import helmet from 'helmet'; import hpp from 'hpp'; -import getRawBody from 'raw-body'; import cache from './middleware/cache'; import * as error from "./middleware/error"; import writeRouter from '@src/controller/write'; @@ -18,6 +17,8 @@ config(); // dotenv const app = express(); +app.set('view engine', 'ejs'); + app.use((req, res, next) => { // monitor eventloop to block requests if busy if (toobusy()) { res.status(503).set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Retry-After': '60' }).send("I'm busy right now, sorry."); @@ -28,7 +29,7 @@ app.use((req, res, next) => { // clean up IPv6 Addresses res.locals.ip = req.ip.startsWith('::ffff:') ? req.ip.substring(7) : req.ip; next(); } else { - const message = "No IP provided" + const message = "No IP provided"; logger.error(message); res.status(400).send(message); } @@ -38,16 +39,12 @@ app.use(helmet({ contentSecurityPolicy: { directives: { "default-src": "'self'", app.use(cache); app.use(compression()) app.use(hpp()); -app.use(function (req, res, next) { // limit request size limit when recieving data - if (!['POST', 'PUT', 'DELETE'].includes(req.method)) { return next(); } - getRawBody(req, { length: req.headers['content-length'], limit: '1mb', encoding: true }, - function (err) { if (err) { return next(err) } next() } - ) -}) +app.use(express.urlencoded({ limit: '0.5kb', extended: true })); + // routes app.get('/', (req, res) => { - console.log(req.ip + " - " + res.locals.ip); + logger.log(req.ip + " - " + res.locals.ip, true); res.send('Hello World, via TypeScript and Node.js! ' + res.locals.ip); }); From 6ebece8a3eefbb0f2f1c41b379b99aff4406955f Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 16 Feb 2024 15:54:07 +0100 Subject: [PATCH 07/29] [Temp, Task] #43 basic login page, not yet used as middleware --- httpdocs/css/login.css | 20 ++++++++++++++++ httpdocs/login.html | 19 --------------- src/controller/read.ts | 52 ++++++++++++++++++++++++++++-------------- views/login-form.ejs | 21 +++++++++++++++++ 4 files changed, 76 insertions(+), 36 deletions(-) delete mode 100644 httpdocs/login.html create mode 100644 views/login-form.ejs diff --git a/httpdocs/css/login.css b/httpdocs/css/login.css index e69de29..b9d1d7c 100644 --- a/httpdocs/css/login.css +++ b/httpdocs/css/login.css @@ -0,0 +1,20 @@ +form { + margin-inline: auto; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + max-width: 500px; + gap: 10px; +} +input, button { + flex-grow: 1; +} +textarea, h1 { + flex-basis: 100%; +} +textarea { + height: 50vh; +} +h1 { + text-align: center; +} diff --git a/httpdocs/login.html b/httpdocs/login.html deleted file mode 100644 index 6fff199..0000000 --- a/httpdocs/login.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Login Form - Lorex - - - - - - - - - \ No newline at end of file diff --git a/src/controller/read.ts b/src/controller/read.ts index 02294dc..ed79df3 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -2,33 +2,51 @@ import express, { Request, Response, NextFunction } from 'express'; import * as file from '@src/scripts/file'; import { create as createError } from '@src/middleware/error'; import { validationResult, query } from 'express-validator'; +import logger from '@src/scripts/logger'; const router = express.Router(); -router.get('/', - [query('index').isInt().withMessage("not an integer") +router.get('/', + [query('index').isInt().withMessage("not an integer") .isLength({ max: 3 }).withMessage("not in range") .toInt()], - async function getRead(req:Request, res:Response, next:NextFunction) { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return createError(res, 400, JSON.stringify({errors: errors.array()}), next) - } + async function getRead(req: Request, res: Response, next: NextFunction) { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return createError(res, 400, JSON.stringify({ errors: errors.array() }), next) + } - const fileObj: File.Obj = file.getFile(res, next); - fileObj.content = await file.readAsJson(res, fileObj.path, next) - if (!fileObj.content || !Array.isArray(fileObj.content.entries)) { - return createError(res, undefined, `File corrupt: ${fileObj.path}`, next); - } + const fileObj: File.Obj = file.getFile(res, next); + fileObj.content = await file.readAsJson(res, fileObj.path, next) + if (!fileObj.content || !Array.isArray(fileObj.content.entries)) { + return createError(res, undefined, `File corrupt: ${fileObj.path}`, next); + } - let entries = fileObj.content.entries; + let entries = fileObj.content.entries; - if (req.query.index) { - entries = entries.slice(Number(req.query.index)); - } + if (req.query.index) { + entries = entries.slice(Number(req.query.index)); + } - res.json({entries}); + res.json({ entries }); + }); + + + +// TODO will be converted to middleware +// TODO write test for checking the limit on request body +router.get("/login/", async function login(req: Request, res: Response) { + logger.log("login was called"); + res.locals.text = "start"; + + res.render("login-form"); }); +router.post("/login/", async function postLogin(req: Request, res: Response) { + logger.log("post login was called"); + logger.log(req.body); + res.locals.text = "post recieved"; + res.render("login-form"); +}); export default router; \ No newline at end of file diff --git a/views/login-form.ejs b/views/login-form.ejs new file mode 100644 index 0000000..7964e2c --- /dev/null +++ b/views/login-form.ejs @@ -0,0 +1,21 @@ + + + + + + Login Form - Lorex + + + + + + + + + \ No newline at end of file From 5087ae258a738c97a9b9e60f44475aac940ad159 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Mon, 4 Mar 2024 21:33:32 +0100 Subject: [PATCH 08/29] [Temp] #43, create and validate json web token --- package-lock.json | 109 +++++++++++++++++++++++++++++++++++++++-- package.json | 2 + src/controller/read.ts | 54 ++++++++++++++++++-- src/models/entry.ts | 7 ++- views/login-form.ejs | 2 + 5 files changed, 163 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05d2c4c..8cd28a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "express-validator": "^7.0.1", "helmet": "^7.1.0", "hpp": "^0.2.3", + "jsonwebtoken": "^9.0.2", "module-alias": "^2.2.3", "toobusy-js": "^0.5.1" }, @@ -28,6 +29,7 @@ "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", + "@types/jsonwebtoken": "^9.0.6", "@types/node": "^20.10.6", "@types/toobusy-js": "^0.5.4", "@typescript-eslint/eslint-plugin": "^6.18.1", @@ -1638,6 +1640,15 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", + "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2422,6 +2433,11 @@ "node-int64": "^0.4.0" } }, + "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-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2975,6 +2991,14 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "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", @@ -5025,6 +5049,51 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "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": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "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/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5091,6 +5160,36 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "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.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -5103,11 +5202,15 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "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==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -5969,7 +6072,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -6718,8 +6820,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "17.7.2", diff --git a/package.json b/package.json index 23940e3..944db7b 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", + "@types/jsonwebtoken": "^9.0.6", "@types/node": "^20.10.6", "@types/toobusy-js": "^0.5.4", "@typescript-eslint/eslint-plugin": "^6.18.1", @@ -50,6 +51,7 @@ "express-validator": "^7.0.1", "helmet": "^7.1.0", "hpp": "^0.2.3", + "jsonwebtoken": "^9.0.2", "module-alias": "^2.2.3", "toobusy-js": "^0.5.1" }, diff --git a/src/controller/read.ts b/src/controller/read.ts index ed79df3..439df34 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -2,11 +2,14 @@ import express, { Request, Response, NextFunction } from 'express'; import * as file from '@src/scripts/file'; import { create as createError } from '@src/middleware/error'; import { validationResult, query } from 'express-validator'; +import jwt from 'jsonwebtoken'; import logger from '@src/scripts/logger'; +import { create } from 'domain'; const router = express.Router(); router.get('/', + isLoggedIn, [query('index').isInt().withMessage("not an integer") .isLength({ max: 3 }).withMessage("not in range") .toInt()], @@ -32,8 +35,6 @@ router.get('/', }); - -// TODO will be converted to middleware // TODO write test for checking the limit on request body router.get("/login/", async function login(req: Request, res: Response) { logger.log("login was called"); @@ -46,7 +47,54 @@ router.post("/login/", async function postLogin(req: Request, res: Response) { logger.log("post login was called"); logger.log(req.body); res.locals.text = "post recieved"; - res.render("login-form"); + + // TODO login authentication here + const validLogin = true; + if (!validLogin) { + return res.redirect("/read/login"); + } else { + createToken(req, res); + res.render("login-form"); // TODO Send Token only + } }); +function isLoggedIn(req: Request, res: Response) { + const result = validateToken(req, res); + if (!result) { + return res.redirect("/read/login"); + } +} + + +function validateToken(req: Request, res: Response) { + const key = process.env.KEYB; + const header = req.header('Authorization'); + const [type, token] = header ? header.split(' ') : ""; + let payload: string | jwt.JwtPayload = ""; + if (type === 'Bearer' && typeof token !== 'undefined' && key) { + try { + payload = jwt.verify(token, key); + res.status(200).send({ code: 0, message: `all good` }); + } catch (err) { + res.status(401).send({ code: 123, message: 'Invalid or expired token.' }); + } + console.log("payload: " + payload + " _ " + !!payload); + return !!payload; + } else { + return false; + } +} + + +function createToken(req: Request, res: Response) { + const key = process.env.KEYB; + if (!key) { throw new Error('KEYA is not defined in the environment variables'); } + const id = Math.random().toString(36).substring(2, 8); + const payload = { + _id: id + }; + const token = jwt.sign(payload, key, { expiresIn: 60 * 1 }); + res.locals.token = token; +} + export default router; \ No newline at end of file diff --git a/src/models/entry.ts b/src/models/entry.ts index 1f510bf..df52dad 100644 --- a/src/models/entry.ts +++ b/src/models/entry.ts @@ -118,13 +118,12 @@ export function checkTime(value: string) { function checkKey(value: string) { - if (process.env.NODE_ENV != "production" && value == "test") { - return true; // dev testing convenience - } - if (!value) { throw new Error('Key required'); } + if (process.env.NODE_ENV != "production" && value == "test") { + return true; // dev testing convenience + } value = decodeURIComponent(value); diff --git a/views/login-form.ejs b/views/login-form.ejs index 7964e2c..6d6826e 100644 --- a/views/login-form.ejs +++ b/views/login-form.ejs @@ -15,6 +15,8 @@ + +

Token: <%= locals.token %>

From 95b532211065843280702af130be47dae2369379 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Tue, 5 Mar 2024 01:45:43 +0100 Subject: [PATCH 09/29] [Task] #43, add slowDown and RateLimit for failed login attempts --- src/controller/read.ts | 41 +++++++++++++++++++++++++++-------------- src/controller/write.ts | 2 +- src/middleware/limit.ts | 31 +++++++++++++++++++++++-------- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index 439df34..e3de1c2 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -4,7 +4,8 @@ import { create as createError } from '@src/middleware/error'; import { validationResult, query } from 'express-validator'; import jwt from 'jsonwebtoken'; import logger from '@src/scripts/logger'; -import { create } from 'domain'; +import { crypt } from '@src/scripts/crypt'; +import { loginSlowDown, loginLimiter } from '@src/middleware/limit'; const router = express.Router(); @@ -43,29 +44,41 @@ router.get("/login/", async function login(req: Request, res: Response) { res.render("login-form"); }); -router.post("/login/", async function postLogin(req: Request, res: Response) { +router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { logger.log("post login was called"); logger.log(req.body); res.locals.text = "post recieved"; - - // TODO login authentication here - const validLogin = true; - if (!validLogin) { - return res.redirect("/read/login"); - } else { - createToken(req, res); - res.render("login-form"); // TODO Send Token only - } + loginLimiter(req, res, () => { + let validLogin = false; + const password = crypt(req.body.password); + // Loop through all environment variables + for (const key in process.env) { + if (!key.startsWith('USER')) { continue; } + if (key.substring(5) == req.body.user && + process.env[key] == password) { + validLogin = true; + break; + } + } + if (validLogin) { + const token = createToken(req, res); + res.json({ "token": token }); + } else { + res.redirect("/read/login"); + } + }); }); function isLoggedIn(req: Request, res: Response) { + console.log("login check"); const result = validateToken(req, res); if (!result) { - return res.redirect("/read/login"); + loginLimiter(req, res, () => { + res.redirect("/read/login"); + }); } } - function validateToken(req: Request, res: Response) { const key = process.env.KEYB; const header = req.header('Authorization'); @@ -85,7 +98,6 @@ function validateToken(req: Request, res: Response) { } } - function createToken(req: Request, res: Response) { const key = process.env.KEYB; if (!key) { throw new Error('KEYA is not defined in the environment variables'); } @@ -95,6 +107,7 @@ function createToken(req: Request, res: Response) { }; const token = jwt.sign(payload, key, { expiresIn: 60 * 1 }); res.locals.token = token; + return token; } export default router; \ No newline at end of file diff --git a/src/controller/write.ts b/src/controller/write.ts index 7b405ff..28aa439 100644 --- a/src/controller/write.ts +++ b/src/controller/write.ts @@ -43,7 +43,7 @@ async function writeData(req: Request, res: Response, next: NextFunction) { const router = express.Router(); - router.get('/', baseSlowDown, entry.validate, errorChecking, writeData); +router.get('/', baseSlowDown, entry.validate, errorChecking, writeData); router.head('/', baseSlowDown, entry.validate, errorChecking); export default router; \ No newline at end of file diff --git a/src/middleware/limit.ts b/src/middleware/limit.ts index 37100f8..5bdb109 100644 --- a/src/middleware/limit.ts +++ b/src/middleware/limit.ts @@ -8,7 +8,7 @@ import logger from '@src/scripts/logger'; */ const baseOptions: Partial = { windowMs: 30 * 60 * 1000, - skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") + //skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") } const baseSlowDownOptions: Partial = { @@ -22,6 +22,14 @@ const baseRateLimitOptions: Partial = { limit: 10, // Limit each IP per window standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers + handler: function rateHandler(req: Request, res: Response, next: NextFunction, options: rateLimiterOptions) { + if (!Object.prototype.hasOwnProperty.call(ipsThatReachedLimit, res.locals.ip)) { + logger.error(`[RateLimit] reached ${req.originalUrl}, ${res.locals.ip}, ${req.get('User-Agent')}`); + ipsThatReachedLimit[res.locals.ip] = { limitReachedOnError: true, time: Date.now() }; + } + res.status(options.statusCode).send(options.message); + } + } @@ -44,14 +52,21 @@ setInterval(() => { */ export const baseSlowDown = slowDown(baseSlowDownOptions); +export const loginSlowDown = slowDown({ + ...baseSlowDownOptions, + delayAfter: 1, // no delay for amount of attempts + delayMs: (used: number) => (used - 1) * 250, // Add delay after delayAfter is reached + }); + + export const errorRateLimiter = rateLimit({ ...baseRateLimitOptions, message: 'Too many requests with errors', - handler: (req: Request, res: Response, next: NextFunction, options: rateLimiterOptions) => { - if (!Object.prototype.hasOwnProperty.call(ipsThatReachedLimit, res.locals.ip)) { - logger.error(`[RateLimit] for invalid requests reached ${res.locals.ip}, ${req.get('User-Agent')}`); - ipsThatReachedLimit[res.locals.ip] = { limitReachedOnError: true, time: Date.now() }; - } - res.status(options.statusCode).send(options.message); - } +}); + +export const loginLimiter = rateLimit({ + ...baseRateLimitOptions, + windowMs: 3 * 60 * 1000, + limit: 3, + message: 'Too many failed login attempts', }); \ No newline at end of file From f01d5799abbe7feeeaf20506959a3258dd940f41 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 8 Mar 2024 14:47:25 +0100 Subject: [PATCH 10/29] [Task] #43, ratelimit for login page --- src/controller/read.ts | 6 +++--- src/middleware/limit.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index e3de1c2..305d891 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -5,7 +5,7 @@ import { validationResult, query } from 'express-validator'; import jwt from 'jsonwebtoken'; import logger from '@src/scripts/logger'; import { crypt } from '@src/scripts/crypt'; -import { loginSlowDown, loginLimiter } from '@src/middleware/limit'; +import { loginSlowDown, loginLimiter, baseSlowDown, baseRateLimiter } from '@src/middleware/limit'; const router = express.Router(); @@ -37,7 +37,7 @@ router.get('/', // TODO write test for checking the limit on request body -router.get("/login/", async function login(req: Request, res: Response) { +router.get("/login/", baseSlowDown, baseRateLimiter, async function login(req: Request, res: Response) { logger.log("login was called"); res.locals.text = "start"; @@ -110,4 +110,4 @@ function createToken(req: Request, res: Response) { return token; } -export default router; \ No newline at end of file +export default router; diff --git a/src/middleware/limit.ts b/src/middleware/limit.ts index 5bdb109..70e608a 100644 --- a/src/middleware/limit.ts +++ b/src/middleware/limit.ts @@ -8,7 +8,7 @@ import logger from '@src/scripts/logger'; */ const baseOptions: Partial = { windowMs: 30 * 60 * 1000, - //skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") + skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") } const baseSlowDownOptions: Partial = { @@ -28,8 +28,8 @@ const baseRateLimitOptions: Partial = { ipsThatReachedLimit[res.locals.ip] = { limitReachedOnError: true, time: Date.now() }; } res.status(options.statusCode).send(options.message); - } - + }, + message: "Too many attempts" } @@ -58,6 +58,7 @@ export const loginSlowDown = slowDown({ delayMs: (used: number) => (used - 1) * 250, // Add delay after delayAfter is reached }); +export const baseRateLimiter = rateLimit(baseRateLimitOptions); export const errorRateLimiter = rateLimit({ ...baseRateLimitOptions, @@ -68,5 +69,5 @@ export const loginLimiter = rateLimit({ ...baseRateLimitOptions, windowMs: 3 * 60 * 1000, limit: 3, - message: 'Too many failed login attempts', + message: 'Too many attempts without valid login', }); \ No newline at end of file From 24506ac31cb0ba12371f1a3c12b40ec13567e1d7 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 8 Mar 2024 15:23:33 +0100 Subject: [PATCH 11/29] [Task] #43, add global ratelimiter --- src/app.ts | 3 +++ src/controller/read.ts | 2 +- src/middleware/limit.ts | 7 +++---- src/tests/app.test.ts | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/app.ts b/src/app.ts index bea26ab..4b3f45f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,6 +11,7 @@ import writeRouter from '@src/controller/write'; import readRouter from '@src/controller/read'; import path from 'path'; import logger from '@src/scripts/logger'; +import { baseRateLimiter } from './middleware/limit'; // configurations config(); // dotenv @@ -39,12 +40,14 @@ app.use(helmet({ contentSecurityPolicy: { directives: { "default-src": "'self'", app.use(cache); app.use(compression()) app.use(hpp()); +app.use(baseRateLimiter); app.use(express.urlencoded({ limit: '0.5kb', extended: true })); // routes app.get('/', (req, res) => { logger.log(req.ip + " - " + res.locals.ip, true); + console.count(); res.send('Hello World, via TypeScript and Node.js! ' + res.locals.ip); }); diff --git a/src/controller/read.ts b/src/controller/read.ts index 305d891..cf55c64 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -44,7 +44,7 @@ router.get("/login/", baseSlowDown, baseRateLimiter, async function login(req: R res.render("login-form"); }); -router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { +router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response) { logger.log("post login was called"); logger.log(req.body); res.locals.text = "post recieved"; diff --git a/src/middleware/limit.ts b/src/middleware/limit.ts index 70e608a..eb9aea5 100644 --- a/src/middleware/limit.ts +++ b/src/middleware/limit.ts @@ -7,7 +7,7 @@ import logger from '@src/scripts/logger'; ** configurations */ const baseOptions: Partial = { - windowMs: 30 * 60 * 1000, + windowMs: 3 * 60 * 1000, skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") } @@ -19,7 +19,7 @@ const baseSlowDownOptions: Partial = { const baseRateLimitOptions: Partial = { ...baseOptions, - limit: 10, // Limit each IP per window + limit: 50, // Limit each IP per window standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers handler: function rateHandler(req: Request, res: Response, next: NextFunction, options: rateLimiterOptions) { @@ -29,7 +29,7 @@ const baseRateLimitOptions: Partial = { } res.status(options.statusCode).send(options.message); }, - message: "Too many attempts" + message: "Too many requests" } @@ -67,7 +67,6 @@ export const errorRateLimiter = rateLimit({ export const loginLimiter = rateLimit({ ...baseRateLimitOptions, - windowMs: 3 * 60 * 1000, limit: 3, message: 'Too many attempts without valid login', }); \ No newline at end of file diff --git a/src/tests/app.test.ts b/src/tests/app.test.ts index b7c01c2..621186f 100644 --- a/src/tests/app.test.ts +++ b/src/tests/app.test.ts @@ -12,4 +12,5 @@ describe('Server Status', () => { expect(serverStatus).toBe(200); }) -}) \ No newline at end of file +}) + From 67d8ce01f55c62930541d019847576da7f050ee4 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 8 Mar 2024 15:52:34 +0100 Subject: [PATCH 12/29] [fix] #7, improve error handeling for express errors --- src/middleware/error.ts | 7 +++++-- types.d.ts | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/middleware/error.ts b/src/middleware/error.ts index 2eaf6e8..8db272e 100644 --- a/src/middleware/error.ts +++ b/src/middleware/error.ts @@ -17,8 +17,11 @@ export function notFound(req: Request, res: Response, next: NextFunction) { next(error); } -export function handler(err: Error, req: Request, res: Response, next: NextFunction) { - const statusCode = res.statusCode !== 200 ? res.statusCode : 500; +export function handler(err: HttpError, req: Request, res: Response, next: NextFunction) { + let statusCode = res.statusCode; + if (statusCode == 200) { + statusCode = err.statusCode || err.status || 500 + } res.status(statusCode); let message; diff --git a/types.d.ts b/types.d.ts index 8931ce5..0be93e3 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1,6 +1,11 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ +interface HttpError extends Error { + status?: number; + statusCode?: number; +} + namespace RateLimit { interface obj { [key: string]: { From b022c31a572d3875f178eea80a2cee6ef580844c Mon Sep 17 00:00:00 2001 From: Type-Style Date: Sun, 10 Mar 2024 19:42:10 +0100 Subject: [PATCH 13/29] [Task] #43 rework body limitations to be checked only appropiate methods --- src/app.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index 4b3f45f..307d4c2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -41,7 +41,12 @@ app.use(cache); app.use(compression()) app.use(hpp()); app.use(baseRateLimiter); -app.use(express.urlencoded({ limit: '0.5kb', extended: true })); +app.use((req, res, next) => { // limit body for specific http methods + if(['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) { + return express.urlencoded({ limit: '0.5kb', extended: true })(req, res, next); + } + next(); +}); // routes From 81155fe09339c0291e75fe96352d4e0935682534 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Sun, 10 Mar 2024 19:42:52 +0100 Subject: [PATCH 14/29] [Task] #43 added check for data before using it --- src/controller/read.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index cf55c64..2e1540d 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -44,12 +44,15 @@ router.get("/login/", baseSlowDown, baseRateLimiter, async function login(req: R res.render("login-form"); }); -router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response) { +router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { logger.log("post login was called"); logger.log(req.body); res.locals.text = "post recieved"; loginLimiter(req, res, () => { let validLogin = false; + if (!req.body.user || !req.body.password) { + return createError(res, 422, "Body does not contain all expected information", next); + } const password = crypt(req.body.password); // Loop through all environment variables for (const key in process.env) { From 42d64a9d3a0ea064bc570aedb6861102c86319ce Mon Sep 17 00:00:00 2001 From: Type-Style Date: Sun, 10 Mar 2024 19:43:24 +0100 Subject: [PATCH 15/29] [Task] #43 check that body is ignored for GET in request --- src/tests/app.test.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tests/app.test.ts b/src/tests/app.test.ts index 621186f..e3592e1 100644 --- a/src/tests/app.test.ts +++ b/src/tests/app.test.ts @@ -1,4 +1,10 @@ -import axios from 'axios'; +import axios, { AxiosError } from 'axios'; +import qs from 'qs'; + +// random data of 0.75Kb pre GZIP +const randomData = qs.stringify({ + randomData: 'zIakHvSaXDdLtaPaL02LhGr4Fk6hzXF7tELeR733YZyyye1fnjNzrSlHgqcHU8BKqvE5Mi4B7iHIEdqjTelpoWyaqXqX8l6LzOvROAkTF4lrLXLD1oMHwDL9hnjR0P7g0BB2DqagKkoEYD4TmXeAXT9PbevbirWnOEzmIgSv65SlsNTRFYhmzWl93twXEBNclHTCTnZpf6diWoo8FsXZR49pe9v8J1paalh2LlbNF4ZUxMxNpSvSTRHxvkYo0TMpd0NqUSSLduLIWcE1jhCWnmHhsbohDZjFfMhVS8IFvCiu7rxfuWgwMPqD9FcBR79eqJBy2tjDMqA9S1k9k50AkbOQ6USVfEuqOtocqXonTvC3Jml90KYSs0gX4SSTFHofpMtbWIdkuKqZbitQjsPSBpTx27dhFZd8zT4erdE1ltHnq83pjEj9hQYqatmdzQGYnOyh9YDt8i1IJpk4DX83DLzw3QhaFPgZFq98SOj4ILytmBMIqOtD464aF8PKGq6g7dVqYOtyF2FwyY0xgA7LjGaFzaCDjnGEcPIMRc2tcorsuRPKUI0zcde1gYPsn4WKaKUp87hJd1YtorzCXPfvivfGGL5v1XaSzApc9BbZpbxcpTOi4Pgvx7hNafUcaCr6kcjp4JVYSktnnGCwEplgGEF8uCELsEBUi9LNhgsnwgoRh55TaJfcaFfGfYLokXYEgiyOwYhhdEY3kfjHZWAyFS4owCR6nMJGOGMHrQi1fBefdp28PQGwgELix5Vf8j6P' +}); describe('Server Status', () => { it('The server is running', async () => { @@ -12,5 +18,25 @@ describe('Server Status', () => { expect(serverStatus).toBe(200); }) + + it('server is ignoring requests with large body', async () => { + let serverStatus; + try { + const response = await axios.request({ + url: 'http://localhost:80/', + method: 'GET', + data: randomData, + }); + serverStatus = response.status; + } catch (error) { + console.error(error); + } + + expect(serverStatus).toBe(200); + }) + }) + + + From e8660473fe28c58bbc36d652ef75aed5e2948a99 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Sun, 10 Mar 2024 19:44:17 +0100 Subject: [PATCH 16/29] [Task] #43 login test --- src/tests/login.test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/tests/login.test.ts diff --git a/src/tests/login.test.ts b/src/tests/login.test.ts new file mode 100644 index 0000000..0f0efc1 --- /dev/null +++ b/src/tests/login.test.ts @@ -0,0 +1,41 @@ +import axios, { AxiosError } from 'axios'; +import qs from 'qs'; + +const userData = qs.stringify({ + user: "user", + password: "pass", + kilobyte: 'BPSwVu5vcvhWB17HcfIdyQK83mHJZKChv7zDihBJoifWK9EJFzK7VYf3kUgIqkc0io8DnSdewzc9U0GpzodQUFz0KLMaogsJruEbNSKvxnzUxS5UqSR64lLOmGumoPcn2InC0Ebpqfdiw90HFVZVlE3AY6Lhgbx8ILHi55RvpuGefDjBsePgow8Jh9sc8uVMCDglLmHQ0zk3PumMj0KlOszbMmX9fG0pPUsvLLc40biPBv9t97K3BFjYd3fGriRAQ3bFhGHBz2wzGbNQfHjKFDHuSvXOw8KReM7Wwd4Cl02QQ3RnDJVwH6cayh4BqFRXlP3i6uXw0l9qxdTv0q1CtV9rJho6zwo04gkGLvsS3AoYJQtHnOtUDdHPExu7l3nMKnPoRUwl7K2ePfHRuppFGqa43Q49bI04VjEhrB9k5S2uZJoxZdm63rIUrydmkZWdvBLVVZUIXwwIRnwLmoa26htKOz9FPKwWIPOM0NZj4jAoPhKqLDJwziNZn5UupzxBXoUM3BIyEk3K8GXs7eBduH9GCK2z2HPF0fJNtGiHASe7jCOC2mhSC5zGf9k0Yu1Ey63oQQZUtT7L57lp7UzPE2p6wzKDlbJZOn0Ho5OUfq3hE2C8fQRO1M6jDvRTiUIKhhxSHYd75Pvh4SG9lD8w5OHASusLDxmzKBUuG4GrGrQYpd0awJkqnKp5lk7psLD22YTtjTuDgI500tQLXSslxI1kIuB8RnN1LsxHyRQMVtXmNFOKKZV2U2frWpImIz2wSHCYrwRGygwDtiFfwtVwTapjhQqUMyb1vrWWi3EL1Y50fDCjDDHlvLI4N2tr2DULFf3a9m2SYWSoE6CYP4og5YyqjhqFQFm9urREInyZi9L0iQoMYxEqxTjGiVJfKmaSChSd0kQz6z2OdsxFbkMWJ2CAHOL1XNK8iFFSp93fIspaNMIonRVDCj4ZIP1LaPHDmIYcYTNU4k3Uz6VBHSIc1VjiG3sc2MZpKw9An0tJVlWbtVSk2RGYWIANAYyr5pQS' +}); + +describe('Login', () => { + it('form available', async () => { + let serverStatus = {}; + let response = {data: "", status: ""}; + try { + response = await axios.get('http://localhost:80/read/login'); + serverStatus = response.status; + } catch (error) { + console.error(error); + } + + expect(serverStatus).toBe(200); + expect(response.data).toContain(' { + try { + await axios.post('http://localhost:80/read/login', userData); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(413); + } else { + console.error(axiosError); + } + } + }) +}) + + + + From 594aec909a30adb9af740ecd3a46e6af948c63de Mon Sep 17 00:00:00 2001 From: Type-Style Date: Tue, 12 Mar 2024 16:53:59 +0100 Subject: [PATCH 17/29] [Task] #43 create tests for login --- src/controller/read.ts | 28 +++++++++---- src/tests/integration.test.ts | 76 ++++++++++++++++++++--------------- src/tests/login.test.ts | 18 ++++++++- types.d.ts | 11 +++-- 4 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index 2e1540d..a51262d 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -36,33 +36,42 @@ router.get('/', }); -// TODO write test for checking the limit on request body router.get("/login/", baseSlowDown, baseRateLimiter, async function login(req: Request, res: Response) { - logger.log("login was called"); res.locals.text = "start"; res.render("login-form"); }); router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { - logger.log("post login was called"); logger.log(req.body); res.locals.text = "post recieved"; loginLimiter(req, res, () => { let validLogin = false; - if (!req.body.user || !req.body.password) { + const user = req.body.user; + const password = req.body.password; + let cryptedPassword = ""; + if (!user || !password) { return createError(res, 422, "Body does not contain all expected information", next); } - const password = crypt(req.body.password); + + cryptedPassword = crypt(req.body.password); + // Loop through all environment variables for (const key in process.env) { if (!key.startsWith('USER')) { continue; } if (key.substring(5) == req.body.user && - process.env[key] == password) { + process.env[key] == cryptedPassword) { validLogin = true; break; } } + + + // only allow test user in test environment + if (user == "test" && validLogin && process.env.NODE_ENV != "production") { + validLogin = false; + } + if (validLogin) { const token = createToken(req, res); res.json({ "token": token }); @@ -73,7 +82,6 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res }); function isLoggedIn(req: Request, res: Response) { - console.log("login check"); const result = validateToken(req, res); if (!result) { loginLimiter(req, res, () => { @@ -95,6 +103,12 @@ function validateToken(req: Request, res: Response) { res.status(401).send({ code: 123, message: 'Invalid or expired token.' }); } console.log("payload: " + payload + " _ " + !!payload); + + // don't allow test user in production environment + if (typeof payload == "object" && !!payload && payload.user == "test" && process.env.NODE_ENV == "production") { + return false; + } + return !!payload; } else { return false; diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts index 6133c1d..85c37e7 100644 --- a/src/tests/integration.test.ts +++ b/src/tests/integration.test.ts @@ -204,37 +204,47 @@ describe('API calls', () => { describe('/read', () => { - test(`returns json`, async () => { - const response = await axios.get("http://localhost:80/read?index=0"); - expect(response.status).toBe(200); - expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); - }); - test(`index parameter to long`, async () => { - try { - await axios.get("http://localhost:80/read?index=1234"); - } catch (error) { - const axiosError = error as AxiosError; - if (axiosError.response) { - expect(axiosError.response.status).toBe(400); - } else { - console.error(axiosError); - } - } - }); - test(`index parameter to be a number`, async () => { - try { - await axios.get("http://localhost:80/read?index=a9"); - } catch (error) { - const axiosError = error as AxiosError; - if (axiosError.response) { - expect(axiosError.response.status).toBe(400); - } else { - console.error(axiosError); - } - } - }); - test(`index parameter reduces length of json`, async () => { - const response = await axios.get("http://localhost:80/read?index=999"); - expect(response.data.entries.length).toBe(1); - }); + let token = ""; + test(`redirect without logged in`, async () => { + const path = "/read"; + const response = await axios.get("http://localhost:80" + path); + expect(response.request.path).not.toBe(path); + expect(response.request.path).toContain("login"); + }); + + + + // test(`returns json`, async () => { + // const response = await axios.get("http://localhost:80/read?index=0"); + // expect(response.status).toBe(200); + // expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + // }); + // test(`index parameter to long`, async () => { + // try { + // await axios.get("http://localhost:80/read?index=1234"); + // } catch (error) { + // const axiosError = error as AxiosError; + // if (axiosError.response) { + // expect(axiosError.response.status).toBe(400); + // } else { + // console.error(axiosError); + // } + // } + // }); + // test(`index parameter to be a number`, async () => { + // try { + // await axios.get("http://localhost:80/read?index=a9"); + // } catch (error) { + // const axiosError = error as AxiosError; + // if (axiosError.response) { + // expect(axiosError.response.status).toBe(400); + // } else { + // console.error(axiosError); + // } + // } + // }); + // test(`index parameter reduces length of json`, async () => { + // const response = await axios.get("http://localhost:80/read?index=999"); + // expect(response.data.entries.length).toBe(1); + // }); }); \ No newline at end of file diff --git a/src/tests/login.test.ts b/src/tests/login.test.ts index 0f0efc1..a48429d 100644 --- a/src/tests/login.test.ts +++ b/src/tests/login.test.ts @@ -1,16 +1,21 @@ import axios, { AxiosError } from 'axios'; +import exp from 'constants'; import qs from 'qs'; -const userData = qs.stringify({ +const userData = qs.stringify({ user: "user", password: "pass", kilobyte: 'BPSwVu5vcvhWB17HcfIdyQK83mHJZKChv7zDihBJoifWK9EJFzK7VYf3kUgIqkc0io8DnSdewzc9U0GpzodQUFz0KLMaogsJruEbNSKvxnzUxS5UqSR64lLOmGumoPcn2InC0Ebpqfdiw90HFVZVlE3AY6Lhgbx8ILHi55RvpuGefDjBsePgow8Jh9sc8uVMCDglLmHQ0zk3PumMj0KlOszbMmX9fG0pPUsvLLc40biPBv9t97K3BFjYd3fGriRAQ3bFhGHBz2wzGbNQfHjKFDHuSvXOw8KReM7Wwd4Cl02QQ3RnDJVwH6cayh4BqFRXlP3i6uXw0l9qxdTv0q1CtV9rJho6zwo04gkGLvsS3AoYJQtHnOtUDdHPExu7l3nMKnPoRUwl7K2ePfHRuppFGqa43Q49bI04VjEhrB9k5S2uZJoxZdm63rIUrydmkZWdvBLVVZUIXwwIRnwLmoa26htKOz9FPKwWIPOM0NZj4jAoPhKqLDJwziNZn5UupzxBXoUM3BIyEk3K8GXs7eBduH9GCK2z2HPF0fJNtGiHASe7jCOC2mhSC5zGf9k0Yu1Ey63oQQZUtT7L57lp7UzPE2p6wzKDlbJZOn0Ho5OUfq3hE2C8fQRO1M6jDvRTiUIKhhxSHYd75Pvh4SG9lD8w5OHASusLDxmzKBUuG4GrGrQYpd0awJkqnKp5lk7psLD22YTtjTuDgI500tQLXSslxI1kIuB8RnN1LsxHyRQMVtXmNFOKKZV2U2frWpImIz2wSHCYrwRGygwDtiFfwtVwTapjhQqUMyb1vrWWi3EL1Y50fDCjDDHlvLI4N2tr2DULFf3a9m2SYWSoE6CYP4og5YyqjhqFQFm9urREInyZi9L0iQoMYxEqxTjGiVJfKmaSChSd0kQz6z2OdsxFbkMWJ2CAHOL1XNK8iFFSp93fIspaNMIonRVDCj4ZIP1LaPHDmIYcYTNU4k3Uz6VBHSIc1VjiG3sc2MZpKw9An0tJVlWbtVSk2RGYWIANAYyr5pQS' }); +const testData = qs.stringify({ + user: "TEST", + password: "test", +}); describe('Login', () => { it('form available', async () => { let serverStatus = {}; - let response = {data: "", status: ""}; + let response = { data: "", status: "" }; try { response = await axios.get('http://localhost:80/read/login'); serverStatus = response.status; @@ -34,6 +39,15 @@ describe('Login', () => { } } }) + + it('test user can login', async () => { + const response = await axios.post('http://localhost:80/read/login', testData); + + expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + expect(response).toHaveProperty('data.token'); + expect(response.data.token).not.toBeNull(); + console.log(response.data); + }) }) diff --git a/types.d.ts b/types.d.ts index 0be93e3..6bb5b4c 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1,11 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ - -interface HttpError extends Error { - status?: number; - statusCode?: number; -} - namespace RateLimit { interface obj { [key: string]: { @@ -123,3 +117,8 @@ namespace Models { total: number } } + +interface HttpError extends Error { + status?: number; + statusCode?: number; +} \ No newline at end of file From b1572cd12e2a16883c3cd4b5893b74557dca2367 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 11:43:51 +0100 Subject: [PATCH 18/29] [Task] #43 fine tune error handling --- src/controller/read.ts | 13 ++++---- src/tests/integration.test.ts | 63 ++++++++++++++++++++++++----------- src/tests/login.test.ts | 29 ++++++++-------- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index a51262d..356e8aa 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -54,12 +54,12 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res return createError(res, 422, "Body does not contain all expected information", next); } - cryptedPassword = crypt(req.body.password); + cryptedPassword = crypt(password); // Loop through all environment variables for (const key in process.env) { if (!key.startsWith('USER')) { continue; } - if (key.substring(5) == req.body.user && + if (key.substring(5) == user && process.env[key] == cryptedPassword) { validLogin = true; break; @@ -68,7 +68,7 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res // only allow test user in test environment - if (user == "test" && validLogin && process.env.NODE_ENV != "production") { + if (user == "test" && validLogin && process.env.NODE_ENV == "production") { validLogin = false; } @@ -76,7 +76,7 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res const token = createToken(req, res); res.json({ "token": token }); } else { - res.redirect("/read/login"); + return createError(res, 403, `invalid login credentials`, next); } }); }); @@ -102,7 +102,7 @@ function validateToken(req: Request, res: Response) { } catch (err) { res.status(401).send({ code: 123, message: 'Invalid or expired token.' }); } - console.log("payload: " + payload + " _ " + !!payload); + console.log("payload: " + JSON.stringify(payload) + " _ " + !!payload); // don't allow test user in production environment if (typeof payload == "object" && !!payload && payload.user == "test" && process.env.NODE_ENV == "production") { @@ -120,7 +120,8 @@ function createToken(req: Request, res: Response) { if (!key) { throw new Error('KEYA is not defined in the environment variables'); } const id = Math.random().toString(36).substring(2, 8); const payload = { - _id: id + _id: id, + user: req.body.user }; const token = jwt.sign(payload, key, { expiresIn: 60 * 1 }); res.locals.token = token; diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts index 85c37e7..2d052fb 100644 --- a/src/tests/integration.test.ts +++ b/src/tests/integration.test.ts @@ -1,4 +1,5 @@ import axios, { AxiosError } from 'axios'; +import qs from 'qs'; import fs from "fs"; import path from "path"; @@ -41,6 +42,17 @@ function isInRange(actual: string | number, expected: number, range: number) { return Math.abs(Number(actual) - expected) <= range; } +async function verifiedRequest(url:string, token:string) { + const response = await axios({ + method: 'get', + url: url, + headers: { + 'Authorization': `Bearer ${token}`, + } + }); + return response; +} + describe('HEAD /write', () => { it('with all parameters correctly set it should succeed', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 200); @@ -203,8 +215,12 @@ describe('API calls', () => { }); -describe('/read', () => { - let token = ""; +describe('read and login', () => { + let token = ""; + const testData = qs.stringify({ + user: "test", + password: "test", + }); test(`redirect without logged in`, async () => { const path = "/read"; const response = await axios.get("http://localhost:80" + path); @@ -212,25 +228,34 @@ describe('/read', () => { expect(response.request.path).toContain("login"); }); + it('test user can login', async () => { + const response = await axios.post('http://localhost:80/read/login', testData); + expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + expect(response).toHaveProperty('data.token'); + expect(response.data.token).not.toBeNull(); + token = response.data.token; + }) + + test('verified request returns json', async () => { + const response = await verifiedRequest("http://localhost:80/read?index=0", token); + expect(response.status).toBe(200); + expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + }); + + test(`index parameter to long`, async () => { + try { + await verifiedRequest("http://localhost:80/read?index=1234",token); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(400); + } else { + console.error(axiosError); + } + } + }); - // test(`returns json`, async () => { - // const response = await axios.get("http://localhost:80/read?index=0"); - // expect(response.status).toBe(200); - // expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); - // }); - // test(`index parameter to long`, async () => { - // try { - // await axios.get("http://localhost:80/read?index=1234"); - // } catch (error) { - // const axiosError = error as AxiosError; - // if (axiosError.response) { - // expect(axiosError.response.status).toBe(400); - // } else { - // console.error(axiosError); - // } - // } - // }); // test(`index parameter to be a number`, async () => { // try { // await axios.get("http://localhost:80/read?index=a9"); diff --git a/src/tests/login.test.ts b/src/tests/login.test.ts index a48429d..3fefe2d 100644 --- a/src/tests/login.test.ts +++ b/src/tests/login.test.ts @@ -1,15 +1,14 @@ import axios, { AxiosError } from 'axios'; -import exp from 'constants'; import qs from 'qs'; -const userData = qs.stringify({ +const userDataLarge = qs.stringify({ user: "user", password: "pass", kilobyte: 'BPSwVu5vcvhWB17HcfIdyQK83mHJZKChv7zDihBJoifWK9EJFzK7VYf3kUgIqkc0io8DnSdewzc9U0GpzodQUFz0KLMaogsJruEbNSKvxnzUxS5UqSR64lLOmGumoPcn2InC0Ebpqfdiw90HFVZVlE3AY6Lhgbx8ILHi55RvpuGefDjBsePgow8Jh9sc8uVMCDglLmHQ0zk3PumMj0KlOszbMmX9fG0pPUsvLLc40biPBv9t97K3BFjYd3fGriRAQ3bFhGHBz2wzGbNQfHjKFDHuSvXOw8KReM7Wwd4Cl02QQ3RnDJVwH6cayh4BqFRXlP3i6uXw0l9qxdTv0q1CtV9rJho6zwo04gkGLvsS3AoYJQtHnOtUDdHPExu7l3nMKnPoRUwl7K2ePfHRuppFGqa43Q49bI04VjEhrB9k5S2uZJoxZdm63rIUrydmkZWdvBLVVZUIXwwIRnwLmoa26htKOz9FPKwWIPOM0NZj4jAoPhKqLDJwziNZn5UupzxBXoUM3BIyEk3K8GXs7eBduH9GCK2z2HPF0fJNtGiHASe7jCOC2mhSC5zGf9k0Yu1Ey63oQQZUtT7L57lp7UzPE2p6wzKDlbJZOn0Ho5OUfq3hE2C8fQRO1M6jDvRTiUIKhhxSHYd75Pvh4SG9lD8w5OHASusLDxmzKBUuG4GrGrQYpd0awJkqnKp5lk7psLD22YTtjTuDgI500tQLXSslxI1kIuB8RnN1LsxHyRQMVtXmNFOKKZV2U2frWpImIz2wSHCYrwRGygwDtiFfwtVwTapjhQqUMyb1vrWWi3EL1Y50fDCjDDHlvLI4N2tr2DULFf3a9m2SYWSoE6CYP4og5YyqjhqFQFm9urREInyZi9L0iQoMYxEqxTjGiVJfKmaSChSd0kQz6z2OdsxFbkMWJ2CAHOL1XNK8iFFSp93fIspaNMIonRVDCj4ZIP1LaPHDmIYcYTNU4k3Uz6VBHSIc1VjiG3sc2MZpKw9An0tJVlWbtVSk2RGYWIANAYyr5pQS' }); -const testData = qs.stringify({ - user: "TEST", - password: "test", +const userData = qs.stringify({ + user: "user", + password: "pass" }); describe('Login', () => { @@ -29,7 +28,7 @@ describe('Login', () => { it('server is blocking requests with large body', async () => { try { - await axios.post('http://localhost:80/read/login', userData); + await axios.post('http://localhost:80/read/login', userDataLarge); } catch (error) { const axiosError = error as AxiosError; if (axiosError.response) { @@ -40,13 +39,17 @@ describe('Login', () => { } }) - it('test user can login', async () => { - const response = await axios.post('http://localhost:80/read/login', testData); - - expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); - expect(response).toHaveProperty('data.token'); - expect(response.data.token).not.toBeNull(); - console.log(response.data); + it('invalid login verification test', async () => { + try { + await axios.post('http://localhost:80/read/login', userData); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(403); + } else { + console.error(axiosError); + } + } }) }) From 32c9884c7873abf5f1372e3b3099dd6b812aa451 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 15:22:01 +0100 Subject: [PATCH 19/29] [Task] #43, finished login and jwt related tests --- src/tests/app.test.ts | 2 +- src/tests/integration.test.ts | 40 ++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/tests/app.test.ts b/src/tests/app.test.ts index e3592e1..efff7a3 100644 --- a/src/tests/app.test.ts +++ b/src/tests/app.test.ts @@ -19,7 +19,7 @@ describe('Server Status', () => { expect(serverStatus).toBe(200); }) - it('server is ignoring requests with large body', async () => { + it('server is ignoring body on GET requests', async () => { let serverStatus; try { const response = await axios.request({ diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts index 2d052fb..b075cf9 100644 --- a/src/tests/integration.test.ts +++ b/src/tests/integration.test.ts @@ -42,12 +42,12 @@ function isInRange(actual: string | number, expected: number, range: number) { return Math.abs(Number(actual) - expected) <= range; } -async function verifiedRequest(url:string, token:string) { +async function verifiedRequest(url: string, token: string) { const response = await axios({ method: 'get', url: url, headers: { - 'Authorization': `Bearer ${token}`, + 'Authorization': `Bearer ${token}`, } }); return response; @@ -231,6 +231,7 @@ describe('read and login', () => { it('test user can login', async () => { const response = await axios.post('http://localhost:80/read/login', testData); + expect(response.status).toBe(200); expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); expect(response).toHaveProperty('data.token'); expect(response.data.token).not.toBeNull(); @@ -245,7 +246,7 @@ describe('read and login', () => { test(`index parameter to long`, async () => { try { - await verifiedRequest("http://localhost:80/read?index=1234",token); + await verifiedRequest("http://localhost:80/read?index=1234", token); } catch (error) { const axiosError = error as AxiosError; if (axiosError.response) { @@ -256,20 +257,21 @@ describe('read and login', () => { } }); - // test(`index parameter to be a number`, async () => { - // try { - // await axios.get("http://localhost:80/read?index=a9"); - // } catch (error) { - // const axiosError = error as AxiosError; - // if (axiosError.response) { - // expect(axiosError.response.status).toBe(400); - // } else { - // console.error(axiosError); - // } - // } - // }); - // test(`index parameter reduces length of json`, async () => { - // const response = await axios.get("http://localhost:80/read?index=999"); - // expect(response.data.entries.length).toBe(1); - // }); + test(`index parameter to be a number`, async () => { + try { + await verifiedRequest("http://localhost:80/read?index=a9", token); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(400); + } else { + console.error(axiosError); + } + } + }); + test(`index parameter reduces length of json`, async () => { + const response = await verifiedRequest("http://localhost:80/read?index=999", token); + expect(response.status).toBe(200); + expect(response.data.entries.length).toBe(1); + }); }); \ No newline at end of file From eb51ec8a4e915b131c4283339edfe2a0b2871709 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 15:23:11 +0100 Subject: [PATCH 20/29] [Change] #34, no further need for test logging --- src/scripts/logger.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/scripts/logger.ts b/src/scripts/logger.ts index 67effdd..ddd608a 100644 --- a/src/scripts/logger.ts +++ b/src/scripts/logger.ts @@ -10,17 +10,16 @@ if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } -// const logPath = path.resolve(__dirname, '../httpdocs/log', 'start.txt'); const date = new Date().toLocaleString('de-DE', { hour12: false }); export default { - log: (message: string | JSON, showDateInConsole: boolean = false, showLogInTest = false) => { + log: (message: string | JSON, showDateInConsole: boolean = false) => { message = JSON.stringify(message); fs.appendFileSync(logPath, `${date} \t|\t ${message} \n`); if (showDateInConsole) { message = `${chalk.dim(date + ":")} ${message}`; } - if (process.env.NODE_ENV == "development" || showLogInTest && process.env.NODE_ENV == "test") { + if (process.env.NODE_ENV != "production") { console.log(message); } }, @@ -46,7 +45,7 @@ export default { content = content.replace(prefix[0], chalk.red(prefix[0])); } } - console.error(content); // log string right away or processed Object + console.error(content); } } From 3ca53224b817e2448269c2500d75d8a64b0bd70e Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 15:24:02 +0100 Subject: [PATCH 21/29] [Task] #43, fine tune jwt, middleware process improved --- src/controller/read.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controller/read.ts b/src/controller/read.ts index 356e8aa..f86cd3e 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -66,7 +66,6 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res } } - // only allow test user in test environment if (user == "test" && validLogin && process.env.NODE_ENV == "production") { validLogin = false; @@ -81,13 +80,14 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res }); }); -function isLoggedIn(req: Request, res: Response) { +function isLoggedIn(req: Request, res: Response, next: NextFunction) { const result = validateToken(req, res); if (!result) { loginLimiter(req, res, () => { res.redirect("/read/login"); }); } + next(); } function validateToken(req: Request, res: Response) { @@ -98,17 +98,15 @@ function validateToken(req: Request, res: Response) { if (type === 'Bearer' && typeof token !== 'undefined' && key) { try { payload = jwt.verify(token, key); - res.status(200).send({ code: 0, message: `all good` }); } catch (err) { - res.status(401).send({ code: 123, message: 'Invalid or expired token.' }); + res.status(401).send({ message: 'Invalid or expired token.' }); } - console.log("payload: " + JSON.stringify(payload) + " _ " + !!payload); // don't allow test user in production environment if (typeof payload == "object" && !!payload && payload.user == "test" && process.env.NODE_ENV == "production") { - return false; + return false; } - + return !!payload; } else { return false; @@ -118,13 +116,15 @@ function validateToken(req: Request, res: Response) { function createToken(req: Request, res: Response) { const key = process.env.KEYB; if (!key) { throw new Error('KEYA is not defined in the environment variables'); } - const id = Math.random().toString(36).substring(2, 8); + const today = new Date(); + const dateString = today.toLocaleDateString("de-DE", { weekday: "short", year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); const payload = { - _id: id, + date: dateString, user: req.body.user }; const token = jwt.sign(payload, key, { expiresIn: 60 * 1 }); res.locals.token = token; + logger.log(JSON.stringify(payload), true); return token; } From 4c94465c61c771902ba787967eb010e751a61309 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 22:50:26 +0100 Subject: [PATCH 22/29] [CHANGE] #43 created new esLint to have clientside js without ts --- .eslintrc.json | 2 +- .github/workflows/eslint.yml | 9 ++++----- httpdocs/js/.eslintrc.json | 12 ++++++++++++ package.json | 1 + src/tests/app.test.ts | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 httpdocs/js/.eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json index 30be6b6..aa4c83b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,6 +22,6 @@ //"@typescript-eslint/no-unused-vars": "warn" "jest/no-conditional-expect": "off" }, - "ignorePatterns": ["dist", "jest.config.js"] + "ignorePatterns": ["dist", "jest.config.js", "httpdocs"] } diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 3aed66a..95578d7 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -15,8 +15,7 @@ jobs: with: node-version: 16 - run: npm ci # or yarn install - - uses: sibiraj-s/action-eslint@v3 - with: - eslint-args: '--ignore-path=.gitignore --quiet' - extensions: 'js,jsx,ts,tsx' - annotations: true + - name: Lint server-side code + run: npx eslint src/ --fix + - name: Lint client-side code + run: npx eslint httpdocs/js/ --fix diff --git a/httpdocs/js/.eslintrc.json b/httpdocs/js/.eslintrc.json new file mode 100644 index 0000000..487f0f6 --- /dev/null +++ b/httpdocs/js/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "root": true, + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2021, + "sourceType": "module" + } +} diff --git a/package.json b/package.json index 944db7b..99ec65b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dev:ts": "nodemon --config nodemon-ts.json", "dev:static": "nodemon --config nodemon-static.json", "lint": "eslint . --fix", + "lint:client": "eslint httpdocs/js/ --fix", "test": "jest" }, "keywords": [], diff --git a/src/tests/app.test.ts b/src/tests/app.test.ts index efff7a3..44ba08d 100644 --- a/src/tests/app.test.ts +++ b/src/tests/app.test.ts @@ -1,4 +1,4 @@ -import axios, { AxiosError } from 'axios'; +import axios from 'axios'; import qs from 'qs'; // random data of 0.75Kb pre GZIP From 43e6db104b744cde32e05457925aea014a008008 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 13 Mar 2024 22:50:53 +0100 Subject: [PATCH 23/29] [Temp] #43 test to see new linter configuration --- httpdocs/js/login.js | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 httpdocs/js/login.js diff --git a/httpdocs/js/login.js b/httpdocs/js/login.js new file mode 100644 index 0000000..b8637ec --- /dev/null +++ b/httpdocs/js/login.js @@ -0,0 +1,2 @@ +let hello = ""; +eval('alert("hello")'); From 5bbc1bf48a9f48019413ec1ff34b810f3ce32a9d Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 15 Mar 2024 16:51:18 +0100 Subject: [PATCH 24/29] [Change] #43 switched to bcrypt for passwords --- package-lock.json | 420 ++++++++++++++++++++++++++++++++++++++--- package.json | 1 + src/controller/read.ts | 79 ++++---- src/models/entry.ts | 22 +-- src/scripts/crypt.ts | 25 ++- 5 files changed, 469 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cd28a3..bae40f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "lorex", "version": "0.0.1", "dependencies": { + "bcrypt": "^5.1.1", "chalk": "^4.1.2", "compression": "^1.7.4", "ejs": "^3.1.9", @@ -1385,6 +1386,61 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "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/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/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/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/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2045,8 +2101,7 @@ "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==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { "version": "1.3.8", @@ -2090,6 +2145,38 @@ "node": ">=0.4.0" } }, + "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/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2137,7 +2224,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2169,6 +2255,23 @@ "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/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2313,6 +2416,19 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2578,6 +2694,14 @@ "fsevents": "~2.3.2" } }, + "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/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2645,6 +2769,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "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/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2754,6 +2886,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "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.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2911,6 +3048,11 @@ "node": ">=0.4.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": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2928,6 +3070,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3039,8 +3189,7 @@ "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==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -3758,9 +3907,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -3807,11 +3956,32 @@ "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-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.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==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3835,6 +4005,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3896,7 +4085,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4024,6 +4212,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/hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -4076,6 +4269,39 @@ "node": ">= 0.8" } }, + "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/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4159,7 +4385,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -4221,7 +4446,6 @@ "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==", - "dev": true, "engines": { "node": ">=8" } @@ -5347,6 +5571,48 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "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/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "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/module-alias": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", @@ -5371,6 +5637,30 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5470,6 +5760,25 @@ "node": ">=8" } }, + "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.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -5501,7 +5810,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -5628,7 +5936,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5898,6 +6205,19 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5995,7 +6315,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -6124,6 +6443,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/set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -6191,8 +6515,7 @@ "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==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-update-notifier": { "version": "2.0.0", @@ -6281,6 +6604,14 @@ "node": ">= 0.8" } }, + "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-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6298,7 +6629,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -6312,7 +6642,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6374,6 +6703,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6449,6 +6794,11 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -6694,6 +7044,11 @@ "punycode": "^2.1.0" } }, + "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", @@ -6757,6 +7112,20 @@ "makeerror": "1.0.12" } }, + "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" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6772,6 +7141,14 @@ "node": ">= 8" } }, + "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/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -6792,8 +7169,7 @@ "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==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", diff --git a/package.json b/package.json index 99ec65b..6d5ba9d 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "typescript": "^5.3.3" }, "dependencies": { + "bcrypt": "^5.1.1", "chalk": "^4.1.2", "compression": "^1.7.4", "ejs": "^3.1.9", diff --git a/src/controller/read.ts b/src/controller/read.ts index f86cd3e..62ea931 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -4,7 +4,7 @@ import { create as createError } from '@src/middleware/error'; import { validationResult, query } from 'express-validator'; import jwt from 'jsonwebtoken'; import logger from '@src/scripts/logger'; -import { crypt } from '@src/scripts/crypt'; +import { crypt, compare } from '@src/scripts/crypt'; import { loginSlowDown, loginLimiter, baseSlowDown, baseRateLimiter } from '@src/middleware/limit'; const router = express.Router(); @@ -35,34 +35,34 @@ router.get('/', res.json({ entries }); }); - router.get("/login/", baseSlowDown, baseRateLimiter, async function login(req: Request, res: Response) { res.locals.text = "start"; - - res.render("login-form"); + loginLimiter(req, res, () => { + res.render("login-form"); + }); }); router.post("/login/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { logger.log(req.body); res.locals.text = "post recieved"; - loginLimiter(req, res, () => { + loginLimiter(req, res, async () => { let validLogin = false; const user = req.body.user; const password = req.body.password; - let cryptedPassword = ""; + let userFound = false; if (!user || !password) { return createError(res, 422, "Body does not contain all expected information", next); } - cryptedPassword = crypt(password); - // Loop through all environment variables for (const key in process.env) { if (!key.startsWith('USER')) { continue; } - if (key.substring(5) == user && - process.env[key] == cryptedPassword) { - validLogin = true; - break; + if (key.substring(5) == user) { + userFound = true; + const hash = process.env[key]; + if (hash) { + validLogin = await compare(password, hash); + } } } @@ -75,54 +75,63 @@ router.post("/login/", loginSlowDown, async function postLogin(req: Request, res const token = createToken(req, res); res.json({ "token": token }); } else { + if (!userFound) { + await crypt(password); // If no matching user is found, perform a dummy password comparison to prevent timing attacks + } return createError(res, 403, `invalid login credentials`, next); } }); }); function isLoggedIn(req: Request, res: Response, next: NextFunction) { - const result = validateToken(req, res); - if (!result) { - loginLimiter(req, res, () => { - res.redirect("/read/login"); - }); + const result = validateToken(req); + if (!result.success) { + createError(res, result.status, result.message || "", next) + } else { + next(); } - next(); } -function validateToken(req: Request, res: Response) { - const key = process.env.KEYB; +function validateToken(req: Request) { + const key = process.env.KEYA; const header = req.header('Authorization'); const [type, token] = header ? header.split(' ') : ""; let payload: string | jwt.JwtPayload = ""; - if (type === 'Bearer' && typeof token !== 'undefined' && key) { - try { - payload = jwt.verify(token, key); - } catch (err) { - res.status(401).send({ message: 'Invalid or expired token.' }); - } - // don't allow test user in production environment - if (typeof payload == "object" && !!payload && payload.user == "test" && process.env.NODE_ENV == "production") { - return false; + // Guard; aka early return for common failures before verifying authorization + if (!key) { return { success: false, status: 500, message: 'Wrong Configuration' }; } + if (!header) { return { success: false, status: 401, message: 'No Authorization header' }; } + if (type !== 'Bearer' || !token) { return { success: false, status: 400, message: 'Invalid Authorization header' }; } + + try { + payload = jwt.verify(token, key); + } catch (err) { + let message = "could not verify"; + if (err instanceof Error) { + message = `${err.name} - ${err.message}`; } - return !!payload; - } else { - return false; + return { success: false, status: 403, message: message }; } + + // don't allow test user in production environment + if (typeof payload == "object" && payload.user == "test" && process.env.NODE_ENV == "production") { + return { success: false, status: 403, message: 'test user not allowed on production' }; + } + + return { success: true }; } function createToken(req: Request, res: Response) { - const key = process.env.KEYB; - if (!key) { throw new Error('KEYA is not defined in the environment variables'); } + const key = process.env.KEYA; + if (!key) { throw new Error('Configuration is wrong'); } const today = new Date(); const dateString = today.toLocaleDateString("de-DE", { weekday: "short", year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); const payload = { date: dateString, user: req.body.user }; - const token = jwt.sign(payload, key, { expiresIn: 60 * 1 }); + const token = jwt.sign(payload, key, { expiresIn: 60 * 2 }); res.locals.token = token; logger.log(JSON.stringify(payload), true); return token; diff --git a/src/models/entry.ts b/src/models/entry.ts index df52dad..444a997 100644 --- a/src/models/entry.ts +++ b/src/models/entry.ts @@ -1,6 +1,6 @@ import { NextFunction, Request, Response } from 'express'; import { checkExact, query } from 'express-validator'; -import { crypt } from '@src/scripts/crypt'; +import { compare } from '@src/scripts/crypt'; import { create as createError } from '@src/middleware/error'; import * as file from '@src/scripts/file'; import { getTime } from '@src/scripts/time'; @@ -51,7 +51,7 @@ export const entry = { } } else { entries.push(entry); - } + } file.write(res, fileObj, next); @@ -117,22 +117,16 @@ export function checkTime(value: string) { } -function checkKey(value: string) { - if (!value) { - throw new Error('Key required'); - } +async function checkKey(value: string) { + if (!value) { throw new Error('Key required'); } + if (!process.env.KEYB) { throw new Error('Configuration wrong'); } if (process.env.NODE_ENV != "production" && value == "test") { return true; // dev testing convenience - } - - value = decodeURIComponent(value); + } - const hash = crypt(value); + const result = await compare(decodeURIComponent(value), process.env.KEYB); - if (process.env.KEYB != hash) { - if (process.env.NODE_ENV == "development") { - console.log(hash); - } + if (!result) { throw new Error('Key does not match'); } diff --git a/src/scripts/crypt.ts b/src/scripts/crypt.ts index b14ef42..1928c52 100644 --- a/src/scripts/crypt.ts +++ b/src/scripts/crypt.ts @@ -1,9 +1,20 @@ -import * as crypto from 'crypto'; +import * as bcrypt from "bcrypt"; +import crypto from "crypto"; -export const crypt = function (value:string) { +export const crypt = async function (password: string, quick = false) { + const extendedPassword = pepper(password); + return await bcrypt.hash(extendedPassword, quick ? 8 : 16); +}; + +export const compare = async function (password: string, hash: string) { + const extendedPassword = pepper(password); + return await bcrypt.compare(extendedPassword, hash) +} + +function pepper(password: string) { const key = process.env.KEYA; - if (!key) { - throw new Error('KEYA is not defined in the environment variables'); - } - return crypto.createHmac('sha256', key).update(value).digest("base64"); -}; \ No newline at end of file + if (!key) { throw new Error('KEYA is not defined in the environment variables'); } + return password + crypto.createHmac('sha256', key).digest("base64"); +} + + From 64c1201afd8e2a58dacbce56d87d0b7413af3c87 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Fri, 15 Mar 2024 16:53:15 +0100 Subject: [PATCH 25/29] [Task] #43 read return json in all cases --- src/tests/integration.test.ts | 40 ++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts index b075cf9..5bb4f57 100644 --- a/src/tests/integration.test.ts +++ b/src/tests/integration.test.ts @@ -54,43 +54,53 @@ async function verifiedRequest(url: string, token: string) { } describe('HEAD /write', () => { + // eslint-disable-next-line jest/expect-expect it('with all parameters correctly set it should succeed', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 200); }); + // eslint-disable-next-line jest/expect-expect it('without key it sends 403', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0", 403); }); + // eslint-disable-next-line jest/expect-expect it('with user length not equal to 2 it sends 422', async () => { await callServer(undefined, "user=x&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with lat not between -90 and 90 it sends 422', async () => { await callServer(undefined, "user=xx&lat=91.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with lon not between -180 and 180 it sends 422', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=181.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with timestamp to old sends 422', async () => { const timestamp = new Date().getTime() - 24 * 60 * 60 * 1000 * 2; // two days ago await callServer(timestamp, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); }) + // eslint-disable-next-line jest/expect-expect it('with hdop not between 0 and 100 it sends 422', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=101.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with altitude not between 0 and 10000 it sends 422', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=10001.000&speed=150.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with speed not between 0 and 300 it sends 422', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=301.000&heading=180.0&key=test", 422); }); + // eslint-disable-next-line jest/expect-expect it('with heading not between 0 and 360 it sends 422', async () => { await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=361.0&key=test", 422); }); @@ -195,6 +205,7 @@ describe("GET /write", () => { }); }); + describe('API calls', () => { test(`1000 api calls`, async () => { for (let i = 0; i < 1000; i++) { @@ -222,11 +233,17 @@ describe('read and login', () => { password: "test", }); test(`redirect without logged in`, async () => { - const path = "/read"; - const response = await axios.get("http://localhost:80" + path); - expect(response.request.path).not.toBe(path); - expect(response.request.path).toContain("login"); - }); + try { + await axios.get("http://localhost:80/read/"); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(401); + } else { + console.error(axiosError); + } + } + }); it('test user can login', async () => { const response = await axios.post('http://localhost:80/read/login', testData); @@ -238,6 +255,19 @@ describe('read and login', () => { token = response.data.token; }) + test('wrong token get error', async () => { + try { + await verifiedRequest("http://localhost:80/read?index=0", "justWrongValue"); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(403); + } else { + console.error(axiosError); + } + } + }); + test('verified request returns json', async () => { const response = await verifiedRequest("http://localhost:80/read?index=0", token); expect(response.status).toBe(200); From f213d02a0bc659bf7deb9b6ed2a29b4291519be8 Mon Sep 17 00:00:00 2001 From: Type-Style Date: Mon, 18 Mar 2024 16:11:15 +0100 Subject: [PATCH 26/29] [Task] #43 introduced color classes --- httpdocs/css/colors.css | 131 ++++++++++++++++++++++++++-------------- views/login-form.ejs | 3 +- 2 files changed, 87 insertions(+), 47 deletions(-) diff --git a/httpdocs/css/colors.css b/httpdocs/css/colors.css index 86bcf1d..d77a7fd 100644 --- a/httpdocs/css/colors.css +++ b/httpdocs/css/colors.css @@ -8,49 +8,88 @@ Danger: #ff0000 Success: #59ec04 Neutral: #131211 */ -:root { - /* main */ - --main-900: oklch(10% 0.02 64.55); - --main-750: oklch(25% 0.056 64.55); - --main-625: oklch(37.5% 0.085 64.55); - --main-500: oklch(50% 0.114 64.55); - --main-375: oklch(62.5% 0.142 64.55); - --main-250: oklch(77.2% 0.1738 64.55); /* base */ - --main-100: oklch(90% 0.06 64.55); - - /* info */ - --info-900: oklch(10% 0.055 268.01); - --info-750: oklch(25% 0.158 268.01); - --info-625: oklch(37.5% 0.237 268.01); - --info-500: oklch(44.87% 0.2838 268.01); /* base */ - --info-375: oklch(62.5% 0.19 268.01); - --info-250: oklch(77.2% 0.109 268.01); - --info-100: oklch(90% 0.04 268.01); - - /* alert */ - --alert-900: oklch(10% 0.036 29.23); - --alert-750: oklch(25% 0.103 29.23); - --alert-625: oklch(37.5% 0.154 29.23); - --alert-500: oklch(47.4% 0.195 29.23); - --alert-375: oklch(62.8% 0.2577 29.23); /* base */ - --alert-250: oklch(77.2% 0.133 29.23); - --alert-100: oklch(90% 0.045 29.23); - - /* success */ - --success-900: oklch(10% 0.029 138.96); - --success-750: oklch(25% 0.083 138.96); - --success-625: oklch(37.5% 0.124 138.96); - --success-500: oklch(47.4% 0.157 138.96); - --success-375: oklch(62.5% 0.208 138.96); - --success-250: oklch(83% 0.2607 138.96); /* base */ - --success-100: oklch(90% 0.201 138.96); - - /* neutral */ - --neutral-900: oklch(10% 0.001 67.66); - --neutral-750: oklch(18.3% 0.0026 67.66); /* base */ - --neutral-625: oklch(37.5% 0.006 67.66); - --neutral-500: oklch(47.4% 0.007 67.66); - --neutral-375: oklch(62.5% 0.009 67.66); - --neutral-250: oklch(77.2% 0.011 67.66); - --neutral-100: oklch(90% 0.004 67.66); - } \ No newline at end of file + +[class*=color] { + --lightness: 67.66%; + --hue: 64.55; + --chroma: 0.007; + color: oklch(var(--lightness) var(--chroma) var(--hue)); + + &[class*=l1] {--lightness: 10%;} + &[class*=l2] {--lightness: 25%;} + &[class*=l3] {--lightness: 37.5%;} + &[class*=l4] {--lightness: 50%;} + &[class*=l5] {--lightness: 62.5%;} + &[class*=l6] {--lightness: 77.2%;} + &[class*=l7] {--lightness: 90%;} + + &[class*=main] { + --lightness: 77.2%; + --chroma: 0.1738; + --hue: 64.55; + + &[class*=l1] {--chroma: 0.02;} + &[class*=l2] {--chroma: 0.056;} + &[class*=l3] {--chroma: 0.085;} + &[class*=l4] {--chroma: 0.114;} + &[class*=l5] {--chroma: 0.142;} + &[class*=l6] {--chroma: 0.1738;} /* base */ + &[class*=l7] {--chroma: 0.06;} + } + + &[class*=info] { + --lightness: 44.87%; + --chroma: 0.2838; + --hue: 268.0; + + &[class*=l1] {--chroma: 0.055;} + &[class*=l2] {--chroma: 0.158;} + &[class*=l3] {--chroma: 0.237;} + &[class*=l4] {--chroma: 0.2838;} /* base */ + &[class*=l5] {--chroma: 0.19;} + &[class*=l6] {--chroma: 0.109;} + &[class*=l7] {--chroma: 0.04;} + } + + &[class*=alert] { + --lightness: 62.8%; + --chroma: 0.2577; + --hue: 29.23; + + &[class*=l1] {--chroma: 0.036;} + &[class*=l2] {--chroma: 0.103;} + &[class*=l3] {--chroma: 0.154;} + &[class*=l4] {--chroma: 0.195;} + &[class*=l5] {--chroma: 0.2577;} /* base */ + &[class*=l6] {--chroma: 0.133;} + &[class*=l7] {--chroma: 0.045;} + } + + &[class*=success] { + --lightness: 83%; + --chroma: 0.2607; + --hue: 138.96; + + &[class*=l1] {--chroma: 0.029;} + &[class*=l2] {--chroma: 0.083;} + &[class*=l3] {--chroma: 0.124;} + &[class*=l4] {--chroma: 0.157;} + &[class*=l5] {--chroma: 0.208;} + &[class*=l6] {--chroma: 0.2607;} /* base */ + &[class*=l7] {--chroma: 0.201;} + } + + &[class*=neutral] { + --lightness: 18.3%; + --chroma: 0.0026; + --hue: 67.66; + + &[class*=l1] {--chroma: 0.001;} + &[class*=l2] {--chroma: 0.0026;} /* base */ + &[class*=l3] {--chroma: 0.006;} + &[class*=l4] {--chroma: 0.007;} + &[class*=l5] {--chroma: 0.009;} + &[class*=l6] {--chroma: 0.011;} + &[class*=l7] {--chroma: 0.004;} + } +} diff --git a/views/login-form.ejs b/views/login-form.ejs index 6d6826e..53b0e71 100644 --- a/views/login-form.ejs +++ b/views/login-form.ejs @@ -5,12 +5,13 @@ Login Form - Lorex +