From 7a5596a4a8d91301b38cdfb7a6c4f170fd7187a8 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 10:06:05 +0100 Subject: [PATCH 01/21] backend init --- backend/api/Dockerfile | 0 backend/db/Dockerfile | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 backend/api/Dockerfile create mode 100644 backend/db/Dockerfile diff --git a/backend/api/Dockerfile b/backend/api/Dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/backend/db/Dockerfile b/backend/db/Dockerfile new file mode 100644 index 0000000..e69de29 From c67f869d4de305e34d1b54632875724db555ba4d Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 10:50:31 +0100 Subject: [PATCH 02/21] added createSchema.sql, implemented Dockerfile --- backend/db/Dockerfile | 4 ++++ backend/db/createSchema.sql | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 backend/db/createSchema.sql diff --git a/backend/db/Dockerfile b/backend/db/Dockerfile index e69de29..6c9e911 100644 --- a/backend/db/Dockerfile +++ b/backend/db/Dockerfile @@ -0,0 +1,4 @@ +FROM postgres:15 + +RUN mkdir /docker-entrypoint-initdb.d +COPY createSchema.sql /docker-entrypoint-initdb.d \ No newline at end of file diff --git a/backend/db/createSchema.sql b/backend/db/createSchema.sql new file mode 100644 index 0000000..0041f5f --- /dev/null +++ b/backend/db/createSchema.sql @@ -0,0 +1,39 @@ +CREATE TABLE "user" ( + username VARCHAR(32) PRIMARY KEY +) + +CREATE TABLE game ( + id SERIAL PRIMARY KEY, + score INTEGER NOT NULL, + playtime TIME NOT NULL, + date DATE NOT NULL, + username VARCHAR(32) NOT NULL FOREIGN KEY REFERENCES "user" +) + +CREATE VIEW user_data AS ( + SELECT + username, + max(score) AS highscore, + sum(score) AS total_score, + sum(playtime) AS total_playtime, + avg(score) AS average_score, + count(*) AS games_played + FROM game + GROUP BY username +) + +CREATE VIEW lb_highscore AS ( + SELECT username, max(score) AS highscore FROM game GROUP BY username ORDER BY highscore DESC +) + +CREATE VIEW lb_highscore AS ( + SELECT username, sum(score) AS total_score, FROM game GROUP BY username ORDER BY total_score DESC +) + +CREATE VIEW lb_total_playtime AS ( + SELECT username, sum(playtime) AS total_playtime FROM game GROUP BY username ORDER BY total_playtime DESC +) + +CREATE VIEW lb_average_score AS ( + SELECT username, avg(score) AS average_score FROM game GROUP BY username ORDER BY average_score DESC +) \ No newline at end of file From b25d5cc0f4ca652888947c69c161b2aa352cd529 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 11:11:41 +0100 Subject: [PATCH 03/21] added docker-compose.yml in root, added backend/README.md --- backend/README.md | 27 +++++++++++++++++++++++++++ docker-compose.yml | 12 ++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 backend/README.md create mode 100644 docker-compose.yml diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..bbb4279 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,27 @@ +## Database Schema + +```mermaid +erDiagram + user { + string name PK + } + + game { + serial game_id PK + int score + time playtime + date date + string username FK + } + + user ||--O{ game : "played" + + user_data { + string username PK + int highscore + int total_score + int total_playtime + int average_score + int games_played + } +``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6ef3129 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.1' + +services: + db: + build: backend/db + environment: + POSTGRES_DB: rr + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - ./pgdata:/var/lib/postgresql/data + From 3f450109221763cfcaf28f3e1735c85040b248b0 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 11:13:46 +0100 Subject: [PATCH 04/21] added portmapping to docker-compose.yml --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 6ef3129..19b2122 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: POSTGRES_DB: rr POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" volumes: - ./pgdata:/var/lib/postgresql/data From 576f4a43338ee63ef702c4206951a8fdf84ba184 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 11:23:44 +0100 Subject: [PATCH 05/21] fixed createSchema.sql syntax, added container name --- backend/db/Dockerfile | 5 ++--- backend/db/createSchema.sql | 19 ++++++++++--------- docker-compose.yml | 1 + 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/backend/db/Dockerfile b/backend/db/Dockerfile index 6c9e911..880b4f2 100644 --- a/backend/db/Dockerfile +++ b/backend/db/Dockerfile @@ -1,4 +1,3 @@ -FROM postgres:15 +FROM postgres:15-alpine -RUN mkdir /docker-entrypoint-initdb.d -COPY createSchema.sql /docker-entrypoint-initdb.d \ No newline at end of file +COPY createSchema.sql /docker-entrypoint-initdb.d/createSchema.sql \ No newline at end of file diff --git a/backend/db/createSchema.sql b/backend/db/createSchema.sql index 0041f5f..26eed03 100644 --- a/backend/db/createSchema.sql +++ b/backend/db/createSchema.sql @@ -1,14 +1,15 @@ CREATE TABLE "user" ( username VARCHAR(32) PRIMARY KEY -) +); CREATE TABLE game ( id SERIAL PRIMARY KEY, score INTEGER NOT NULL, playtime TIME NOT NULL, date DATE NOT NULL, - username VARCHAR(32) NOT NULL FOREIGN KEY REFERENCES "user" -) + username VARCHAR(32) NOT NULL, + FOREIGN KEY (username) REFERENCES "user" +); CREATE VIEW user_data AS ( SELECT @@ -20,20 +21,20 @@ CREATE VIEW user_data AS ( count(*) AS games_played FROM game GROUP BY username -) +); CREATE VIEW lb_highscore AS ( SELECT username, max(score) AS highscore FROM game GROUP BY username ORDER BY highscore DESC -) +); CREATE VIEW lb_highscore AS ( - SELECT username, sum(score) AS total_score, FROM game GROUP BY username ORDER BY total_score DESC -) + SELECT username, sum(score) AS total_score FROM game GROUP BY username ORDER BY total_score DESC +); CREATE VIEW lb_total_playtime AS ( SELECT username, sum(playtime) AS total_playtime FROM game GROUP BY username ORDER BY total_playtime DESC -) +); CREATE VIEW lb_average_score AS ( SELECT username, avg(score) AS average_score FROM game GROUP BY username ORDER BY average_score DESC -) \ No newline at end of file +); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 19b2122..89b49f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ version: '3.1' services: db: build: backend/db + container_name: postgres-db environment: POSTGRES_DB: rr POSTGRES_USER: postgres From 1857b03687a68b1403dcbc1597142fc37098ec03 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 11:26:23 +0100 Subject: [PATCH 06/21] changed pgdata dir --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 89b49f4..337f4df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,5 +11,5 @@ services: ports: - "5432:5432" volumes: - - ./pgdata:/var/lib/postgresql/data + - ./backend/pgdata:/var/lib/postgresql/data From e70c9fd2e1a44d0f5b93bd18599b4dafd6f597ed Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 11:37:43 +0100 Subject: [PATCH 07/21] npm init api --- backend/api/package-lock.json | 1306 +++++++++++++++++++++++++++++++++ backend/api/package.json | 17 + backend/api/src/app.ts | 0 3 files changed, 1323 insertions(+) create mode 100644 backend/api/package-lock.json create mode 100644 backend/api/package.json create mode 100644 backend/api/src/app.ts diff --git a/backend/api/package-lock.json b/backend/api/package-lock.json new file mode 100644 index 0000000..b34a83c --- /dev/null +++ b/backend/api/package-lock.json @@ -0,0 +1,1306 @@ +{ + "name": "express-api", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "express-api", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "ts-node": "^10.9.1", + "typescript": "^4.9.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "peer": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "peer": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/backend/api/package.json b/backend/api/package.json new file mode 100644 index 0000000..c0646bc --- /dev/null +++ b/backend/api/package.json @@ -0,0 +1,17 @@ +{ + "name": "express-api", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "dev": "ts-node src/app.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "jweissen", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "ts-node": "^10.9.1", + "typescript": "^4.9.3" + } +} diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts new file mode 100644 index 0000000..e69de29 From 684ded6af0da49b9603979ec76631a4272bfa33c Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 22:13:24 +0100 Subject: [PATCH 08/21] fixed schema names --- backend/db/createSchema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/db/createSchema.sql b/backend/db/createSchema.sql index 26eed03..9d802a8 100644 --- a/backend/db/createSchema.sql +++ b/backend/db/createSchema.sql @@ -27,7 +27,7 @@ CREATE VIEW lb_highscore AS ( SELECT username, max(score) AS highscore FROM game GROUP BY username ORDER BY highscore DESC ); -CREATE VIEW lb_highscore AS ( +CREATE VIEW lb_total_score AS ( SELECT username, sum(score) AS total_score FROM game GROUP BY username ORDER BY total_score DESC ); From fc3a4b31c1ee732848e4153fac4694dd4ecdaba6 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 29 Nov 2022 23:52:54 +0100 Subject: [PATCH 09/21] express initialized with demo endpoint --- backend/api/package-lock.json | 311 +++++++++++++++++++++++++++++++++- backend/api/package.json | 10 +- backend/api/src/app.ts | 31 ++++ backend/api/tsconfig.json | 11 ++ 4 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 backend/api/tsconfig.json diff --git a/backend/api/package-lock.json b/backend/api/package-lock.json index b34a83c..64369aa 100644 --- a/backend/api/package-lock.json +++ b/backend/api/package-lock.json @@ -9,9 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "body-parser": "^1.20.1", "express": "^4.18.2", + "pg-promise": "^10.15.4", "ts-node": "^10.9.1", "typescript": "^4.9.3" + }, + "devDependencies": { + "@types/node": "^18.11.9" } }, "node_modules/@cspotcode/source-map-support": { @@ -70,8 +75,7 @@ "node_modules/@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "peer": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "node_modules/accepts": { "version": "1.3.8", @@ -114,6 +118,14 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/assert-options": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz", + "integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -137,6 +149,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -494,6 +514,11 @@ "node": ">= 0.8" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -507,6 +532,137 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-promise": { + "version": "10.15.4", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.15.4.tgz", + "integrity": "sha512-BKlHCMCdNUmF6gagVbehRWSEiVcZzPVltEx14OJExR9Iz9/1R6KETDWLLGv2l6yRqYFnEZZy1VDjRhArzeIGrw==", + "dependencies": { + "assert-options": "0.8.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -639,6 +795,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==", + "engines": { + "node": ">=4.5" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -750,6 +922,14 @@ "node": ">= 0.8" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -810,8 +990,7 @@ "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "peer": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "accepts": { "version": "1.3.8", @@ -842,6 +1021,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "assert-options": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz", + "integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA==" + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -861,6 +1045,11 @@ "unpipe": "1.0.0" } }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1127,6 +1316,11 @@ "ee-first": "1.1.1" } }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1137,6 +1331,100 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==" + }, + "pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "requires": {} + }, + "pg-promise": { + "version": "10.15.4", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.15.4.tgz", + "integrity": "sha512-BKlHCMCdNUmF6gagVbehRWSEiVcZzPVltEx14OJExR9Iz9/1R6KETDWLLGv2l6yRqYFnEZZy1VDjRhArzeIGrw==", + "requires": { + "assert-options": "0.8.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + } + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1233,6 +1521,16 @@ "object-inspect": "^1.9.0" } }, + "spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==" + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1297,6 +1595,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/backend/api/package.json b/backend/api/package.json index c0646bc..bb75f93 100644 --- a/backend/api/package.json +++ b/backend/api/package.json @@ -2,16 +2,22 @@ "name": "express-api", "version": "1.0.0", "description": "", - "main": "app.js", + "type": "module", "scripts": { - "dev": "ts-node src/app.ts", + "build": "tsc", + "dev": "ts-node-esm src/app.ts", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "jweissen", "license": "ISC", "dependencies": { + "body-parser": "^1.20.1", "express": "^4.18.2", + "pg-promise": "^10.15.4", "ts-node": "^10.9.1", "typescript": "^4.9.3" + }, + "devDependencies": { + "@types/node": "^18.11.9" } } diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index e69de29..d1469f7 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -0,0 +1,31 @@ +import express from 'express'; +import bodyParser from "body-parser"; +import pgPromise from "pg-promise"; +const pgp = pgPromise({}); +const db = pgp('postgres://postgres:postgres@localhost:5432/rr') +const app = express() +const port = 3000 + +app.use(bodyParser.json()) + +app.get('/test', (req, res) => { + res.send(JSON.stringify({success: true})) +}) + +app.post('/highscore', async (req, res) => { + if (req.body !== undefined) { + let data = await db.any( + 'SELECT * FROM lb_highscore LIMIT $1 OFFSET $2', + [req.body.itemsPerPage, req.body.itemsPerPage * (req.body.page - 1)] + ) + res.send(data) + } else { + res.status(400) + res.send("itemsPerPage and/or page not defined") + } + +}) + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`) +}) \ No newline at end of file diff --git a/backend/api/tsconfig.json b/backend/api/tsconfig.json new file mode 100644 index 0000000..9a2dd65 --- /dev/null +++ b/backend/api/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2020", + "sourceMap": true, + "outDir": "build" + }, + "include": ["src/**/*"] +} \ No newline at end of file From ba9dd7d17a4c6cc4e696de5b246b0e04ee934d41 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Wed, 30 Nov 2022 22:04:06 +0100 Subject: [PATCH 10/21] improved express setup (added helmet, morgan) --- backend/api/package-lock.json | 107 ++++++++++++++++++++++++++++++++++ backend/api/package.json | 2 + backend/api/src/app.ts | 36 ++++++------ 3 files changed, 127 insertions(+), 18 deletions(-) diff --git a/backend/api/package-lock.json b/backend/api/package-lock.json index 64369aa..6403112 100644 --- a/backend/api/package-lock.json +++ b/backend/api/package-lock.json @@ -11,6 +11,8 @@ "dependencies": { "body-parser": "^1.20.1", "express": "^4.18.2", + "helmet": "^6.0.1", + "morgan": "^1.10.0", "pg-promise": "^10.15.4", "ts-node": "^10.9.1", "typescript": "^4.9.3" @@ -126,6 +128,22 @@ "node": ">=10.0.0" } }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -387,6 +405,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/helmet": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.0.1.tgz", + "integrity": "sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -482,6 +508,32 @@ "node": ">= 0.6" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -514,6 +566,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -1026,6 +1086,21 @@ "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz", "integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1225,6 +1300,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "helmet": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.0.1.tgz", + "integrity": "sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw==" + }, "http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1293,6 +1373,28 @@ "mime-db": "1.52.0" } }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "requires": { + "ee-first": "1.1.1" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1316,6 +1418,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", diff --git a/backend/api/package.json b/backend/api/package.json index bb75f93..587f2e3 100644 --- a/backend/api/package.json +++ b/backend/api/package.json @@ -13,6 +13,8 @@ "dependencies": { "body-parser": "^1.20.1", "express": "^4.18.2", + "helmet": "^6.0.1", + "morgan": "^1.10.0", "pg-promise": "^10.15.4", "ts-node": "^10.9.1", "typescript": "^4.9.3" diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index d1469f7..e61afa4 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -1,29 +1,29 @@ import express from 'express'; -import bodyParser from "body-parser"; + import pgPromise from "pg-promise"; -const pgp = pgPromise({}); -const db = pgp('postgres://postgres:postgres@localhost:5432/rr') +import helmet from "helmet"; +import bodyParser from "body-parser"; +import morgan from 'morgan'; + const app = express() const port = 3000 -app.use(bodyParser.json()) +app.use(helmet()) -app.get('/test', (req, res) => { - res.send(JSON.stringify({success: true})) -}) +// init database connection +const pgp = pgPromise({}); +const db = pgp('postgres://postgres:postgres@localhost:5432/rr') -app.post('/highscore', async (req, res) => { - if (req.body !== undefined) { - let data = await db.any( - 'SELECT * FROM lb_highscore LIMIT $1 OFFSET $2', - [req.body.itemsPerPage, req.body.itemsPerPage * (req.body.page - 1)] - ) - res.send(data) - } else { - res.status(400) - res.send("itemsPerPage and/or page not defined") - } +// configure & use logger +let morganFormatted = morgan('[:date[iso]] :method :url - :status') +app.use(morganFormatted); + +app.get('/highscore', async (req, res) => { + let data = await db.any( + 'SELECT * FROM lb_highscore;' + ) + res.send(data) }) app.listen(port, () => { From 72bb54a536c8b9f1c0d9612979aa686e4998c7dd Mon Sep 17 00:00:00 2001 From: j-weissen Date: Wed, 30 Nov 2022 23:37:11 +0100 Subject: [PATCH 11/21] db views now enumerate rows --- backend/db/Dockerfile | 2 +- .../_createSchema.sql} | 12 +----- backend/db/initScripts/loadMockData.sql | 41 +++++++++++++++++++ docker-compose.yml | 2 +- 4 files changed, 45 insertions(+), 12 deletions(-) rename backend/db/{createSchema.sql => initScripts/_createSchema.sql} (56%) create mode 100644 backend/db/initScripts/loadMockData.sql diff --git a/backend/db/Dockerfile b/backend/db/Dockerfile index 880b4f2..c742e53 100644 --- a/backend/db/Dockerfile +++ b/backend/db/Dockerfile @@ -1,3 +1,3 @@ FROM postgres:15-alpine -COPY createSchema.sql /docker-entrypoint-initdb.d/createSchema.sql \ No newline at end of file +COPY initScripts/* /docker-entrypoint-initdb.d/ \ No newline at end of file diff --git a/backend/db/createSchema.sql b/backend/db/initScripts/_createSchema.sql similarity index 56% rename from backend/db/createSchema.sql rename to backend/db/initScripts/_createSchema.sql index 9d802a8..ee4cb61 100644 --- a/backend/db/createSchema.sql +++ b/backend/db/initScripts/_createSchema.sql @@ -24,17 +24,9 @@ CREATE VIEW user_data AS ( ); CREATE VIEW lb_highscore AS ( - SELECT username, max(score) AS highscore FROM game GROUP BY username ORDER BY highscore DESC -); - -CREATE VIEW lb_total_score AS ( - SELECT username, sum(score) AS total_score FROM game GROUP BY username ORDER BY total_score DESC + SELECT row_number() OVER (ORDER BY max(score) DESC) AS rank, username, max(score) AS highscore FROM game GROUP BY username ORDER BY rank ); CREATE VIEW lb_total_playtime AS ( - SELECT username, sum(playtime) AS total_playtime FROM game GROUP BY username ORDER BY total_playtime DESC + SELECT row_number() OVER (ORDER BY sum(playtime) DESC) AS rank, username, sum(playtime) AS total_playtime FROM game GROUP BY username ORDER BY rank ); - -CREATE VIEW lb_average_score AS ( - SELECT username, avg(score) AS average_score FROM game GROUP BY username ORDER BY average_score DESC -); \ No newline at end of file diff --git a/backend/db/initScripts/loadMockData.sql b/backend/db/initScripts/loadMockData.sql new file mode 100644 index 0000000..b6c2676 --- /dev/null +++ b/backend/db/initScripts/loadMockData.sql @@ -0,0 +1,41 @@ +insert into "user" (username) values ('dpettus0'); +insert into "user" (username) values ('egreetland1'); +insert into "user" (username) values ('smontford2'); +insert into "user" (username) values ('idagwell3'); +insert into "user" (username) values ('lgagan4'); +insert into "user" (username) values ('acarmont5'); +insert into "user" (username) values ('kjermyn6'); +insert into "user" (username) values ('dokieran7'); +insert into "user" (username) values ('pdrinkel8'); +insert into "user" (username) values ('amelladew9'); + +insert into game (username, score, playtime, date) values ('dpettus0', 74, '19:59', '2022-07-19'); +insert into game (username, score, playtime, date) values ('dpettus0', 86, '20:32', '2022-11-24'); +insert into game (username, score, playtime, date) values ('dpettus0', 68, '10:41', '2022-03-24'); +insert into game (username, score, playtime, date) values ('egreetland1', 39, '5:55', '2022-06-01'); +insert into game (username, score, playtime, date) values ('egreetland1', 20, '9:23', '2022-03-12'); +insert into game (username, score, playtime, date) values ('egreetland1', 28, '23:45', '2022-04-01'); +insert into game (username, score, playtime, date) values ('smontford2', 44, '18:43', '2022-06-24'); +insert into game (username, score, playtime, date) values ('smontford2', 92, '14:54', '2022-11-06'); +insert into game (username, score, playtime, date) values ('smontford2', 73, '0:45', '2022-07-26'); +insert into game (username, score, playtime, date) values ('idagwell3', 27, '2:49', '2022-02-03'); +insert into game (username, score, playtime, date) values ('idagwell3', 26, '2:32', '2022-07-19'); +insert into game (username, score, playtime, date) values ('idagwell3', 12, '17:03', '2022-04-25'); +insert into game (username, score, playtime, date) values ('lgagan4', 6, '8:49', '2021-12-03'); +insert into game (username, score, playtime, date) values ('lgagan4', 22, '22:27', '2022-03-02'); +insert into game (username, score, playtime, date) values ('lgagan4', 94, '1:04', '2022-10-19'); +insert into game (username, score, playtime, date) values ('acarmont5', 2, '2:14', '2022-04-06'); +insert into game (username, score, playtime, date) values ('acarmont5', 21, '17:18', '2022-06-03'); +insert into game (username, score, playtime, date) values ('acarmont5', 33, '16:01', '2022-02-02'); +insert into game (username, score, playtime, date) values ('kjermyn6', 27, '7:03', '2022-02-06'); +insert into game (username, score, playtime, date) values ('kjermyn6', 62, '0:45', '2022-11-15'); +insert into game (username, score, playtime, date) values ('kjermyn6', 12, '8:54', '2022-06-29'); +insert into game (username, score, playtime, date) values ('dokieran7', 63, '16:01', '2022-11-05'); +insert into game (username, score, playtime, date) values ('dokieran7', 29, '0:46', '2022-10-01'); +insert into game (username, score, playtime, date) values ('dokieran7', 67, '1:27', '2022-09-29'); +insert into game (username, score, playtime, date) values ('pdrinkel8', 84, '10:37', '2021-12-18'); +insert into game (username, score, playtime, date) values ('pdrinkel8', 14, '19:14', '2022-01-31'); +insert into game (username, score, playtime, date) values ('pdrinkel8', 21, '19:04', '2022-03-08'); +insert into game (username, score, playtime, date) values ('amelladew9', 46, '2:34', '2022-04-18'); +insert into game (username, score, playtime, date) values ('amelladew9', 78, '9:33', '2022-09-10'); +insert into game (username, score, playtime, date) values ('amelladew9', 82, '11:19', '2022-11-29'); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 89b49f4..337f4df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,5 +11,5 @@ services: ports: - "5432:5432" volumes: - - ./pgdata:/var/lib/postgresql/data + - ./backend/pgdata:/var/lib/postgresql/data From f3f0945ccc9d2e705c45a4b280d07237952ed278 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Mon, 5 Dec 2022 19:30:27 +0100 Subject: [PATCH 12/21] improved api --- backend/api/Dockerfile | 9 ++ backend/api/package-lock.json | 141 ++++++++++++++++++++++++++++ backend/api/package.json | 3 +- backend/api/src/app.ts | 10 +- backend/api/src/exampleEndpoints.ts | 16 ++++ backend/api/tsconfig.json | 2 +- 6 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 backend/api/src/exampleEndpoints.ts diff --git a/backend/api/Dockerfile b/backend/api/Dockerfile index e69de29..1a26abf 100644 --- a/backend/api/Dockerfile +++ b/backend/api/Dockerfile @@ -0,0 +1,9 @@ +FROM node:18 + +COPY ./* /app/ +WORKDIR /app + +RUN npm install + +ENTRYPOINT ["npm", "run"] +CMD ["prod"] \ No newline at end of file diff --git a/backend/api/package-lock.json b/backend/api/package-lock.json index 6403112..35a2734 100644 --- a/backend/api/package-lock.json +++ b/backend/api/package-lock.json @@ -18,6 +18,7 @@ "typescript": "^4.9.3" }, "devDependencies": { + "@types/express": "^4.17.14", "@types/node": "^18.11.9" } }, @@ -74,11 +75,81 @@ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, "node_modules/@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1047,11 +1118,81 @@ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", diff --git a/backend/api/package.json b/backend/api/package.json index 587f2e3..95248c1 100644 --- a/backend/api/package.json +++ b/backend/api/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "tsc", "dev": "ts-node-esm src/app.ts", - "test": "echo \"Error: no test specified\" && exit 1" + "prod": "tsc && node build/app.ts" }, "author": "jweissen", "license": "ISC", @@ -20,6 +20,7 @@ "typescript": "^4.9.3" }, "devDependencies": { + "@types/express": "^4.17.14", "@types/node": "^18.11.9" } } diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index e61afa4..9fe8b2e 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -10,14 +10,14 @@ const port = 3000 app.use(helmet()) -// init database connection -const pgp = pgPromise({}); -const db = pgp('postgres://postgres:postgres@localhost:5432/rr') - // configure & use logger let morganFormatted = morgan('[:date[iso]] :method :url - :status') app.use(morganFormatted); +// init database connection +const pgp = pgPromise({}); +const db = pgp('postgres://postgres:postgres@localhost:5432/rr') + app.get('/highscore', async (req, res) => { let data = await db.any( @@ -27,5 +27,5 @@ app.get('/highscore', async (req, res) => { }) app.listen(port, () => { - console.log(`Example app listening on port ${port}`) + morganFormatted.log(`Server started at http://localhost:3000`); }) \ No newline at end of file diff --git a/backend/api/src/exampleEndpoints.ts b/backend/api/src/exampleEndpoints.ts new file mode 100644 index 0000000..4b3afb7 --- /dev/null +++ b/backend/api/src/exampleEndpoints.ts @@ -0,0 +1,16 @@ +import * as express from 'express'; +import * as bodyParser from "body-parser"; + +let router = express.Router(); + +router.use(bodyParser.json()) + +router.get('/helloworld', (req, res) => { + res.json({message: "Hello World!"}) +}) + +router.post('/echo', (req, res) => { + res.json(req.body) +}) + +module.exports = router \ No newline at end of file diff --git a/backend/api/tsconfig.json b/backend/api/tsconfig.json index 9a2dd65..e96a813 100644 --- a/backend/api/tsconfig.json +++ b/backend/api/tsconfig.json @@ -5,7 +5,7 @@ "moduleResolution": "NodeNext", "target": "ES2020", "sourceMap": true, - "outDir": "build" + "outDir": "build/" }, "include": ["src/**/*"] } \ No newline at end of file From 529da45219e35b8a6341c82695d0ea9247060844 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 6 Dec 2022 09:45:31 +0100 Subject: [PATCH 13/21] everything dockerized --- .env.example | 7 +++++++ backend/api/Dockerfile | 4 +++- backend/api/package-lock.json | 19 +++++++++++++++++++ backend/api/package.json | 3 ++- backend/api/src/app.ts | 29 +++++++++++++++++++++-------- docker-compose.yml | 14 ++++++++++---- 6 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..12ac7c8 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +POSTGRES_PORT=3000 +EXPRESS_PORT=5432 + +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=rr + diff --git a/backend/api/Dockerfile b/backend/api/Dockerfile index 1a26abf..3ed989a 100644 --- a/backend/api/Dockerfile +++ b/backend/api/Dockerfile @@ -1,9 +1,11 @@ FROM node:18 -COPY ./* /app/ +COPY . /app WORKDIR /app RUN npm install +EXPOSE 3000 + ENTRYPOINT ["npm", "run"] CMD ["prod"] \ No newline at end of file diff --git a/backend/api/package-lock.json b/backend/api/package-lock.json index 35a2734..2cf000a 100644 --- a/backend/api/package-lock.json +++ b/backend/api/package-lock.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@types/express": "^4.17.14", + "@types/morgan": "^1.9.3", "@types/node": "^18.11.9" } }, @@ -123,6 +124,15 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, + "node_modules/@types/morgan": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", + "integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", @@ -1166,6 +1176,15 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, + "@types/morgan": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", + "integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", diff --git a/backend/api/package.json b/backend/api/package.json index 95248c1..46e3922 100644 --- a/backend/api/package.json +++ b/backend/api/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "tsc", "dev": "ts-node-esm src/app.ts", - "prod": "tsc && node build/app.ts" + "prod": "tsc && node build/app.js" }, "author": "jweissen", "license": "ISC", @@ -21,6 +21,7 @@ }, "devDependencies": { "@types/express": "^4.17.14", + "@types/morgan": "^1.9.3", "@types/node": "^18.11.9" } } diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index 9fe8b2e..ee69f12 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -16,16 +16,29 @@ app.use(morganFormatted); // init database connection const pgp = pgPromise({}); -const db = pgp('postgres://postgres:postgres@localhost:5432/rr') +const db = pgp('postgres://postgres:postgres@db:5432/rr') - -app.get('/highscore', async (req, res) => { - let data = await db.any( - 'SELECT * FROM lb_highscore;' - ) - res.send(data) +app.get('/helloworld', (req, res) => { + res.json({message: "Hello World!"}) }) +app.get('/highscore', async (req, res) => { + let data = await dbQueryCatcher(async () => + await db.manyOrNone('SELECT * FROM lb_highscore;') + ) + res.json(data) +}) + +async function dbQueryCatcher(request): Promise { + let data; + try { + data = await request(); + } catch (e) { + console.log((e as Error).message) + } + return data; +} + app.listen(port, () => { - morganFormatted.log(`Server started at http://localhost:3000`); + console.log(`Server started at http://localhost:3000`); }) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 337f4df..c60a011 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,11 +5,17 @@ services: build: backend/db container_name: postgres-db environment: - POSTGRES_DB: rr - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: - - "5432:5432" + - "${POSTGRES_PORT}:5432" volumes: - ./backend/pgdata:/var/lib/postgresql/data + api: + build: backend/api + container_name: express-api + ports: + - "${EXPRESS_PORT}:3000" + From fd257b335cb3d6b158fcc69d6e9f4a14652ff206 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 6 Dec 2022 09:53:38 +0100 Subject: [PATCH 14/21] fixed ports in .env.enxample --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 12ac7c8..821e903 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ -POSTGRES_PORT=3000 -EXPRESS_PORT=5432 +POSTGRES_PORT=5432 +EXPRESS_PORT=3000 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres From 6e8e0e0101a9438cfccdf8feba47aed01b01f0e9 Mon Sep 17 00:00:00 2001 From: j-weissen Date: Mon, 12 Dec 2022 11:35:53 +0100 Subject: [PATCH 15/21] added Model classes --- backend/README.md | 16 +++++++- backend/api/.dockerignore | 2 + backend/api/src/Database.ts | 24 ++++++++++++ backend/api/src/app.ts | 37 +++++++++++-------- backend/api/src/exampleEndpoints.ts | 16 -------- backend/api/src/leaderboardRouter.ts | 11 ++++++ .../api/src/manager/LeaderBoardSerializer.ts | 3 ++ backend/api/src/manager/UserDataManager.ts | 20 ++++++++++ .../manager/UserDataPgPromiseSerializer.ts | 19 ++++++++++ backend/api/src/manager/UserDataSerializer.ts | 6 +++ backend/api/src/model/Leaderboard.ts | 5 +++ backend/api/src/model/LeaderboardEntry.ts | 5 +++ backend/api/src/model/Time.ts | 6 +++ backend/api/src/model/UserData.ts | 8 ++++ 14 files changed, 145 insertions(+), 33 deletions(-) create mode 100644 backend/api/.dockerignore create mode 100644 backend/api/src/Database.ts delete mode 100644 backend/api/src/exampleEndpoints.ts create mode 100644 backend/api/src/leaderboardRouter.ts create mode 100644 backend/api/src/manager/LeaderBoardSerializer.ts create mode 100644 backend/api/src/manager/UserDataManager.ts create mode 100644 backend/api/src/manager/UserDataPgPromiseSerializer.ts create mode 100644 backend/api/src/manager/UserDataSerializer.ts create mode 100644 backend/api/src/model/Leaderboard.ts create mode 100644 backend/api/src/model/LeaderboardEntry.ts create mode 100644 backend/api/src/model/Time.ts create mode 100644 backend/api/src/model/UserData.ts diff --git a/backend/README.md b/backend/README.md index bbb4279..e70fd37 100644 --- a/backend/README.md +++ b/backend/README.md @@ -24,4 +24,18 @@ erDiagram int average_score int games_played } -``` \ No newline at end of file + + lb_highscore { + int rank + string username + int highscore + } + + lb_total_playtime { + int rank + string username + time total_playtime + } +``` + +## API Endpoints diff --git a/backend/api/.dockerignore b/backend/api/.dockerignore new file mode 100644 index 0000000..3e2e84b --- /dev/null +++ b/backend/api/.dockerignore @@ -0,0 +1,2 @@ +build/ +node_modules/ diff --git a/backend/api/src/Database.ts b/backend/api/src/Database.ts new file mode 100644 index 0000000..99d789d --- /dev/null +++ b/backend/api/src/Database.ts @@ -0,0 +1,24 @@ +import pgPromise from "pg-promise"; + + +export abstract class Database { + static db = null; + get db() { + if (Database.db == null) { + Database.db = pgPromise({})('postgres://postgres:postgres@db:5432/rr') + } + return Database.db; + } + + static async catcher(request): Promise { + let data; + try { + data = await request(); + } catch (e) { + console.log((e as Error).message) + } + return data; + } + + +} \ No newline at end of file diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index ee69f12..9b08420 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -1,9 +1,13 @@ import express from 'express'; +import {Database} from "./Database.js"; -import pgPromise from "pg-promise"; import helmet from "helmet"; import bodyParser from "body-parser"; import morgan from 'morgan'; +import {UserDataManager} from "./manager/UserDataManager.js"; +import {UserDataPgPromiseSerializer} from "./manager/UserDataPgPromiseSerializer.js"; +import {leaderboardRouter} from "./leaderboardRouter.js"; + const app = express() const port = 3000 @@ -14,30 +18,31 @@ app.use(helmet()) let morganFormatted = morgan('[:date[iso]] :method :url - :status') app.use(morganFormatted); -// init database connection -const pgp = pgPromise({}); -const db = pgp('postgres://postgres:postgres@db:5432/rr') +app.use('/leaderboard', leaderboardRouter) app.get('/helloworld', (req, res) => { res.json({message: "Hello World!"}) }) app.get('/highscore', async (req, res) => { - let data = await dbQueryCatcher(async () => - await db.manyOrNone('SELECT * FROM lb_highscore;') - ) + let data = await Database.db.manyOrNone('SELECT * FROM lb_highscore;') + .catch((error) => console.log(error.message)) res.json(data) }) -async function dbQueryCatcher(request): Promise { - let data; - try { - data = await request(); - } catch (e) { - console.log((e as Error).message) - } - return data; -} +app.get('/user/:username', async (req, res) => { + let data = await Database.db.oneOrNone( + 'SELECT * FROM user_data WHERE username = $1;', + [req.params.username]) + .catch((error) => console.log(error.message) + ) + let userDataManager: UserDataManager = new UserDataManager(data, new UserDataPgPromiseSerializer); + res.json(userDataManager.userData); +}) + + + + app.listen(port, () => { console.log(`Server started at http://localhost:3000`); diff --git a/backend/api/src/exampleEndpoints.ts b/backend/api/src/exampleEndpoints.ts deleted file mode 100644 index 4b3afb7..0000000 --- a/backend/api/src/exampleEndpoints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as express from 'express'; -import * as bodyParser from "body-parser"; - -let router = express.Router(); - -router.use(bodyParser.json()) - -router.get('/helloworld', (req, res) => { - res.json({message: "Hello World!"}) -}) - -router.post('/echo', (req, res) => { - res.json(req.body) -}) - -module.exports = router \ No newline at end of file diff --git a/backend/api/src/leaderboardRouter.ts b/backend/api/src/leaderboardRouter.ts new file mode 100644 index 0000000..d4b0deb --- /dev/null +++ b/backend/api/src/leaderboardRouter.ts @@ -0,0 +1,11 @@ +import express from 'express'; +export const leaderboardRouter = express.Router() + + +leaderboardRouter.get('/highscore', (req, res) => { + res.send('highscore') +}) + +leaderboardRouter.get('/totalplaytime', (req, res) => { + res.send('total play time') +}) \ No newline at end of file diff --git a/backend/api/src/manager/LeaderBoardSerializer.ts b/backend/api/src/manager/LeaderBoardSerializer.ts new file mode 100644 index 0000000..41a6a68 --- /dev/null +++ b/backend/api/src/manager/LeaderBoardSerializer.ts @@ -0,0 +1,3 @@ +export interface LeaderBoardSerializer { + +} \ No newline at end of file diff --git a/backend/api/src/manager/UserDataManager.ts b/backend/api/src/manager/UserDataManager.ts new file mode 100644 index 0000000..c28aad8 --- /dev/null +++ b/backend/api/src/manager/UserDataManager.ts @@ -0,0 +1,20 @@ +import {UserData} from "../model/UserData.js"; +import {UserDataSerializer} from "./UserDataSerializer.js"; + +export class UserDataManager { + private _userData: UserData; + private serializer: UserDataSerializer; + + constructor(data: any, serializer: UserDataSerializer) { + this.serializer = serializer; + this._userData = this.serializer.serialize(data); + } + + get userData(): UserData { + return this._userData; + } + + set userData(value: UserData) { + this._userData = value; + } +} \ No newline at end of file diff --git a/backend/api/src/manager/UserDataPgPromiseSerializer.ts b/backend/api/src/manager/UserDataPgPromiseSerializer.ts new file mode 100644 index 0000000..4721740 --- /dev/null +++ b/backend/api/src/manager/UserDataPgPromiseSerializer.ts @@ -0,0 +1,19 @@ +import {UserData} from "../model/UserData.js"; +import {UserDataSerializer} from "./UserDataSerializer.js"; + +export class UserDataPgPromiseSerializer implements UserDataSerializer { + deserialize(userData: UserData): any { + throw new Error("Method not implemented") + } + + serialize(data: any): UserData { + return { + username: data.username, + highscore: data.highscore, + totalScore: data.total_score, + totalPlaytime: data.total_playtime, + averageScore: data.averageScore, + gamesPlayed: data.games_played, + } + } +} \ No newline at end of file diff --git a/backend/api/src/manager/UserDataSerializer.ts b/backend/api/src/manager/UserDataSerializer.ts new file mode 100644 index 0000000..9d54880 --- /dev/null +++ b/backend/api/src/manager/UserDataSerializer.ts @@ -0,0 +1,6 @@ +import {UserData} from "../model/UserData.js"; + +export interface UserDataSerializer { + serialize(data: any): UserData, + deserialize(userData: UserData): any, +} \ No newline at end of file diff --git a/backend/api/src/model/Leaderboard.ts b/backend/api/src/model/Leaderboard.ts new file mode 100644 index 0000000..406962a --- /dev/null +++ b/backend/api/src/model/Leaderboard.ts @@ -0,0 +1,5 @@ +import {LeaderboardEntry} from "./LeaderboardEntry.js"; + +export class Leaderboard { + content: LeaderboardEntry[]; +} \ No newline at end of file diff --git a/backend/api/src/model/LeaderboardEntry.ts b/backend/api/src/model/LeaderboardEntry.ts new file mode 100644 index 0000000..b43f6dd --- /dev/null +++ b/backend/api/src/model/LeaderboardEntry.ts @@ -0,0 +1,5 @@ +export interface LeaderboardEntry { + rank: number, + username: string, + score: T, +} \ No newline at end of file diff --git a/backend/api/src/model/Time.ts b/backend/api/src/model/Time.ts new file mode 100644 index 0000000..69cf57b --- /dev/null +++ b/backend/api/src/model/Time.ts @@ -0,0 +1,6 @@ +export interface Time { + seconds: number, + minutes?: number, + hours?: number, + days?: number, +} \ No newline at end of file diff --git a/backend/api/src/model/UserData.ts b/backend/api/src/model/UserData.ts new file mode 100644 index 0000000..8845351 --- /dev/null +++ b/backend/api/src/model/UserData.ts @@ -0,0 +1,8 @@ +export interface UserData { + username: string, + highscore: number, + totalScore: number, + totalPlaytime: string, + averageScore: number, + gamesPlayed: number, +} \ No newline at end of file From d174f7440843417ca247b583f37720eab44f73db Mon Sep 17 00:00:00 2001 From: j-weissen Date: Tue, 13 Dec 2022 09:50:33 +0100 Subject: [PATCH 16/21] optimized schema --- backend/README.md | 31 +++++++--- backend/db/initScripts/_createSchema.sql | 75 ++++++++++++++++++---- backend/db/initScripts/loadMockData.sql | 79 ++++++++++++------------ 3 files changed, 125 insertions(+), 60 deletions(-) diff --git a/backend/README.md b/backend/README.md index bbb4279..97e5f5f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -3,7 +3,8 @@ ```mermaid erDiagram user { - string name PK + serial id PK + string name } game { @@ -11,17 +12,33 @@ erDiagram int score time playtime date date - string username FK + int user_id FK } - user ||--O{ game : "played" - - user_data { - string username PK + user_scores { + int user_id PK int highscore int total_score int total_playtime int average_score int games_played } -``` \ No newline at end of file + + lb_highscore { + int rank + int user_id + int highscore + } + + lb_total_playtime { + int rank + int user_id + time total_playtime + } + + user ||--O{ game : "played" + user ||--|| user_scores : "" +``` +`lb_highscore` and `lb_total_playtime` are views querying the `user_scores` table. + +A trigger function on insert to the `user` table creates a new row in `user_scores`. Everytime a new `game` is inserted, the row is updated. \ No newline at end of file diff --git a/backend/db/initScripts/_createSchema.sql b/backend/db/initScripts/_createSchema.sql index ee4cb61..d203328 100644 --- a/backend/db/initScripts/_createSchema.sql +++ b/backend/db/initScripts/_createSchema.sql @@ -1,5 +1,16 @@ CREATE TABLE "user" ( - username VARCHAR(32) PRIMARY KEY + id SERIAL PRIMARY KEY, + name VARCHAR(32) +); + +CREATE TABLE user_scores ( + user_id INT PRIMARY KEY REFERENCES "user", + highscore INT NOT NULL DEFAULT 0, + total_score INT NOT NULL DEFAULT 0, + total_playtime TIME NOT NULL DEFAULT '00:00:00', + average_score INT NOT NULL DEFAULT 0, + games_played INT NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES "user" ); CREATE TABLE game ( @@ -7,26 +18,64 @@ CREATE TABLE game ( score INTEGER NOT NULL, playtime TIME NOT NULL, date DATE NOT NULL, - username VARCHAR(32) NOT NULL, - FOREIGN KEY (username) REFERENCES "user" + user_id INT NOT NULL, + FOREIGN KEY (user_id) REFERENCES "user" ); -CREATE VIEW user_data AS ( +CREATE VIEW lb_highscore AS ( + SELECT row_number() OVER (ORDER BY highscore DESC) AS rank, user_id, highscore FROM user_scores ORDER BY rank +); + +CREATE VIEW lb_total_playtime AS ( + SELECT row_number() OVER (ORDER BY total_playtime DESC) AS rank, user_id, total_playtime AS total_playtime FROM user_scores ORDER BY rank +); + +CREATE OR REPLACE FUNCTION insert_new_user_scores() + RETURNS TRIGGER + LANGUAGE plpgsql AS +$$ + BEGIN + INSERT INTO user_scores (user_id) VALUES (NEW.id); + RETURN NEW; + END; +$$; + +CREATE TRIGGER user_added + AFTER INSERT ON "user" + FOR EACH ROW +EXECUTE FUNCTION insert_new_user_scores(); + +CREATE OR REPLACE FUNCTION update_user_scores() + RETURNS TRIGGER + LANGUAGE plpgsql AS +$$ +DECLARE + row user_scores%ROWTYPE; +BEGIN SELECT - username, + user_id, max(score) AS highscore, sum(score) AS total_score, sum(playtime) AS total_playtime, avg(score) AS average_score, count(*) AS games_played + INTO row FROM game - GROUP BY username -); + WHERE user_id = NEW.user_id + GROUP BY user_id; -CREATE VIEW lb_highscore AS ( - SELECT row_number() OVER (ORDER BY max(score) DESC) AS rank, username, max(score) AS highscore FROM game GROUP BY username ORDER BY rank -); + UPDATE user_scores SET + highscore = row.highscore, + total_score = row.total_score, + total_playtime = row.total_playtime, + average_score = row.average_score, + games_played = row.games_played + WHERE user_id = row.user_id; + RETURN NEW; +END +$$; -CREATE VIEW lb_total_playtime AS ( - SELECT row_number() OVER (ORDER BY sum(playtime) DESC) AS rank, username, sum(playtime) AS total_playtime FROM game GROUP BY username ORDER BY rank -); +CREATE TRIGGER game_played + AFTER INSERT ON game + FOR EACH ROW +EXECUTE FUNCTION update_user_scores(); diff --git a/backend/db/initScripts/loadMockData.sql b/backend/db/initScripts/loadMockData.sql index b6c2676..7735cc9 100644 --- a/backend/db/initScripts/loadMockData.sql +++ b/backend/db/initScripts/loadMockData.sql @@ -1,41 +1,40 @@ -insert into "user" (username) values ('dpettus0'); -insert into "user" (username) values ('egreetland1'); -insert into "user" (username) values ('smontford2'); -insert into "user" (username) values ('idagwell3'); -insert into "user" (username) values ('lgagan4'); -insert into "user" (username) values ('acarmont5'); -insert into "user" (username) values ('kjermyn6'); -insert into "user" (username) values ('dokieran7'); -insert into "user" (username) values ('pdrinkel8'); -insert into "user" (username) values ('amelladew9'); +insert into "user" (name) values ('dpettus0'); +insert into "user" (name) values ('egreetland1'); +insert into "user" (name) values ('smontford2'); +insert into "user" (name) values ('idagwell3'); +insert into "user" (name) values ('lgagan4'); +insert into "user" (name) values ('acarmont5'); +insert into "user" (name) values ('kjermyn6'); +insert into "user" (name) values ('dokieran7'); +insert into "user" (name) values ('pdrinkel8'); -insert into game (username, score, playtime, date) values ('dpettus0', 74, '19:59', '2022-07-19'); -insert into game (username, score, playtime, date) values ('dpettus0', 86, '20:32', '2022-11-24'); -insert into game (username, score, playtime, date) values ('dpettus0', 68, '10:41', '2022-03-24'); -insert into game (username, score, playtime, date) values ('egreetland1', 39, '5:55', '2022-06-01'); -insert into game (username, score, playtime, date) values ('egreetland1', 20, '9:23', '2022-03-12'); -insert into game (username, score, playtime, date) values ('egreetland1', 28, '23:45', '2022-04-01'); -insert into game (username, score, playtime, date) values ('smontford2', 44, '18:43', '2022-06-24'); -insert into game (username, score, playtime, date) values ('smontford2', 92, '14:54', '2022-11-06'); -insert into game (username, score, playtime, date) values ('smontford2', 73, '0:45', '2022-07-26'); -insert into game (username, score, playtime, date) values ('idagwell3', 27, '2:49', '2022-02-03'); -insert into game (username, score, playtime, date) values ('idagwell3', 26, '2:32', '2022-07-19'); -insert into game (username, score, playtime, date) values ('idagwell3', 12, '17:03', '2022-04-25'); -insert into game (username, score, playtime, date) values ('lgagan4', 6, '8:49', '2021-12-03'); -insert into game (username, score, playtime, date) values ('lgagan4', 22, '22:27', '2022-03-02'); -insert into game (username, score, playtime, date) values ('lgagan4', 94, '1:04', '2022-10-19'); -insert into game (username, score, playtime, date) values ('acarmont5', 2, '2:14', '2022-04-06'); -insert into game (username, score, playtime, date) values ('acarmont5', 21, '17:18', '2022-06-03'); -insert into game (username, score, playtime, date) values ('acarmont5', 33, '16:01', '2022-02-02'); -insert into game (username, score, playtime, date) values ('kjermyn6', 27, '7:03', '2022-02-06'); -insert into game (username, score, playtime, date) values ('kjermyn6', 62, '0:45', '2022-11-15'); -insert into game (username, score, playtime, date) values ('kjermyn6', 12, '8:54', '2022-06-29'); -insert into game (username, score, playtime, date) values ('dokieran7', 63, '16:01', '2022-11-05'); -insert into game (username, score, playtime, date) values ('dokieran7', 29, '0:46', '2022-10-01'); -insert into game (username, score, playtime, date) values ('dokieran7', 67, '1:27', '2022-09-29'); -insert into game (username, score, playtime, date) values ('pdrinkel8', 84, '10:37', '2021-12-18'); -insert into game (username, score, playtime, date) values ('pdrinkel8', 14, '19:14', '2022-01-31'); -insert into game (username, score, playtime, date) values ('pdrinkel8', 21, '19:04', '2022-03-08'); -insert into game (username, score, playtime, date) values ('amelladew9', 46, '2:34', '2022-04-18'); -insert into game (username, score, playtime, date) values ('amelladew9', 78, '9:33', '2022-09-10'); -insert into game (username, score, playtime, date) values ('amelladew9', 82, '11:19', '2022-11-29'); \ No newline at end of file +insert into game (user_id, score, playtime, date) values ('1', 74, '19:59', '2022-07-19'); +insert into game (user_id, score, playtime, date) values ('1', 86, '20:32', '2022-11-24'); +insert into game (user_id, score, playtime, date) values ('1', 68, '10:41', '2022-03-24'); +insert into game (user_id, score, playtime, date) values ('2', 39, '5:55', '2022-06-01'); +insert into game (user_id, score, playtime, date) values ('2', 20, '9:23', '2022-03-12'); +insert into game (user_id, score, playtime, date) values ('2', 28, '23:45', '2022-04-01'); +insert into game (user_id, score, playtime, date) values ('2', 44, '18:43', '2022-06-24'); +insert into game (user_id, score, playtime, date) values ('3', 92, '14:54', '2022-11-06'); +insert into game (user_id, score, playtime, date) values ('3', 73, '0:45', '2022-07-26'); +insert into game (user_id, score, playtime, date) values ('3', 27, '2:49', '2022-02-03'); +insert into game (user_id, score, playtime, date) values ('4', 26, '2:32', '2022-07-19'); +insert into game (user_id, score, playtime, date) values ('4', 12, '17:03', '2022-04-25'); +insert into game (user_id, score, playtime, date) values ('4', 6, '8:49', '2021-12-03'); +insert into game (user_id, score, playtime, date) values ('4', 22, '22:27', '2022-03-02'); +insert into game (user_id, score, playtime, date) values ('5', 94, '1:04', '2022-10-19'); +insert into game (user_id, score, playtime, date) values ('5', 2, '2:14', '2022-04-06'); +insert into game (user_id, score, playtime, date) values ('5', 21, '17:18', '2022-06-03'); +insert into game (user_id, score, playtime, date) values ('6', 33, '16:01', '2022-02-02'); +insert into game (user_id, score, playtime, date) values ('6', 27, '7:03', '2022-02-06'); +insert into game (user_id, score, playtime, date) values ('6', 62, '0:45', '2022-11-15'); +insert into game (user_id, score, playtime, date) values ('7', 12, '8:54', '2022-06-29'); +insert into game (user_id, score, playtime, date) values ('7', 63, '16:01', '2022-11-05'); +insert into game (user_id, score, playtime, date) values ('7', 29, '0:46', '2022-10-01'); +insert into game (user_id, score, playtime, date) values ('8', 67, '1:27', '2022-09-29'); +insert into game (user_id, score, playtime, date) values ('8', 84, '10:37', '2021-12-18'); +insert into game (user_id, score, playtime, date) values ('8', 14, '19:14', '2022-01-31'); +insert into game (user_id, score, playtime, date) values ('9', 21, '19:04', '2022-03-08'); +insert into game (user_id, score, playtime, date) values ('9', 46, '2:34', '2022-04-18'); +insert into game (user_id, score, playtime, date) values ('9', 78, '9:33', '2022-09-10'); +insert into game (user_id, score, playtime, date) values ('9', 82, '11:19', '2022-11-29'); \ No newline at end of file From 2219ea93bf5c5f16a5f7bd8d5b05be60c73ff1dd Mon Sep 17 00:00:00 2001 From: j-weissen Date: Wed, 14 Dec 2022 00:10:02 +0100 Subject: [PATCH 17/21] added Serializer & Manager classes --- backend/api/src/Database.ts | 22 +++--------- backend/api/src/app.ts | 30 +++------------- backend/api/src/leaderboardRoute.ts | 23 +++++++++++++ backend/api/src/leaderboardRouter.ts | 11 ------ .../manager/HighscoreLeaderboardManager.ts | 32 +++++++++++++++++ .../api/src/manager/LeaderBoardSerializer.ts | 3 -- backend/api/src/manager/Manager.ts | 8 +++++ .../api/src/manager/TimeLeaderboardManager.ts | 34 +++++++++++++++++++ backend/api/src/manager/UserDataManager.ts | 20 ----------- .../manager/UserDataPgPromiseSerializer.ts | 19 ----------- backend/api/src/manager/UserDataSerializer.ts | 6 ---- backend/api/src/manager/UserScoresManager.ts | 33 ++++++++++++++++++ backend/api/src/model/Leaderboard.ts | 8 +++-- backend/api/src/model/LeaderboardEntry.ts | 5 --- .../src/model/{UserData.ts => UserScores.ts} | 2 +- .../HighscoreLeaderboardSerializer.ts | 4 +++ backend/api/src/serializer/Serializer.ts | 4 +++ .../serializer/TimeLeaderboardSerializer.ts | 5 +++ .../src/serializer/UserScoresSerializer.ts | 4 +++ ...HighscoreLeaderboardPgPromiseSerializer.ts | 20 +++++++++++ .../TimeLeaderboardPgPromiseSerializer.ts | 20 +++++++++++ .../UserScoresPgPromiseSerializer.ts | 20 +++++++++++ backend/api/src/userRoute.ts | 30 ++++++++++++++++ docker-compose.yml | 2 ++ 24 files changed, 254 insertions(+), 111 deletions(-) create mode 100644 backend/api/src/leaderboardRoute.ts delete mode 100644 backend/api/src/leaderboardRouter.ts create mode 100644 backend/api/src/manager/HighscoreLeaderboardManager.ts delete mode 100644 backend/api/src/manager/LeaderBoardSerializer.ts create mode 100644 backend/api/src/manager/Manager.ts create mode 100644 backend/api/src/manager/TimeLeaderboardManager.ts delete mode 100644 backend/api/src/manager/UserDataManager.ts delete mode 100644 backend/api/src/manager/UserDataPgPromiseSerializer.ts delete mode 100644 backend/api/src/manager/UserDataSerializer.ts create mode 100644 backend/api/src/manager/UserScoresManager.ts delete mode 100644 backend/api/src/model/LeaderboardEntry.ts rename backend/api/src/model/{UserData.ts => UserScores.ts} (83%) create mode 100644 backend/api/src/serializer/HighscoreLeaderboardSerializer.ts create mode 100644 backend/api/src/serializer/Serializer.ts create mode 100644 backend/api/src/serializer/TimeLeaderboardSerializer.ts create mode 100644 backend/api/src/serializer/UserScoresSerializer.ts create mode 100644 backend/api/src/serializer/pgpromise/HighscoreLeaderboardPgPromiseSerializer.ts create mode 100644 backend/api/src/serializer/pgpromise/TimeLeaderboardPgPromiseSerializer.ts create mode 100644 backend/api/src/serializer/pgpromise/UserScoresPgPromiseSerializer.ts create mode 100644 backend/api/src/userRoute.ts diff --git a/backend/api/src/Database.ts b/backend/api/src/Database.ts index 99d789d..aba63ad 100644 --- a/backend/api/src/Database.ts +++ b/backend/api/src/Database.ts @@ -2,23 +2,11 @@ import pgPromise from "pg-promise"; export abstract class Database { - static db = null; - get db() { - if (Database.db == null) { - Database.db = pgPromise({})('postgres://postgres:postgres@db:5432/rr') + private static _db = null; + static get db() { + if (Database._db == null) { + Database._db = pgPromise({})('postgres://postgres:postgres@db:5432/rr') } - return Database.db; + return Database._db; } - - static async catcher(request): Promise { - let data; - try { - data = await request(); - } catch (e) { - console.log((e as Error).message) - } - return data; - } - - } \ No newline at end of file diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index 9b08420..fddff85 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -1,12 +1,8 @@ import express from 'express'; -import {Database} from "./Database.js"; - import helmet from "helmet"; -import bodyParser from "body-parser"; import morgan from 'morgan'; -import {UserDataManager} from "./manager/UserDataManager.js"; -import {UserDataPgPromiseSerializer} from "./manager/UserDataPgPromiseSerializer.js"; -import {leaderboardRouter} from "./leaderboardRouter.js"; +import {leaderboardRoute} from "./leaderboardRoute.js"; +import {userRoute} from "./userRoute.js"; const app = express() @@ -18,31 +14,13 @@ app.use(helmet()) let morganFormatted = morgan('[:date[iso]] :method :url - :status') app.use(morganFormatted); -app.use('/leaderboard', leaderboardRouter) +app.use('/leaderboard', leaderboardRoute) +app.use('/user', userRoute) app.get('/helloworld', (req, res) => { res.json({message: "Hello World!"}) }) -app.get('/highscore', async (req, res) => { - let data = await Database.db.manyOrNone('SELECT * FROM lb_highscore;') - .catch((error) => console.log(error.message)) - res.json(data) -}) - -app.get('/user/:username', async (req, res) => { - let data = await Database.db.oneOrNone( - 'SELECT * FROM user_data WHERE username = $1;', - [req.params.username]) - .catch((error) => console.log(error.message) - ) - let userDataManager: UserDataManager = new UserDataManager(data, new UserDataPgPromiseSerializer); - res.json(userDataManager.userData); -}) - - - - app.listen(port, () => { console.log(`Server started at http://localhost:3000`); diff --git a/backend/api/src/leaderboardRoute.ts b/backend/api/src/leaderboardRoute.ts new file mode 100644 index 0000000..8267647 --- /dev/null +++ b/backend/api/src/leaderboardRoute.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import {Database} from "./Database.js"; +import {TimeLeaderboardManager} from "./manager/TimeLeaderboardManager.js"; +import {HighscoreLeaderboardManager} from "./manager/HighscoreLeaderboardManager.js"; +import { + HighscoreLeaderboardPgPromiseSerializer +} from "./serializer/pgpromise/HighscoreLeaderboardPgPromiseSerializer.js"; +import {TimeLeaderboardPgPromiseSerializer} from "./serializer/pgpromise/TimeLeaderboardPgPromiseSerializer.js"; + +export const leaderboardRoute = express.Router() + + +leaderboardRoute.get('/highscore', async (req, res) => { + let data = await Database.db.manyOrNone('SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;') + const leaderboardManager = new HighscoreLeaderboardManager(data, new HighscoreLeaderboardPgPromiseSerializer); + res.send(leaderboardManager.content) +}) + +leaderboardRoute.get('/totalplaytime', async (req, res) => { + let data = await Database.db.manyOrNone('SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;') + const leaderboardManager = new TimeLeaderboardManager(data, new TimeLeaderboardPgPromiseSerializer); + res.send(leaderboardManager.content) +}) \ No newline at end of file diff --git a/backend/api/src/leaderboardRouter.ts b/backend/api/src/leaderboardRouter.ts deleted file mode 100644 index d4b0deb..0000000 --- a/backend/api/src/leaderboardRouter.ts +++ /dev/null @@ -1,11 +0,0 @@ -import express from 'express'; -export const leaderboardRouter = express.Router() - - -leaderboardRouter.get('/highscore', (req, res) => { - res.send('highscore') -}) - -leaderboardRouter.get('/totalplaytime', (req, res) => { - res.send('total play time') -}) \ No newline at end of file diff --git a/backend/api/src/manager/HighscoreLeaderboardManager.ts b/backend/api/src/manager/HighscoreLeaderboardManager.ts new file mode 100644 index 0000000..c1955e7 --- /dev/null +++ b/backend/api/src/manager/HighscoreLeaderboardManager.ts @@ -0,0 +1,32 @@ +import {Leaderboard} from "../model/Leaderboard.js"; +import {Manager} from "./Manager.js"; +import {HighscoreLeaderboardSerializer} from "../serializer/HighscoreLeaderboardSerializer.js"; + +export class HighscoreLeaderboardManager implements Manager>{ + private _content: Leaderboard; + private _serializer: HighscoreLeaderboardSerializer; + + constructor(rawData: any, serializer: HighscoreLeaderboardSerializer) { + this.serializer = serializer; + this._content = this.serializer.serialize(rawData); + } + + //region getter&setter + get content() { + return this._content; + } + + set content(value: Leaderboard) { + this._content = value; + } + + get serializer() { + return this._serializer; + } + + set serializer(value: HighscoreLeaderboardSerializer) { + this._serializer = value; + } + //endregion + +} \ No newline at end of file diff --git a/backend/api/src/manager/LeaderBoardSerializer.ts b/backend/api/src/manager/LeaderBoardSerializer.ts deleted file mode 100644 index 41a6a68..0000000 --- a/backend/api/src/manager/LeaderBoardSerializer.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface LeaderBoardSerializer { - -} \ No newline at end of file diff --git a/backend/api/src/manager/Manager.ts b/backend/api/src/manager/Manager.ts new file mode 100644 index 0000000..6cd27e3 --- /dev/null +++ b/backend/api/src/manager/Manager.ts @@ -0,0 +1,8 @@ +import {Serializer} from "../serializer/Serializer.js"; + +export interface Manager { + get content(), + set content(value: T), + get serializer(), + set serializer(value: Serializer), +} \ No newline at end of file diff --git a/backend/api/src/manager/TimeLeaderboardManager.ts b/backend/api/src/manager/TimeLeaderboardManager.ts new file mode 100644 index 0000000..df9ec0e --- /dev/null +++ b/backend/api/src/manager/TimeLeaderboardManager.ts @@ -0,0 +1,34 @@ +import {Manager} from "./Manager.js"; +import {Leaderboard} from "../model/Leaderboard.js"; +import {Time} from "../model/Time.js"; +import {TimeLeaderboardSerializer} from "../serializer/TimeLeaderboardSerializer.js"; +import {TimeLeaderboardPgPromiseSerializer} from "../serializer/pgpromise/TimeLeaderboardPgPromiseSerializer.js"; + +export class TimeLeaderboardManager implements Manager> { + private _content: Leaderboard