From 1a429ceae599b3faab754c27966652c183b61591 Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 1 Aug 2023 17:23:31 +1000 Subject: [PATCH 01/20] Update removeHtmlEntities to remove additional HTML entities. --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 5ea310b..9904818 100644 --- a/app.js +++ b/app.js @@ -39,5 +39,5 @@ const buildApp = async () => { buildApp(); export function removeHtmlEntities(str) { - return str.replace(/“|”/g, ''); + return str.replace(/“|”|—|’|“|”|‘|–|…|″|‶/g, ''); } \ No newline at end of file From 6927dc7f28f641479a85c69d702a25581344bb1d Mon Sep 17 00:00:00 2001 From: Ben Robson Date: Sat, 5 Aug 2023 13:08:01 +1000 Subject: [PATCH 02/20] Add Code Owners --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..9240f75 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @benrobson \ No newline at end of file From 33cd76262aa14e3c24200b5ad8ec49b0f5939281 Mon Sep 17 00:00:00 2001 From: benrobson Date: Sat, 11 Jan 2025 21:40:42 +1100 Subject: [PATCH 03/20] Update deps, append .js and fix cheerio import. --- app.js | 2 +- package-lock.json | 731 ++++++++++++++++++++++------------------------ package.json | 2 +- routes/index.js | 4 +- 4 files changed, 347 insertions(+), 392 deletions(-) diff --git a/app.js b/app.js index 9904818..bc4a21c 100644 --- a/app.js +++ b/app.js @@ -4,7 +4,7 @@ import dotenv from 'dotenv'; dotenv.config() // Site Routes -import siteRoutes from './routes' +import siteRoutes from './routes/index.js' // // Application Boot diff --git a/package-lock.json b/package-lock.json index 004b25c..afe9cef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0", "license": "ISC", "dependencies": { - "cheerio": "^1.0.0-rc.12", + "cheerio": "^1.0.0", "dotenv": "^16.0.3", "fastify": "^4.15.0", "moment": "^2.29.4", @@ -23,47 +23,34 @@ } }, "node_modules/@fastify/ajv-compiler": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", - "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", + "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", "dependencies": { "ajv": "^8.11.0", "ajv-formats": "^2.1.1", "fast-uri": "^2.0.0" } }, - "node_modules/@fastify/deepmerge": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", - "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" - }, "node_modules/@fastify/error": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.2.0.tgz", - "integrity": "sha512-KAfcLa+CnknwVi5fWogrLXgidLic+GXnLjijXdpl8pvkvbXU5BGa37iZO9FGvsh9ZL4y+oFi5cbHBm5UOG+dmQ==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.2.0.tgz", - "integrity": "sha512-ypZynRvXA3dibfPykQN3RB5wBdEUgSGgny8Qc6k163wYPLD4mEGEDkACp+00YmqkGvIm8D/xYoHajwyEdWD/eg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", "dependencies": { - "fast-json-stringify": "^5.0.0" + "fast-json-stringify": "^5.7.0" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" + "fast-deep-equal": "^3.1.3" } }, "node_modules/abstract-logging": { @@ -72,14 +59,14 @@ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -102,6 +89,21 @@ } } }, + "node_modules/ajv/node_modules/fast-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", + "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -114,11 +116,6 @@ "node": ">= 8" } }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -128,13 +125,12 @@ } }, "node_modules/avvio": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.1.tgz", - "integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", "dependencies": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" } }, "node_modules/balanced-match": { @@ -142,31 +138,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "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/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/boolbase": { @@ -184,54 +164,35 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=18.17" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -254,15 +215,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -275,6 +230,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -285,9 +243,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -327,19 +285,11 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "ms": "^2.1.1" } }, "node_modules/dom-serializer": { @@ -381,30 +331,45 @@ } }, "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "engines": { "node": ">=0.12" }, @@ -412,26 +377,10 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/fast-content-type-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz", - "integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", @@ -444,65 +393,93 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-json-stringify": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.7.0.tgz", - "integrity": "sha512-sBVPTgnAZseLu1Qgj6lUbQ0HfjFhZWXAmpZ5AaSGkyLh5gAXBga/uPJjQPHpDFjC9adWIpdOcCLSDTgrZ7snoQ==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", "dependencies": { - "@fastify/deepmerge": "^1.0.0", + "@fastify/merge-json-schemas": "^0.1.0", "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", + "ajv-formats": "^3.0.1", "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", "rfdc": "^1.2.0" } }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/fast-querystring": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.1.tgz", - "integrity": "sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "node_modules/fast-redact": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", - "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", "engines": { "node": ">=6" } }, "node_modules/fast-uri": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.2.0.tgz", - "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" }, "node_modules/fastify": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.15.0.tgz", - "integrity": "sha512-m/CaRN8nf5uyYdrDe2qqq+0z3oGyE+A++qlKQoLJTI4WI0nWK9D6R3FxXQ3MVwt/md977GMR4F43pE9oqrS2zw==", + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.0.tgz", + "integrity": "sha512-MaaUHUGcCgC8fXQDsDtioaCcag1fmPJ9j64vAKunqZF4aSub040ZGi/ag8NGE2714yREPOKZuHCfpPzuUD3UQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "dependencies": { "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.0.0", - "@fastify/fast-json-stringify-compiler": "^4.2.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", "abstract-logging": "^2.0.1", - "avvio": "^8.2.0", - "fast-content-type-parse": "^1.0.0", - "find-my-way": "^7.6.0", - "light-my-request": "^5.6.1", - "pino": "^8.5.0", - "process-warning": "^2.0.0", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", "proxy-addr": "^2.0.7", "rfdc": "^1.3.0", - "secure-json-parse": "^2.5.0", - "semver": "^7.3.7", - "tiny-lru": "^10.0.0" + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" } }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dependencies": { "reusify": "^1.0.4" } @@ -530,9 +507,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -541,13 +518,13 @@ } }, "node_modules/find-my-way": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.0.tgz", - "integrity": "sha512-H7berWdHJ+5CNVr4ilLWPai4ml7Y2qAsxjw3pfeBxPigZmaDTzF0wjJLj90xRCmGcWYcyt050yN+34OZDJm1eQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", + "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" + "safe-regex2": "^3.1.0" }, "engines": { "node": ">=14" @@ -573,9 +550,9 @@ } }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -605,9 +582,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -618,28 +595,20 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/ignore-by-default": { "version": "1.0.1", @@ -692,32 +661,29 @@ "node": ">=0.12.0" } }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/light-my-request": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.9.1.tgz", - "integrity": "sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz", + "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", "dependencies": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", + "cookie": "^0.7.0", + "process-warning": "^3.0.0", "set-cookie-parser": "^2.4.1" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -730,17 +696,17 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/node-domexception": { "version": "1.0.0", @@ -761,9 +727,9 @@ } }, "node_modules/node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -804,36 +770,14 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/nodemon/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "bin": { "semver": "bin/semver" } }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -854,27 +798,41 @@ } }, "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "dependencies": { - "domhandler": "^5.0.2", "parse5": "^7.0.0" }, "funding": { @@ -893,52 +851,58 @@ } }, "node_modules/pino": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.11.0.tgz", - "integrity": "sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.1.0", - "thread-stream": "^2.0.0" + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", - "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", "dependencies": { - "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "node_modules/pino-std-serializers": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz", - "integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } + "node_modules/pino/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, "node_modules/process-warning": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", - "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -957,33 +921,11 @@ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, - "node_modules/readable-stream": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", - "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1012,11 +954,11 @@ } }, "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", "engines": { - "node": ">=4" + "node": ">=10" } }, "node_modules/reusify": { @@ -1029,30 +971,35 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" }, "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", "dependencies": { - "ret": "~0.2.0" + "ret": "~0.4.0" } }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "engines": { "node": ">=10" } }, + "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/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/secure-json-parse": { "version": "2.7.0", @@ -1060,12 +1007,9 @@ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -1074,9 +1018,9 @@ } }, "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" }, "node_modules/simple-update-notifier": { "version": "1.1.0", @@ -1098,9 +1042,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -1125,21 +1069,13 @@ } }, "node_modules/thread-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", - "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "dependencies": { "real-require": "^0.2.0" } }, - "node_modules/tiny-lru": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-10.4.1.tgz", - "integrity": "sha512-buLIzw7ppqymuO3pt10jHk/6QMeZLbidihMQU+N6sogF6EnBzG0qtDWIHuhw1x3dyNgVL/KTGIZsTK81+yCzLg==", - "engines": { - "node": ">=12" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1151,13 +1087,18 @@ "node": ">=8.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "engines": { + "node": ">=12" + } + }, "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dependencies": { - "nopt": "~1.0.10" - }, + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "bin": { "nodetouch": "bin/nodetouch.js" } @@ -1167,22 +1108,41 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "engines": { + "node": ">=18.17" } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "engines": { "node": ">= 8" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", @@ -1202,11 +1162,6 @@ "engines": { "node": ">=4.0" } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index f1e3e5f..4137959 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "node": "16.17.0" }, "dependencies": { - "cheerio": "^1.0.0-rc.12", + "cheerio": "^1.0.0", "dotenv": "^16.0.3", "fastify": "^4.15.0", "moment": "^2.29.4", diff --git a/routes/index.js b/routes/index.js index 8cd0c4d..5a07d4f 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,8 +1,8 @@ import fetch from 'node-fetch'; -import cheerio from 'cheerio'; +import * as cheerio from "cheerio"; import moment from "moment"; import xml2js from 'xml2js'; -import { removeHtmlEntities } from '../app'; +import { removeHtmlEntities } from '../app.js'; export default function applicationSiteRoutes(app) { app.get('/', async function (req, res) { From da9064b249ef578d366ef070e922a87bd644ae41 Mon Sep 17 00:00:00 2001 From: benrobson Date: Sat, 11 Jan 2025 22:13:19 +1100 Subject: [PATCH 04/20] Start work on multi guild instance, and work on API. --- .env.example | 9 +- api/common.js | 89 +++++++++++++++++ api/routes/index.js | 5 + api/routes/tenant.js | 157 ++++++++++++++++++++++++++++++ api/routes/verifyToken.js | 23 +++++ app.js | 12 +++ controllers/databaseController.js | 30 ++++++ controllers/tenantController.js | 0 dbinit.sql | 7 ++ package-lock.json | 100 +++++++++++++++++++ package.json | 1 + 11 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 api/common.js create mode 100644 api/routes/index.js create mode 100644 api/routes/tenant.js create mode 100644 api/routes/verifyToken.js create mode 100644 controllers/databaseController.js create mode 100644 controllers/tenantController.js create mode 100644 dbinit.sql diff --git a/.env.example b/.env.example index 9ee06aa..96582bf 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,9 @@ PORT=8080 -DEBUG=FALSE \ No newline at end of file +DEBUG=FALSE +APIKEY=KEY + +DBHOST=localhost +DBPORT=3306 +DBUSER=root +DBPASSWORD=root +DBNAME=devoteMe \ No newline at end of file diff --git a/api/common.js b/api/common.js new file mode 100644 index 0000000..27f08cd --- /dev/null +++ b/api/common.js @@ -0,0 +1,89 @@ +import dotenv from "dotenv"; +dotenv.config(); +import fetch from "node-fetch"; +import db from "../controllers/databaseController.js"; + +/* + Ensure that a required field is present and has a non-null value, + and to return an error message if this is not the case. + + @param body Passing through the req.body + @param field The name of the field. + @param res Passing through res. +*/ +export function required(body, field, res) { + // Prematurely exits an API request if a required field has not been + // defined or null. If the body is not defined then we error as well. + // This can happen when no parameters exist. + if (!body || !(field in body)) + return res.send({ + success: false, + message: `Body requires field '${field}'`, + }); + + if (body[field] === null) + return res.send({ + success: false, + message: `Field ${field} cannot be null`, + }); + + return body[field]; +} + +/* + Check if an optional field is present in the body object, and return its value if it is. + + @param body Passing through the req.body + @param field The name of the field. +*/ +export function optional(body, field) { + // Jaedan: I am aware that this is pretty much default behaviour, however + // this takes into consideration times where no body is included. Without + // this check requests with only optional fields (that are all unused) will + // cause a null object to be referenced, causing an error. + if (!body || !(field in body) || body[field] === null) return null; + + return body[field]; +} + +/* + Makes a POST API request to the specified postURL with the provided apiPostBody. + It includes a header with the x-access-token value taken from an environment variable named apiKey. + If the request is successful, it logs the response data. + If the request fails, it sets a cookie with a "danger" alert type and an error message, + then redirects the user to the specified failureRedirectURL. + + @param postURL The POST url that the apiPostBody will go to in the API. + @param apiPostBody The request body for the postURL. + @param failureRedirectURL If the request returns false, where the API will redirect the user to. + @param res Passing through res. +*/ +export async function postAPIRequest( + postURL, + apiPostBody, + failureRedirectURL, + res +) { + const response = await fetch(postURL, { + method: "POST", + body: JSON.stringify(apiPostBody), + headers: { + "Content-Type": "application/json", + "x-access-token": process.env.apiKey, + }, + }); + + const data = await response.json(); + + console.log(data); + + if (data.alertType) { + setBannerCookie(`${data.alertType}`, `${data.alertContent}`, res); + } + + if (!data.success) { + return res.redirect(failureRedirectURL); + } + + return console.log(data); +} \ No newline at end of file diff --git a/api/routes/index.js b/api/routes/index.js new file mode 100644 index 0000000..9bea8f4 --- /dev/null +++ b/api/routes/index.js @@ -0,0 +1,5 @@ +import tenantApiRoute from "./tenant.js"; + +export default (app, db) => { + tenantApiRoute(app, db); +}; diff --git a/api/routes/tenant.js b/api/routes/tenant.js new file mode 100644 index 0000000..a9454bd --- /dev/null +++ b/api/routes/tenant.js @@ -0,0 +1,157 @@ +import { required, optional } from "../common.js"; + +export default function tenantApiRoute(app, db) { + const baseEndpoint = "/api/tenant"; + + app.get(baseEndpoint + "/get", async function (req, res) { + const tenantId = optional(req.query, "id"); + + try { + function getTenants(dbQuery) { + return new Promise((resolve, reject) => { + db.query(dbQuery, function (error, results, fields) { + if (error) { + reject(error); + } else { + resolve(results); + } + }); + }); + } + + // Get Tenant by specific ID. + if (tenantId) { + let dbQuery = `SELECT * FROM tenants WHERE tenantId=${tenantId};`; + const results = await getTenants(dbQuery); + if (!results.length) { + return res.send({ + success: false, + message: `There are no tenants`, + }); + } + + res.send({ + success: true, + data: results, + }); + return; + } + + // Show all Tenants + let dbQuery = `SELECT * FROM tenants;`; + const results = await getTenants(dbQuery); + if (!results.length) { + return res.send({ + success: false, + message: `There are no tenants`, + }); + } + + res.send({ + success: true, + data: results, + }); + } catch (error) { + res.send({ + success: false, + message: `${error}`, + }); + } + }); + + app.post(baseEndpoint + "/create", async function (req, res) { + isFeatureEnabled(features.announcements, res, lang); + + const actioningUser = required(req.body, "actioningUser", res); + const enabled = required(req.body, "enabled", res); + const announcementType = required(req.body, "announcementType", res); + const body = optional(req.body, "body", res); + const colourMessageFormat = optional(req.body, "colourMessageFormat", res); + const link = optional(req.body, "link", res); + + try { + db.query( + `INSERT INTO announcements (enabled, body, announcementType, link, colourMessageFormat) VALUES (?, ?, ?, ?, ?)`, + [ + enabled, + body, + announcementType, + link, + colourMessageFormat, + Date.now(), + ], + function (error, results, fields) { + if (error) { + return res.send({ + success: false, + message: `${error}`, + }); + } + + res.send({ + success: true, + alertType: "success", + content: lang.announcement.announcementCreated, + }); + } + ); + } catch (error) { + res.send({ + success: false, + message: `${error}`, + }); + } + }); + + app.post(baseEndpoint + "/edit", async function (req, res) { + isFeatureEnabled(features.announcements, res, lang); + + const actioningUser = required(req.body, "actioningUser", res); + const announcementId = required(req.body, "announcementId", res); + const enabled = required(req.body, "enabled", res); + const announcementType = required(req.body, "announcementType", res); + const body = optional(req.body, "body", res); + const colourMessageFormat = optional(req.body, "colourMessageFormat", res); + const link = optional(req.body, "link", res); + + try { + db.query( + ` + UPDATE announcements + SET + enabled=?, + announcementType=?, + body=?, + colourMessageFormat=?, + link=? + WHERE announcementId=?;`, + [ + enabled, + announcementType, + body, + colourMessageFormat, + link, + announcementId, + ], + function (error, results, fields) { + if (error) { + return res.send({ + success: false, + message: `${error}`, + }); + } + + return res.send({ + success: true, + message: `lang.announcement.announcementEdited`, + }); + } + ); + } catch (error) { + res.send({ + success: false, + message: `${error}`, + }); + } + }); +} \ No newline at end of file diff --git a/api/routes/verifyToken.js b/api/routes/verifyToken.js new file mode 100644 index 0000000..265ee1b --- /dev/null +++ b/api/routes/verifyToken.js @@ -0,0 +1,23 @@ + +export default function verifyToken(req, res, done) { + var token = req.headers["x-access-token"]; + + if (!token) { + // Token not included + return res.send({ + success: false, + message: `lang.api.noToken`, + }); + } + + if (token === process.env.APIKEY) { + // Passed + done(); + } else { + // Token was incorrect. + return res.send({ + success: false, + message: `lang.api.invalidToken`, + }); + } +} diff --git a/app.js b/app.js index bc4a21c..815e8e0 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,15 @@ import fastify from 'fastify'; import packageData from './package.json' assert {type: "json"}; +import db from "./controllers/databaseController.js"; import dotenv from 'dotenv'; dotenv.config() // Site Routes import siteRoutes from './routes/index.js' +import apiRoutes from "./api/routes/index.js"; + +// API token authentication +import verifyToken from "./api/routes/verifyToken.js"; // // Application Boot @@ -20,6 +25,13 @@ const buildApp = async () => { next(); }); + await app.register((instance, options, next) => { + // API routes (Token authenticated) + instance.addHook("preValidation", verifyToken); + apiRoutes(instance, db); + next(); + }); + const port = process.env.PORT; app.listen({ port: port, host: '0.0.0.0' }, (err) => { diff --git a/controllers/databaseController.js b/controllers/databaseController.js new file mode 100644 index 0000000..1032d0a --- /dev/null +++ b/controllers/databaseController.js @@ -0,0 +1,30 @@ +import mysql from "mysql2/promise"; // Use mysql2/promise for async/await +import dotenv from "dotenv"; + +dotenv.config(); + +const pool = mysql.createPool({ + connectionLimit: 10, + host: process.env.DBHOST, + port: process.env.DBPORT, + user: process.env.DBUSER, + password: process.env.DBPASSWORD, + database: process.env.DBNAME, + multipleStatements: true, +}); + +// Function to get a connection and log a message +async function initializePool() { + try { + const connection = await pool.getConnection(); + console.log(`[CONSOLE] [DB] Database pool connection is successful.`); + connection.release(); + } catch (err) { + console.error(`[ERROR] [DB] There was an error connecting:\n ${err.stack}`); + } +} + +// Call the initialization function +initializePool(); + +export default pool; \ No newline at end of file diff --git a/controllers/tenantController.js b/controllers/tenantController.js new file mode 100644 index 0000000..e69de29 diff --git a/dbinit.sql b/dbinit.sql new file mode 100644 index 0000000..2252ec6 --- /dev/null +++ b/dbinit.sql @@ -0,0 +1,7 @@ +CREATE DATABASE devoteMe; + +CREATE TABLE tenants ( + tenantId INT AUTO_INCREMENT PRIMARY KEY, + tenantName VARCHAR(255) NOT NULL, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/package-lock.json b/package-lock.json index afe9cef..dc216cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "dotenv": "^16.0.3", "fastify": "^4.15.0", "moment": "^2.29.4", + "mysql2": "^3.12.0", "node-fetch": "^3.3.1", "nodemon": "^2.0.22", "xml2js": "^0.5.0" @@ -133,6 +134,14 @@ "fastq": "^1.17.1" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -292,6 +301,14 @@ "ms": "^2.1.1" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -562,6 +579,14 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -661,6 +686,11 @@ "node": ">=0.12.0" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/json-schema-ref-resolver": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", @@ -684,6 +714,33 @@ "set-cookie-parser": "^2.4.1" } }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz", + "integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -708,6 +765,36 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mysql2": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz", + "integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -1017,6 +1104,11 @@ "node": ">=10" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -1057,6 +1149,14 @@ "node": ">= 10.x" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 4137959..bd0c26d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dotenv": "^16.0.3", "fastify": "^4.15.0", "moment": "^2.29.4", + "mysql2": "^3.12.0", "node-fetch": "^3.3.1", "nodemon": "^2.0.22", "xml2js": "^0.5.0" From 8e40c9d2f2c9cb9d9011131559cac3eba054d5ba Mon Sep 17 00:00:00 2001 From: benrobson Date: Sun, 12 Jan 2025 12:30:08 +1100 Subject: [PATCH 05/20] Tenant create operational. --- api/routes/tenant.js | 119 +++++++++--------------------- controllers/databaseController.js | 6 +- dbinit.sql | 4 +- 3 files changed, 41 insertions(+), 88 deletions(-) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index a9454bd..dc5fca7 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -3,23 +3,18 @@ import { required, optional } from "../common.js"; export default function tenantApiRoute(app, db) { const baseEndpoint = "/api/tenant"; + // Function to fetch tenants + async function getTenants(dbQuery) { + const [results, fields] = await db.query(dbQuery); + return results; + } + + // Get tenant(s) endpoint app.get(baseEndpoint + "/get", async function (req, res) { const tenantId = optional(req.query, "id"); try { - function getTenants(dbQuery) { - return new Promise((resolve, reject) => { - db.query(dbQuery, function (error, results, fields) { - if (error) { - reject(error); - } else { - resolve(results); - } - }); - }); - } - - // Get Tenant by specific ID. + // Get Tenant by specific ID if (tenantId) { let dbQuery = `SELECT * FROM tenants WHERE tenantId=${tenantId};`; const results = await getTenants(dbQuery); @@ -59,42 +54,21 @@ export default function tenantApiRoute(app, db) { } }); + // Create tenant endpoint app.post(baseEndpoint + "/create", async function (req, res) { - isFeatureEnabled(features.announcements, res, lang); - - const actioningUser = required(req.body, "actioningUser", res); - const enabled = required(req.body, "enabled", res); - const announcementType = required(req.body, "announcementType", res); - const body = optional(req.body, "body", res); - const colourMessageFormat = optional(req.body, "colourMessageFormat", res); - const link = optional(req.body, "link", res); + const tenantId = required(req.body, "tenantId", res); + const tenantName = required(req.body, "tenantName", res); try { - db.query( - `INSERT INTO announcements (enabled, body, announcementType, link, colourMessageFormat) VALUES (?, ?, ?, ?, ?)`, - [ - enabled, - body, - announcementType, - link, - colourMessageFormat, - Date.now(), - ], - function (error, results, fields) { - if (error) { - return res.send({ - success: false, - message: `${error}`, - }); - } - - res.send({ - success: true, - alertType: "success", - content: lang.announcement.announcementCreated, - }); - } + await db.query( + `INSERT INTO tenants (tenantId, tenantName) VALUES (?, ?)`, + [tenantId, tenantName] ); + + res.send({ + success: true, + content: `New Tenant Created: ${tenantName}`, + }); } catch (error) { res.send({ success: false, @@ -103,50 +77,29 @@ export default function tenantApiRoute(app, db) { } }); - app.post(baseEndpoint + "/edit", async function (req, res) { - isFeatureEnabled(features.announcements, res, lang); - - const actioningUser = required(req.body, "actioningUser", res); - const announcementId = required(req.body, "announcementId", res); - const enabled = required(req.body, "enabled", res); - const announcementType = required(req.body, "announcementType", res); - const body = optional(req.body, "body", res); - const colourMessageFormat = optional(req.body, "colourMessageFormat", res); - const link = optional(req.body, "link", res); + // Edit tenant endpoint + app.post(baseEndpoint + "/update", async function (req, res) { + const tenantId = required(req.body, "tenantId", res); + const tenantName = required(req.body, "tenantName", res); try { - db.query( + await db.query( ` - UPDATE announcements + UPDATE tenants SET - enabled=?, - announcementType=?, - body=?, - colourMessageFormat=?, - link=? - WHERE announcementId=?;`, + tenantName=? + WHERE tenantId=?; + `, [ - enabled, - announcementType, - body, - colourMessageFormat, - link, - announcementId, - ], - function (error, results, fields) { - if (error) { - return res.send({ - success: false, - message: `${error}`, - }); - } - - return res.send({ - success: true, - message: `lang.announcement.announcementEdited`, - }); - } + tenantName, + tenantId + ] ); + + return res.send({ + success: true, + message: `Tenant updated`, + }); } catch (error) { res.send({ success: false, diff --git a/controllers/databaseController.js b/controllers/databaseController.js index 1032d0a..fc8ddcc 100644 --- a/controllers/databaseController.js +++ b/controllers/databaseController.js @@ -1,4 +1,4 @@ -import mysql from "mysql2/promise"; // Use mysql2/promise for async/await +import mysql from "mysql2"; import dotenv from "dotenv"; dotenv.config(); @@ -10,8 +10,8 @@ const pool = mysql.createPool({ user: process.env.DBUSER, password: process.env.DBPASSWORD, database: process.env.DBNAME, - multipleStatements: true, -}); + multipleStatements: true +}).promise(); // Function to get a connection and log a message async function initializePool() { diff --git a/dbinit.sql b/dbinit.sql index 2252ec6..bf543ce 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -1,7 +1,7 @@ CREATE DATABASE devoteMe; CREATE TABLE tenants ( - tenantId INT AUTO_INCREMENT PRIMARY KEY, + tenantId BIGINT PRIMARY KEY, tenantName VARCHAR(255) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); +); \ No newline at end of file From 9920513d57f04ada738b3ea04d0a5db7cdd14fef Mon Sep 17 00:00:00 2001 From: benrobson Date: Sun, 12 Jan 2025 21:09:37 +1100 Subject: [PATCH 06/20] Implemented Devotion endpoint and track stats --- api/routes/devotion.js | 29 +++++++++++++++++++++++++++++ api/routes/index.js | 2 ++ api/routes/tenant.js | 9 ++++----- dbinit.sql | 18 ++++++++++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 api/routes/devotion.js diff --git a/api/routes/devotion.js b/api/routes/devotion.js new file mode 100644 index 0000000..2aec23e --- /dev/null +++ b/api/routes/devotion.js @@ -0,0 +1,29 @@ +import { required, optional } from "../common.js"; + +export default function devotionApiRoute(app, db) { + const baseEndpoint = "/api/devotion"; + + // Create devotion endpoint + app.post(baseEndpoint + "/add", async function (req, res) { + const tenantId = required(req.body, "tenantId", res); + const messageId = required(req.body, "messageId", res); + const userId = required(req.body, "userId", res); + + try { + await db.query( + `INSERT INTO devotions (tenantId, messageId, userId) VALUES (?, ?, ?)`, + [tenantId, messageId, userId] + ); + + res.send({ + success: true, + content: `Devotion entry created for tenant ${tenantId} with message ID ${messageId}`, + }); + } catch (error) { + res.send({ + success: false, + message: `Error creating devotion entry: ${error}`, + }); + } + }); +} \ No newline at end of file diff --git a/api/routes/index.js b/api/routes/index.js index 9bea8f4..4936335 100644 --- a/api/routes/index.js +++ b/api/routes/index.js @@ -1,5 +1,7 @@ +import devotionApiRoute from "./devotion.js"; import tenantApiRoute from "./tenant.js"; export default (app, db) => { tenantApiRoute(app, db); + devotionApiRoute(app, db); }; diff --git a/api/routes/tenant.js b/api/routes/tenant.js index dc5fca7..fab49b9 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -77,10 +77,11 @@ export default function tenantApiRoute(app, db) { } }); - // Edit tenant endpoint + // Update tenant endpoint app.post(baseEndpoint + "/update", async function (req, res) { const tenantId = required(req.body, "tenantId", res); const tenantName = required(req.body, "tenantName", res); + const tenantConfiguration = required(req.body, "tenantConfiguration", res); try { await db.query( @@ -88,12 +89,10 @@ export default function tenantApiRoute(app, db) { UPDATE tenants SET tenantName=? + tenantConfiguration=? WHERE tenantId=?; `, - [ - tenantName, - tenantId - ] + [tenantName, tenantConfiguration, tenantId] ); return res.send({ diff --git a/dbinit.sql b/dbinit.sql index bf543ce..721cf82 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -4,4 +4,22 @@ CREATE TABLE tenants ( tenantId BIGINT PRIMARY KEY, tenantName VARCHAR(255) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE prayers ( + prayerId INT PRIMARY KEY AUTO_INCREMENT, + tenantId BIGINT NOT NULL, + messageId VARCHAR(255) NOT NULL, + userId BIGINT NOT NULL, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) +); + +CREATE TABLE devotions ( + devotionId INT PRIMARY KEY AUTO_INCREMENT, + tenantId BIGINT NOT NULL, + messageId VARCHAR(255) UNIQUE NOT NULL, + userId BIGINT NOT NULL, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ); \ No newline at end of file From c0d7d40f3f9ee6a4aedfd90df5c522ce1270e2ee Mon Sep 17 00:00:00 2001 From: benrobson Date: Sun, 12 Jan 2025 21:30:55 +1100 Subject: [PATCH 07/20] Add stat tracking for votd and /check routes on votd and devotion for duplicates. --- api/routes/devotion.js | 28 +++++++++++++++++++++ api/routes/index.js | 2 ++ api/routes/votd.js | 57 ++++++++++++++++++++++++++++++++++++++++++ dbinit.sql | 9 +++++++ 4 files changed, 96 insertions(+) create mode 100644 api/routes/votd.js diff --git a/api/routes/devotion.js b/api/routes/devotion.js index 2aec23e..e600afb 100644 --- a/api/routes/devotion.js +++ b/api/routes/devotion.js @@ -3,6 +3,34 @@ import { required, optional } from "../common.js"; export default function devotionApiRoute(app, db) { const baseEndpoint = "/api/devotion"; + app.get(baseEndpoint + "/check", async function (req, res) { + const tenantId = required(req.query, "tenantId", res); + const messageId = required(req.query, "messageId", res); + const userId = required(req.query, "userId", res); + + try { + // Perform a SELECT query to check if the record exists + const [rows] = await db.query( + `SELECT 1 FROM devotions WHERE tenantId = ? AND messageId = ? AND userId = ? LIMIT 1`, + [tenantId, messageId, userId] + ); + + // If any row is returned, that means the record exists + const exists = rows.length > 0; + + // Return a boolean indicating if the record exists + res.send({ + success: true, + exists: exists, + }); + } catch (error) { + res.send({ + success: false, + message: `Error checking Devotion entry: ${error}`, + }); + } + }); + // Create devotion endpoint app.post(baseEndpoint + "/add", async function (req, res) { const tenantId = required(req.body, "tenantId", res); diff --git a/api/routes/index.js b/api/routes/index.js index 4936335..299a037 100644 --- a/api/routes/index.js +++ b/api/routes/index.js @@ -1,7 +1,9 @@ import devotionApiRoute from "./devotion.js"; import tenantApiRoute from "./tenant.js"; +import votdApiRoute from "./votd.js"; export default (app, db) => { tenantApiRoute(app, db); devotionApiRoute(app, db); + votdApiRoute(app, db); }; diff --git a/api/routes/votd.js b/api/routes/votd.js new file mode 100644 index 0000000..7c37d0d --- /dev/null +++ b/api/routes/votd.js @@ -0,0 +1,57 @@ +import { required, optional } from "../common.js"; + +export default function votdApiRoute(app, db) { + const baseEndpoint = "/api/votd"; + + app.get(baseEndpoint + "/check", async function (req, res) { + const tenantId = required(req.query, "tenantId", res); + const messageId = required(req.query, "messageId", res); + const userId = required(req.query, "userId", res); + + try { + // Perform a SELECT query to check if the record exists + const [rows] = await db.query( + `SELECT 1 FROM votd WHERE tenantId = ? AND messageId = ? AND userId = ? LIMIT 1`, + [tenantId, messageId, userId] + ); + + // If any row is returned, that means the record exists + const exists = rows.length > 0; + + // Return a boolean indicating if the record exists + res.send({ + success: true, + exists: exists, + }); + } catch (error) { + res.send({ + success: false, + message: `Error checking VOTD entry: ${error}`, + }); + } + }); + + // Add votd endpoint + app.post(baseEndpoint + "/add", async function (req, res) { + const tenantId = required(req.body, "tenantId", res); + const messageId = required(req.body, "messageId", res); + const userId = required(req.body, "userId", res); + + try { + await db.query( + `INSERT INTO votd (tenantId, messageId, userId) VALUES (?, ?, ?)`, + [tenantId, messageId, userId] + ); + + res.send({ + success: true, + content: `VOTD entry created for tenant ${tenantId} with message ID ${messageId}`, + }); + } catch (error) { + res.send({ + success: false, + message: `Error creating devotion entry: ${error}`, + }); + } + }); +} \ No newline at end of file diff --git a/dbinit.sql b/dbinit.sql index 721cf82..05e8f18 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -22,4 +22,13 @@ CREATE TABLE devotions ( userId BIGINT NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) +); + +CREATE TABLE votd ( + votdId INT PRIMARY KEY AUTO_INCREMENT, + tenantId BIGINT NOT NULL, + messageId VARCHAR(255) UNIQUE NOT NULL, + userId BIGINT NOT NULL, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ); \ No newline at end of file From f84c98068e464654d888483e370b5394d0f7acaa Mon Sep 17 00:00:00 2001 From: benrobson Date: Sun, 12 Jan 2025 22:06:15 +1100 Subject: [PATCH 08/20] Start work on tenant configuration db table and route --- api/routes/tenant.js | 71 ++++++++++++++++++++++++++++++++++---------- dbinit.sql | 8 +++++ 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index fab49b9..8899567 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -80,29 +80,68 @@ export default function tenantApiRoute(app, db) { // Update tenant endpoint app.post(baseEndpoint + "/update", async function (req, res) { const tenantId = required(req.body, "tenantId", res); - const tenantName = required(req.body, "tenantName", res); - const tenantConfiguration = required(req.body, "tenantConfiguration", res); + const votdChannel = optional(req.body, "votd_channel", res); + const devotionChannel = optional(req.body, "devotion_channel", res); try { - await db.query( - ` - UPDATE tenants - SET - tenantName=? - tenantConfiguration=? - WHERE tenantId=?; - `, - [tenantName, tenantConfiguration, tenantId] + // Check if the tenant's configuration already exists + const existingConfig = await db.query( + `SELECT id FROM tenantConfiguration WHERE tenantId = ?`, + [tenantId] ); - return res.send({ - success: true, - message: `Tenant updated`, - }); + let updateFields = []; + let updateValues = []; + + if (votdChannel) { + updateFields.push("votd_channel = ?"); + updateValues.push(votdChannel); + } + + if (devotionChannel) { + updateFields.push("devotion_channel = ?"); + updateValues.push(devotionChannel); + } + + // Check if there are fields to update + if (updateFields.length > 0) { + updateValues.push(tenantId); // Add tenantId for the WHERE clause + + // If configuration exists, update the fields + if (existingConfig.length > 0) { + await db.query( + ` + UPDATE tenantConfiguration + SET ${updateFields.join(", ")} + WHERE tenantId = ?; + `, + updateValues + ); + } else { + // If configuration doesn't exist, insert new values + await db.query( + ` + INSERT INTO tenantConfiguration (tenantId, ${updateFields.join(", ")}) + VALUES (?, ?); + `, + [tenantId, ...updateValues] + ); + } + + return res.send({ + success: true, + message: `Tenant configuration updated`, + }); + } else { + return res.send({ + success: false, + message: "No valid fields provided to update.", + }); + } } catch (error) { res.send({ success: false, - message: `${error}`, + message: `Error: ${error.message}`, }); } }); diff --git a/dbinit.sql b/dbinit.sql index 05e8f18..b6e21e6 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -6,6 +6,14 @@ CREATE TABLE tenants ( createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE tenantConfiguration ( + id INT AUTO_INCREMENT PRIMARY KEY, + tenantId INT NOT NULL, + devotion_channel BIGINT, + votd_channel BIGINT, + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE +); + CREATE TABLE prayers ( prayerId INT PRIMARY KEY AUTO_INCREMENT, tenantId BIGINT NOT NULL, From 5d455a1d3a91addf3f8b175cce492fcb9c3d896e Mon Sep 17 00:00:00 2001 From: benrobson Date: Mon, 13 Jan 2025 04:39:47 +1100 Subject: [PATCH 09/20] Fix tenant update --- api/routes/tenant.js | 58 +++++++++++++++++++++++--------------------- dbinit.sql | 19 ++++++++------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index 8899567..50cd569 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -60,11 +60,17 @@ export default function tenantApiRoute(app, db) { const tenantName = required(req.body, "tenantName", res); try { + // Insert into tenants table await db.query( `INSERT INTO tenants (tenantId, tenantName) VALUES (?, ?)`, [tenantId, tenantName] ); + // Insert into tenantConfiguration table with the tenantId + await db.query(`INSERT INTO tenantConfiguration (tenantId) VALUES (?)`, [ + tenantId, + ]); + res.send({ success: true, content: `New Tenant Created: ${tenantName}`, @@ -72,7 +78,7 @@ export default function tenantApiRoute(app, db) { } catch (error) { res.send({ success: false, - message: `${error}`, + message: `${error.message}`, }); } }); @@ -84,47 +90,43 @@ export default function tenantApiRoute(app, db) { const devotionChannel = optional(req.body, "devotion_channel", res); try { - // Check if the tenant's configuration already exists - const existingConfig = await db.query( - `SELECT id FROM tenantConfiguration WHERE tenantId = ?`, - [tenantId] - ); - - let updateFields = []; - let updateValues = []; + // Determine the field and value to update + let fieldToUpdate = ""; + let valueToUpdate = null; if (votdChannel) { - updateFields.push("votd_channel = ?"); - updateValues.push(votdChannel); + fieldToUpdate = "votd_channel"; + valueToUpdate = votdChannel; + } else if (devotionChannel) { + fieldToUpdate = "devotion_channel"; + valueToUpdate = devotionChannel; } - if (devotionChannel) { - updateFields.push("devotion_channel = ?"); - updateValues.push(devotionChannel); - } - - // Check if there are fields to update - if (updateFields.length > 0) { - updateValues.push(tenantId); // Add tenantId for the WHERE clause + if (fieldToUpdate) { + // Check if the tenant's configuration already exists in tenantConfiguration + const existingConfig = await db.query( + `SELECT id FROM tenantConfiguration WHERE tenantId = ?`, + [tenantId] + ); - // If configuration exists, update the fields if (existingConfig.length > 0) { + // If configuration exists, update the field await db.query( ` UPDATE tenantConfiguration - SET ${updateFields.join(", ")} + SET ${fieldToUpdate} = ? WHERE tenantId = ?; - `, - updateValues + `, + [valueToUpdate, tenantId] ); } else { // If configuration doesn't exist, insert new values await db.query( ` - INSERT INTO tenantConfiguration (tenantId, ${updateFields.join(", ")}) + INSERT INTO tenantConfiguration (tenantId, ${fieldToUpdate}) VALUES (?, ?); - `, - [tenantId, ...updateValues] + `, + [tenantId, valueToUpdate] ); } @@ -135,10 +137,12 @@ export default function tenantApiRoute(app, db) { } else { return res.send({ success: false, - message: "No valid fields provided to update.", + message: "No valid field provided to update.", }); } } catch (error) { + console.log(error); + res.send({ success: false, message: `Error: ${error.message}`, diff --git a/dbinit.sql b/dbinit.sql index b6e21e6..da73920 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -1,4 +1,5 @@ CREATE DATABASE devoteMe; +USE devoteMe; CREATE TABLE tenants ( tenantId BIGINT PRIMARY KEY, @@ -7,11 +8,11 @@ CREATE TABLE tenants ( ); CREATE TABLE tenantConfiguration ( - id INT AUTO_INCREMENT PRIMARY KEY, - tenantId INT NOT NULL, - devotion_channel BIGINT, - votd_channel BIGINT, - FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE + id INT AUTO_INCREMENT PRIMARY KEY, + tenantId BIGINT NOT NULL, + devotion_channel BIGINT, + votd_channel BIGINT, + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE prayers ( @@ -20,7 +21,7 @@ CREATE TABLE prayers ( messageId VARCHAR(255) NOT NULL, userId BIGINT NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE devotions ( @@ -29,7 +30,7 @@ CREATE TABLE devotions ( messageId VARCHAR(255) UNIQUE NOT NULL, userId BIGINT NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE votd ( @@ -38,5 +39,5 @@ CREATE TABLE votd ( messageId VARCHAR(255) UNIQUE NOT NULL, userId BIGINT NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) -); \ No newline at end of file + FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE +); From f53a10ceb35aaa7710627d78867d208c1880e958 Mon Sep 17 00:00:00 2001 From: benrobson Date: Mon, 13 Jan 2025 04:45:57 +1100 Subject: [PATCH 10/20] Update engine requirements. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd0c26d..13b63bb 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "license": "ISC", "engines": { "npm": ">=8.5.0", - "node": "16.17.0" + "node": ">=20.0.0" }, "dependencies": { "cheerio": "^1.0.0", From 01a6bd401c881f27898239aad813b8a5fc3a4026 Mon Sep 17 00:00:00 2001 From: benrobson Date: Mon, 13 Jan 2025 05:05:11 +1100 Subject: [PATCH 11/20] Added /configuration/get endpoint. --- api/routes/tenant.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index 50cd569..4ed12b8 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -54,6 +54,34 @@ export default function tenantApiRoute(app, db) { } }); + // Get tenant configuration by ID endpoint + app.get(baseEndpoint + "/configuration/get", async function (req, res) { + const tenantId = required(req.query, "id", res); + + try { + // Get Tenant Configuration by specific ID + const dbQuery = `SELECT * FROM tenantConfiguration WHERE tenantId = ?`; + const results = await db.query(dbQuery, [tenantId]); + + if (!results.length) { + return res.send({ + success: false, + message: `No configuration found for tenantId ${tenantId}`, + }); + } + + res.send({ + success: true, + data: results[0], + }); + } catch (error) { + res.send({ + success: false, + message: `Error: ${error.message}`, + }); + } + }); + // Create tenant endpoint app.post(baseEndpoint + "/create", async function (req, res) { const tenantId = required(req.body, "tenantId", res); From 33ba6af727cdc1b5ca6338bbbcf6b070927c2fa1 Mon Sep 17 00:00:00 2001 From: benrobson Date: Mon, 13 Jan 2025 05:06:30 +1100 Subject: [PATCH 12/20] Make it just node 20 for now --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13b63bb..51cc8ce 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "license": "ISC", "engines": { "npm": ">=8.5.0", - "node": ">=20.0.0" + "node": "20.0.0" }, "dependencies": { "cheerio": "^1.0.0", From cc49a0d13436db30239d539e1ceb527a2b093cba Mon Sep 17 00:00:00 2001 From: benrobson Date: Mon, 13 Jan 2025 22:10:43 +1100 Subject: [PATCH 13/20] Make all users, guilds and channels 20 chars. --- api/routes/tenant.js | 1 + dbinit.sql | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index 4ed12b8..5874285 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -104,6 +104,7 @@ export default function tenantApiRoute(app, db) { content: `New Tenant Created: ${tenantName}`, }); } catch (error) { + console.log(error); res.send({ success: false, message: `${error.message}`, diff --git a/dbinit.sql b/dbinit.sql index da73920..90dced5 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -2,42 +2,42 @@ CREATE DATABASE devoteMe; USE devoteMe; CREATE TABLE tenants ( - tenantId BIGINT PRIMARY KEY, - tenantName VARCHAR(255) NOT NULL, + tenantId VARCHAR(20) PRIMARY KEY, + tenantName VARCHAR(100) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE tenantConfiguration ( id INT AUTO_INCREMENT PRIMARY KEY, - tenantId BIGINT NOT NULL, - devotion_channel BIGINT, - votd_channel BIGINT, + tenantId VARCHAR(20) NOT NULL, + devotion_channel VARCHAR(20), + votd_channel VARCHAR(20), FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE prayers ( prayerId INT PRIMARY KEY AUTO_INCREMENT, - tenantId BIGINT NOT NULL, + tenantId VARCHAR(20) NOT NULL, messageId VARCHAR(255) NOT NULL, - userId BIGINT NOT NULL, + userId VARCHAR(20) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE devotions ( devotionId INT PRIMARY KEY AUTO_INCREMENT, - tenantId BIGINT NOT NULL, + tenantId VARCHAR(20) NOT NULL, messageId VARCHAR(255) UNIQUE NOT NULL, - userId BIGINT NOT NULL, + userId VARCHAR(20) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); CREATE TABLE votd ( votdId INT PRIMARY KEY AUTO_INCREMENT, - tenantId BIGINT NOT NULL, + tenantId VARCHAR(20) NOT NULL, messageId VARCHAR(255) UNIQUE NOT NULL, - userId BIGINT NOT NULL, + userId VARCHAR(20) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE ); From 56117350b971b1fc851e7404ab30eaf8116e923e Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 14 Jan 2025 00:55:54 +1100 Subject: [PATCH 14/20] Added some logging to tenant/create --- api/routes/tenant.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/api/routes/tenant.js b/api/routes/tenant.js index 5874285..275aaec 100644 --- a/api/routes/tenant.js +++ b/api/routes/tenant.js @@ -87,24 +87,40 @@ export default function tenantApiRoute(app, db) { const tenantId = required(req.body, "tenantId", res); const tenantName = required(req.body, "tenantName", res); + console.log( + `Received request to create tenant. Tenant ID: ${tenantId}, Tenant Name: ${tenantName}` + ); + try { // Insert into tenants table + console.log(`Inserting into tenants table: ${tenantId}, ${tenantName}`); await db.query( `INSERT INTO tenants (tenantId, tenantName) VALUES (?, ?)`, [tenantId, tenantName] ); // Insert into tenantConfiguration table with the tenantId + console.log( + `Inserting into tenantConfiguration table for tenantId: ${tenantId}` + ); await db.query(`INSERT INTO tenantConfiguration (tenantId) VALUES (?)`, [ tenantId, ]); + console.log( + `Successfully created tenant: ${tenantName} (ID: ${tenantId})` + ); + res.send({ success: true, content: `New Tenant Created: ${tenantName}`, }); } catch (error) { - console.log(error); + console.error( + `Error occurred while creating tenant: ${tenantName} (ID: ${tenantId})`, + error + ); + res.send({ success: false, message: `${error.message}`, From 3278e26c85949aa546b0c535775b6d6b0d2c412d Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 14 Jan 2025 01:03:49 +1100 Subject: [PATCH 15/20] Amend port specification --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 815e8e0..25274cb 100644 --- a/app.js +++ b/app.js @@ -32,7 +32,7 @@ const buildApp = async () => { next(); }); - const port = process.env.PORT; + const port = process.env.PORT || 3000; app.listen({ port: port, host: '0.0.0.0' }, (err) => { if (err) { From a859a828a952fa5e0e7aee1739ec8989b18f7a4a Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 14 Jan 2025 22:33:21 +1100 Subject: [PATCH 16/20] Add temp logging of token --- api/routes/verifyToken.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/routes/verifyToken.js b/api/routes/verifyToken.js index 265ee1b..8f8c242 100644 --- a/api/routes/verifyToken.js +++ b/api/routes/verifyToken.js @@ -17,6 +17,7 @@ export default function verifyToken(req, res, done) { // Token was incorrect. return res.send({ success: false, + token: token, message: `lang.api.invalidToken`, }); } From b48f0bf09a081f7785edfea61d01232707a3a559 Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 14 Jan 2025 22:45:50 +1100 Subject: [PATCH 17/20] Revert logging token --- api/routes/verifyToken.js | 1 - 1 file changed, 1 deletion(-) diff --git a/api/routes/verifyToken.js b/api/routes/verifyToken.js index 8f8c242..265ee1b 100644 --- a/api/routes/verifyToken.js +++ b/api/routes/verifyToken.js @@ -17,7 +17,6 @@ export default function verifyToken(req, res, done) { // Token was incorrect. return res.send({ success: false, - token: token, message: `lang.api.invalidToken`, }); } From 846d3a5de8b979c6322dbea1662adac4527adaff Mon Sep 17 00:00:00 2001 From: benrobson Date: Tue, 14 Jan 2025 23:11:06 +1100 Subject: [PATCH 18/20] Fix APIKEY on postAPIRequest --- api/common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/common.js b/api/common.js index 27f08cd..451a038 100644 --- a/api/common.js +++ b/api/common.js @@ -48,7 +48,7 @@ export function optional(body, field) { /* Makes a POST API request to the specified postURL with the provided apiPostBody. - It includes a header with the x-access-token value taken from an environment variable named apiKey. + It includes a header with the x-access-token value taken from an environment variable named APIKEY. If the request is successful, it logs the response data. If the request fails, it sets a cookie with a "danger" alert type and an error message, then redirects the user to the specified failureRedirectURL. @@ -69,7 +69,7 @@ export async function postAPIRequest( body: JSON.stringify(apiPostBody), headers: { "Content-Type": "application/json", - "x-access-token": process.env.apiKey, + "x-access-token": process.env.APIKEY, }, }); From 5333af762fdbd953943e6abf25d8e8ad14191c73 Mon Sep 17 00:00:00 2001 From: benrobson Date: Fri, 17 Jan 2025 23:15:01 +1100 Subject: [PATCH 19/20] Remove UNIQUE from devotions and votd table --- dbinit.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbinit.sql b/dbinit.sql index 90dced5..f5627ac 100644 --- a/dbinit.sql +++ b/dbinit.sql @@ -27,7 +27,7 @@ CREATE TABLE prayers ( CREATE TABLE devotions ( devotionId INT PRIMARY KEY AUTO_INCREMENT, tenantId VARCHAR(20) NOT NULL, - messageId VARCHAR(255) UNIQUE NOT NULL, + messageId VARCHAR(255) NOT NULL, userId VARCHAR(20) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE @@ -36,7 +36,7 @@ CREATE TABLE devotions ( CREATE TABLE votd ( votdId INT PRIMARY KEY AUTO_INCREMENT, tenantId VARCHAR(20) NOT NULL, - messageId VARCHAR(255) UNIQUE NOT NULL, + messageId VARCHAR(255) NOT NULL, userId VARCHAR(20) NOT NULL, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenantId) REFERENCES tenants(tenantId) ON DELETE CASCADE From 7d46d8f4738fbb7a6ef8dcf4ac4029204e655a3e Mon Sep 17 00:00:00 2001 From: shadowolf Date: Tue, 17 Jun 2025 19:47:20 +1000 Subject: [PATCH 20/20] TEST: Up version to 20.18.1 --- app.js | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 25274cb..424472e 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,5 @@ import fastify from 'fastify'; -import packageData from './package.json' assert {type: "json"}; +import packageData from './package.json' with { type: "json" }; import db from "./controllers/databaseController.js"; import dotenv from 'dotenv'; dotenv.config() diff --git a/package-lock.json b/package-lock.json index dc216cc..180c491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "xml2js": "^0.5.0" }, "engines": { - "node": "16.17.0", + "node": "20.0.0", "npm": ">=8.5.0" } }, diff --git a/package.json b/package.json index 51cc8ce..da4b21e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "license": "ISC", "engines": { "npm": ">=8.5.0", - "node": "20.0.0" + "node": ">=20.18.1" }, "dependencies": { "cheerio": "^1.0.0",