From f65bf959b1bdc392725b03338ce6dc81cbcbd89d Mon Sep 17 00:00:00 2001
From: Mike Smith <89040888+smiggiddy@users.noreply.github.com>
Date: Mon, 24 Mar 2025 04:06:46 -0400
Subject: [PATCH] rename the app

---
 messages/.dockerignore                      |  15 +
 messages/Dockerfile                         |  31 ++
 messages/bun.lock                           | 468 ++++++++++++++++++++
 messages/package.json                       |  11 +
 messages/public/css/style.css               | 428 ++++++++++++++++++
 messages/public/images/favicon.png          | Bin 0 -> 1240 bytes
 messages/public/js/script.js                | 109 +++++
 messages/src/app.js                         |  53 +++
 messages/src/config.js                      |   0
 messages/src/controllers/authController.js  |  46 ++
 messages/src/controllers/indexController.js |  90 ++++
 messages/src/middlewares/auth.js            |  50 +++
 messages/src/middlewares/logging.js         |   8 +
 messages/src/models/db.js                   |  35 ++
 messages/src/models/init.sql                |  73 +++
 messages/src/models/query.js                | 138 ++++++
 messages/src/routes/authRouter.js           |  32 ++
 messages/src/routes/indexRouter.js          |  13 +
 messages/src/views/content.ejs              |   0
 messages/src/views/feed.ejs                 |  31 ++
 messages/src/views/footer.ejs               |  12 +
 messages/src/views/header.ejs               |  36 ++
 messages/src/views/home.ejs                 |  49 ++
 messages/src/views/login.ejs                |  30 ++
 messages/src/views/note-form.ejs            |  19 +
 messages/src/views/register.ejs             |  35 ++
 messages/src/views/view-profile.ejs         |  20 +
 27 files changed, 1832 insertions(+)
 create mode 100644 messages/.dockerignore
 create mode 100644 messages/Dockerfile
 create mode 100644 messages/bun.lock
 create mode 100644 messages/package.json
 create mode 100644 messages/public/css/style.css
 create mode 100644 messages/public/images/favicon.png
 create mode 100644 messages/public/js/script.js
 create mode 100644 messages/src/app.js
 create mode 100644 messages/src/config.js
 create mode 100644 messages/src/controllers/authController.js
 create mode 100644 messages/src/controllers/indexController.js
 create mode 100644 messages/src/middlewares/auth.js
 create mode 100644 messages/src/middlewares/logging.js
 create mode 100644 messages/src/models/db.js
 create mode 100644 messages/src/models/init.sql
 create mode 100644 messages/src/models/query.js
 create mode 100644 messages/src/routes/authRouter.js
 create mode 100644 messages/src/routes/indexRouter.js
 create mode 100644 messages/src/views/content.ejs
 create mode 100644 messages/src/views/feed.ejs
 create mode 100644 messages/src/views/footer.ejs
 create mode 100644 messages/src/views/header.ejs
 create mode 100644 messages/src/views/home.ejs
 create mode 100644 messages/src/views/login.ejs
 create mode 100644 messages/src/views/note-form.ejs
 create mode 100644 messages/src/views/register.ejs
 create mode 100644 messages/src/views/view-profile.ejs

diff --git a/messages/.dockerignore b/messages/.dockerignore
new file mode 100644
index 0000000..f965aed
--- /dev/null
+++ b/messages/.dockerignore
@@ -0,0 +1,15 @@
+node_modules
+Dockerfile*
+docker-compose*
+.dockerignore
+.git
+.gitignore
+README.md
+LICENSE
+.vscode
+Makefile
+helm-charts
+.env
+.editorconfig
+.idea
+coverage*
diff --git a/messages/Dockerfile b/messages/Dockerfile
new file mode 100644
index 0000000..d0631b9
--- /dev/null
+++ b/messages/Dockerfile
@@ -0,0 +1,31 @@
+FROM oven/bun:1 AS base
+WORKDIR /usr/src/app
+
+# install dependencies into temp directory
+# this will cache them and speed up future builds
+FROM base AS install
+RUN mkdir -p /temp/dev
+COPY package.json bun.lock /temp/dev/
+RUN cd /temp/dev && bun install --frozen-lockfile
+
+# copy node_modules from temp directory
+# then copy all (non-ignored) project files into the image
+FROM base AS prerelease
+COPY --from=install /temp/dev/node_modules node_modules
+COPY . .
+
+# [optional] tests & build
+ENV NODE_ENV=production
+RUN bun test
+RUN bun run build
+
+# copy production dependencies and source code into final image
+FROM base AS release
+COPY --from=install /temp/prod/node_modules node_modules
+COPY --from=prerelease /usr/src/app/app.js .
+COPY --from=prerelease /usr/src/app/package.json .
+
+# run the app
+USER bun
+EXPOSE 3000/tcp
+ENTRYPOINT [ "bun", "run", "app.js" ]
diff --git a/messages/bun.lock b/messages/bun.lock
new file mode 100644
index 0000000..7c42a58
--- /dev/null
+++ b/messages/bun.lock
@@ -0,0 +1,468 @@
+{
+  "lockfileVersion": 1,
+  "workspaces": {
+    "": {
+      "name": "KeyNotes.App",
+      "dependencies": {
+        "connect-sqlite3": "^0.9.15",
+        "ejs": "^3.1.10",
+        "express": "^4.21.2",
+        "express-session": "^1.18.1",
+        "express-validator": "^7.2.1",
+      },
+    },
+  },
+  "packages": {
+    "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="],
+
+    "@npmcli/fs": ["@npmcli/fs@1.1.1", "", { "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" } }, "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ=="],
+
+    "@npmcli/move-file": ["@npmcli/move-file@1.1.2", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg=="],
+
+    "@tootallnate/once": ["@tootallnate/once@1.1.2", "", {}, "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="],
+
+    "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="],
+
+    "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
+
+    "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
+
+    "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
+    "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
+
+    "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+    "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+    "aproba": ["aproba@2.0.0", "", {}, "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="],
+
+    "are-we-there-yet": ["are-we-there-yet@3.0.1", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" } }, "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg=="],
+
+    "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="],
+
+    "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
+
+    "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+    "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+
+    "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
+
+    "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
+
+    "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="],
+
+    "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
+
+    "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
+
+    "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
+
+    "cacache": ["cacache@15.3.0", "", { "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "glob": "^7.1.4", "infer-owner": "^1.0.4", "lru-cache": "^6.0.0", "minipass": "^3.1.1", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.2", "mkdirp": "^1.0.3", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^8.0.1", "tar": "^6.0.2", "unique-filename": "^1.1.1" } }, "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ=="],
+
+    "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g=="],
+
+    "call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="],
+
+    "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+    "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
+
+    "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
+
+    "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+    "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+    "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="],
+
+    "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+    "connect-sqlite3": ["connect-sqlite3@0.9.15", "", { "dependencies": { "sqlite3": "^5.0.2" } }, "sha512-aJGDtASX8DTUZ++7iTN97vR0vGFpm8jDFew/qHK3veISkCpVpPS0tMdqs7i9fiHLaqaU0Jh3c4sUvNxsizaSTA=="],
+
+    "console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="],
+
+    "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
+
+    "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
+
+    "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
+
+    "cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="],
+
+    "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+
+    "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
+
+    "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
+
+    "delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="],
+
+    "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
+
+    "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
+
+    "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
+
+    "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+    "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
+
+    "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
+
+    "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+    "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
+
+    "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="],
+
+    "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
+
+    "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
+
+    "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="],
+
+    "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+    "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+    "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+    "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
+
+    "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
+
+    "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
+
+    "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
+
+    "express-session": ["express-session@1.18.1", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" } }, "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA=="],
+
+    "express-validator": ["express-validator@7.2.1", "", { "dependencies": { "lodash": "^4.17.21", "validator": "~13.12.0" } }, "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ=="],
+
+    "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
+
+    "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
+
+    "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="],
+
+    "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
+
+    "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
+
+    "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
+
+    "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
+
+    "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
+
+    "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+    "gauge": ["gauge@4.0.4", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg=="],
+
+    "get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="],
+
+    "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+    "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
+
+    "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+
+    "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+    "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+    "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+    "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+    "has-unicode": ["has-unicode@2.0.1", "", {}, "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="],
+
+    "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+    "http-cache-semantics": ["http-cache-semantics@4.1.1", "", {}, "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="],
+
+    "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
+
+    "http-proxy-agent": ["http-proxy-agent@4.0.1", "", { "dependencies": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" } }, "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg=="],
+
+    "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
+
+    "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
+    "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
+
+    "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+
+    "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+
+    "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
+
+    "infer-owner": ["infer-owner@1.0.4", "", {}, "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="],
+
+    "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
+
+    "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
+
+    "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
+
+    "ip-address": ["ip-address@9.0.5", "", { "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" } }, "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g=="],
+
+    "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
+
+    "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+    "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="],
+
+    "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+    "jake": ["jake@10.9.2", "", { "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", "filelist": "^1.0.4", "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" } }, "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA=="],
+
+    "jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="],
+
+    "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+    "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
+
+    "make-fetch-happen": ["make-fetch-happen@9.1.0", "", { "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^6.0.0", "minipass": "^3.1.3", "minipass-collect": "^1.0.2", "minipass-fetch": "^1.3.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.2", "promise-retry": "^2.0.1", "socks-proxy-agent": "^6.0.0", "ssri": "^8.0.0" } }, "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg=="],
+
+    "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+    "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
+
+    "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
+
+    "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
+
+    "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
+
+    "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+    "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+    "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
+
+    "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+    "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
+
+    "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
+
+    "minipass-collect": ["minipass-collect@1.0.2", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA=="],
+
+    "minipass-fetch": ["minipass-fetch@1.4.1", "", { "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", "minizlib": "^2.0.0" }, "optionalDependencies": { "encoding": "^0.1.12" } }, "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw=="],
+
+    "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="],
+
+    "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
+
+    "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="],
+
+    "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
+
+    "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
+
+    "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
+
+    "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
+
+    "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
+
+    "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
+
+    "node-abi": ["node-abi@3.74.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w=="],
+
+    "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
+
+    "node-gyp": ["node-gyp@8.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^9.1.0", "nopt": "^5.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w=="],
+
+    "nopt": ["nopt@5.0.0", "", { "dependencies": { "abbrev": "1" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ=="],
+
+    "npmlog": ["npmlog@6.0.2", "", { "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg=="],
+
+    "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
+
+    "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
+
+    "on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="],
+
+    "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
+
+    "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
+
+    "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
+
+    "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
+
+    "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
+
+    "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
+
+    "promise-inflight": ["promise-inflight@1.0.1", "", {}, "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="],
+
+    "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="],
+
+    "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
+
+    "pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="],
+
+    "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
+
+    "random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="],
+
+    "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
+
+    "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
+
+    "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
+
+    "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
+
+    "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
+
+    "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
+
+    "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
+    "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
+
+    "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+
+    "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
+
+    "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="],
+
+    "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
+
+    "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
+
+    "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
+
+    "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
+
+    "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
+
+    "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
+
+    "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
+
+    "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
+
+    "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
+
+    "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
+
+    "socks": ["socks@2.8.4", "", { "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ=="],
+
+    "socks-proxy-agent": ["socks-proxy-agent@6.2.1", "", { "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" } }, "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ=="],
+
+    "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
+
+    "sqlite3": ["sqlite3@5.1.7", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^7.0.0", "prebuild-install": "^7.1.1", "tar": "^6.1.11" }, "optionalDependencies": { "node-gyp": "8.x" } }, "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog=="],
+
+    "ssri": ["ssri@8.0.1", "", { "dependencies": { "minipass": "^3.1.1" } }, "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ=="],
+
+    "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
+
+    "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+    "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
+    "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+    "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
+
+    "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+    "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
+
+    "tar-fs": ["tar-fs@2.1.2", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA=="],
+
+    "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
+
+    "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
+
+    "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
+
+    "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
+
+    "uid-safe": ["uid-safe@2.1.5", "", { "dependencies": { "random-bytes": "~1.0.0" } }, "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA=="],
+
+    "unique-filename": ["unique-filename@1.1.1", "", { "dependencies": { "unique-slug": "^2.0.0" } }, "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ=="],
+
+    "unique-slug": ["unique-slug@2.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w=="],
+
+    "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
+
+    "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
+
+    "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
+
+    "validator": ["validator@13.12.0", "", {}, "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg=="],
+
+    "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
+
+    "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+    "wide-align": ["wide-align@1.1.5", "", { "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg=="],
+
+    "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
+
+    "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
+
+    "agent-base/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+    "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
+
+    "express-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
+
+    "express-session/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
+
+    "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
+
+    "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+    "https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+    "humanize-ms/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+    "make-fetch-happen/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
+
+    "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+    "socks-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+
+    "ssri/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
+
+    "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
+
+    "agent-base/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+    "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
+
+    "http-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+    "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+    "socks-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+  }
+}
diff --git a/messages/package.json b/messages/package.json
new file mode 100644
index 0000000..3e58dd0
--- /dev/null
+++ b/messages/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "KeyNotes.App",
+  "type": "module",
+  "dependencies": {
+    "connect-sqlite3": "^0.9.15",
+    "ejs": "^3.1.10",
+    "express": "^4.21.2",
+    "express-session": "^1.18.1",
+    "express-validator": "^7.2.1"
+  }
+}
diff --git a/messages/public/css/style.css b/messages/public/css/style.css
new file mode 100644
index 0000000..4e11935
--- /dev/null
+++ b/messages/public/css/style.css
@@ -0,0 +1,428 @@
+:root {
+  --font-size: 18px;
+  --desktop-heading-font-size: 5em;
+  --desktop-line-height: 1;
+  --mobile-heading-font-size: 4em;
+  --mobile-line-height: 0.8;
+  --border-radius-sm: 0.5em;
+  --border-radius-md: 0.7em;
+  --border-radius-lg: 1em;
+
+  /* colors */
+
+  --100-blue: hsl(219, 64%, 96%, 100%);
+  --200-blue: hsl(222, 81%, 94%, 100%);
+  --300-blue: hsl(221, 86%, 92%, 100%);
+  --400-blue: hsl(222, 92%, 90%, 100%);
+  --500-blue: hsl(222, 97%, 88%, 100%);
+  --600-blue: hsl(222, 97%, 85%, 100%);
+  --700-blue: hsl(222, 100%, 84%, 100%);
+  --800-blue: hsl(222, 100%, 34%, 100%);
+}
+/* 1. Use a more-intuitive box-sizing model */
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+/* 2. Remove default margin */
+* {
+  margin: 0;
+}
+
+body {
+  /* 3. Add accessible line-height */
+  line-height: 1.5;
+  /* 4. Improve text rendering */
+  -webkit-font-smoothing: antialiased;
+  font-family: "Lato, serif";
+  background: var(--100-blue);
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+}
+
+/* 5. Improve media defaults */
+img,
+picture,
+video,
+canvas,
+svg {
+  display: block;
+  max-width: 100%;
+}
+
+/* 6. Inherit fonts for form controls */
+input,
+button,
+textarea,
+select {
+  font: inherit;
+}
+
+/* 7. Avoid text overflows */
+p,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  overflow-wrap: break-word;
+}
+
+/* 8. Improve line wrapping */
+p {
+  text-wrap: pretty;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  text-wrap: balance;
+}
+
+a {
+  color: var(--800-blue);
+  text-decoration: none;
+}
+
+/*
+  9. Create a root stacking context
+*/
+#root,
+#__next {
+  isolation: isolate;
+}
+
+nav {
+  font-size: 1.5rem;
+  padding: 1em 0;
+}
+
+nav > ul {
+  list-style: none;
+  text-align: center;
+  margin: 0 auto;
+}
+
+ul {
+  padding: 0;
+  margin: auto;
+  justify-content: space-around;
+  max-width: 80vw;
+}
+
+ul > li {
+  display: inline-block;
+  text-align: center;
+}
+
+li a {
+  color: black;
+}
+
+form {
+  width: 100%;
+  max-width: 50vw;
+}
+
+input {
+  padding: 0.75em;
+}
+
+footer {
+  margin-top: auto;
+  padding: 2em;
+}
+
+/*.full-page {*/
+/*  height: 90vh;*/
+/*}*/
+
+.divider {
+  height: 0.25em;
+  background: var(--300-blue);
+  width: 80vw;
+  margin: 0 auto 1em;
+}
+
+.flex {
+  display: flex;
+}
+
+.col {
+  flex-direction: column;
+}
+
+.container.flex.hero {
+  align-items: center;
+  justify-content: center;
+  min-height: 60vh;
+}
+
+.container.hero {
+  text-align: center;
+}
+
+.container.hero * {
+  margin: 0.5em 0;
+}
+
+.container.hero p {
+  font-size: 1.2rem;
+  padding: 1em;
+}
+
+.center-horizontal {
+  text-align: center;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 1em;
+  margin: 0 auto;
+}
+
+.hero__heading {
+  margin: 0;
+  font-size: var(--mobile-heading-font-size);
+  font-family: "Rokkitt", serif;
+  line-height: var(--desktop-line-height);
+}
+
+.btn {
+  display: inline-block;
+  width: 100%;
+  padding: 1em;
+  text-decoration: none;
+  border-radius: 0.7em;
+  font-size: 1em;
+}
+
+.btn__link {
+  text-align: center;
+  background: #f0f0f0;
+  line-height: 1.5em;
+  max-width: 200px;
+}
+
+.btn__link:hover {
+  background: #bfbba9;
+}
+
+.btn__form {
+  background: #f0f0f0;
+}
+
+.btn__form:hover {
+  background: #bfbba9;
+}
+
+.hero > .btn__link {
+  margin-top: 2em;
+}
+
+#main {
+  padding: 0 50px;
+}
+
+#main > .container.flex {
+  justify-content: center;
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.feed-heading {
+  margin-left: 1em;
+  text-transform: capitalize;
+  text-align: center;
+}
+
+.feed-heading span {
+  text-transform: initial;
+}
+
+.author__meta {
+  gap: 0.5em;
+}
+
+.grid {
+  display: grid;
+  /*grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));*/
+  gap: 1em;
+  padding: 2em;
+  grid-auto-flow: dense;
+  margin: 0 auto;
+}
+
+.grid-item {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  gap: 2em;
+  position: relative;
+}
+
+.grid-item.card {
+  border: 1px solid black;
+  border-radius: 0.5em;
+  text-align: center;
+  background: var(--500-blue);
+}
+
+.grid-item > p {
+  padding: 5em;
+}
+
+.grid__card__metadata {
+  position: absolute;
+  bottom: 0;
+  display: flex;
+  font-size: 0.8em;
+  justify-content: space-between;
+  width: 100%;
+  padding: 0 0.9em 0.7em;
+}
+
+.likes {
+  display: flex;
+  align-self: center;
+  width: auto;
+  align-items: center;
+  justify-content: center;
+  gap: 0.5em;
+}
+
+.card__likes {
+  padding: 0;
+  font-weight: 400;
+}
+
+.timestamp {
+  font-weight: 300;
+}
+
+.card .card__message {
+  margin-bottom: 1em;
+}
+
+.form-item {
+  flex-direction: column;
+  width: 100%;
+  margin: 0.5em 0;
+}
+
+.form-item.flex label {
+  text-align: left;
+  padding-left: 0.2em;
+}
+
+.form-item-row {
+  flex-direction: column;
+  width: 100%;
+  margin: 0.5em 0;
+  gap: 0.5em;
+}
+
+.hover__underline {
+  /*display: inline-block;*/
+  position: relative;
+}
+
+.red {
+  color: red;
+}
+
+.hover__underline::after {
+  content: "";
+  position: absolute;
+  width: 100%;
+  transform: scaleX(0);
+  height: 2px;
+  bottom: 0;
+  left: 0;
+  background-color: var(--700-blue);
+  transform-origin: bottom left;
+  transition: transform 0.25s ease-out;
+}
+
+.hover__underline:hover::after {
+  transform: scaleX(1);
+  transform-origin: bottom left;
+}
+
+.hidden {
+  display: none;
+}
+
+.social-icons {
+  gap: 0.5em;
+  justify-content: baseline;
+}
+
+.social-icons a {
+  font-size: 1.2em;
+  color: var(--800-blue);
+}
+
+@media only screen and (min-width: 750px) {
+  /*ul {*/
+  /*  max-width: 40vw;*/
+  /*}*/
+  .hero__heading {
+    margin: 0;
+    font-size: var(--desktop-heading-font-size);
+    font-family: "Rokkitt", serif;
+    line-height: var(--desktop-line-height);
+  }
+  .container.hero p {
+    font-size: 1.5rem;
+    padding: 0.5em;
+  }
+  .container.flex * {
+    flex: 1;
+  }
+  #main {
+    padding: 0 100px;
+  }
+
+  #main > .container.flex {
+    gap: 10em;
+  }
+
+  .grid {
+    width: 100%;
+    max-width: 750px;
+  }
+  .grid__span_two {
+    grid-column-end: span 2;
+  }
+  .form-item-row {
+    flex-direction: row;
+    width: 100%;
+    margin: 0.5em 0;
+    gap: 0.5em;
+  }
+
+  .nav__profile {
+    position: relative;
+  }
+
+  .nav__hover {
+    position: absolute;
+    background: var(--100-blue);
+    border: 2px solid black;
+    width: 100%;
+    /*max-width: 150px;*/
+    border-radius: var(--border-radius-sm);
+    z-index: 1;
+  }
+
+  /*.form-item > .btn {*/
+  /*  max-width: 200px;*/
+  /*}*/
+}
diff --git a/messages/public/images/favicon.png b/messages/public/images/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..e98cbc1301b7cdd2951d28cf99b0eca7570b4046
GIT binary patch
literal 1240
zcmV;}1Sk86P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F8000D^Nkl<ZSV!$w
zeN0<b6#u=J;$TV{jPbFy(6W{?O8FAl*FXEi1jLMpQ|H`<F>JE97@cM&IB_%nY21c{
zL^m}tK{It*9GROgPP6GKu)_GV;L66=fPp~i`bA&c=Y4PMT|t+u0$WUWlcx8)^X|F7
z^SkGqcP@i~q19@m5xVFVFrp%GNF%BUh76A5K>h+>4SLQ2UShMy$N6e)<2ODW8X9u&
zMyOS_F1ti5Vh)piqIiW=Dn;ayB@m0n!e%0oDCkZ73kZH#r=~DAHYz+K4jH*=`}*$g
z?wbMtO?p-#*`SUHVx=k>Wjo3tS19J}bs%D%#V(TxHPwe;nVbaY;|_GSH}4hzX!SW=
z#Pbw>UR7O#goNe)+1r0xNNC5jn-x?&W;6D^TR~XBcvqWo8KczcVjmHO4)bTtzi{NU
z&moh^<`n>M%;WLEVzrSl0N&eMfvY`cI6ba-W`$lalQ5D2|Bf*+F;HtX0{wFhJl<w_
zSQzs1^RagAI@laexTf8xI`|Qq8Vq1LR?ej5<S2X|^v!m8J|aKRW<fzAc9idg-RZ)|
zhYq08Q11t@jsS4}1Hc<^?Lm@K1&70d6LqJkt?bMUq^4-7Tzm9eI@fB{$jQp|M>@VH
zx<5@FnEdNj^jGfN4|PiFy#c6v{{yJi8rbZ1eD%#Sy2+~C$D!A)r1Ga<d`ai&D^syL
zKQ{o}cf$I1qXAPB<JeIAG@jk`!u<tcCk&2_456#783j)k;l-_`_XePB=PptU5{2mP
z>!&e_=%^?}MoOud=Bw9eHIqssurxZF>UzmKFIhi&V+3bT9uE#cM&24kL`ESzJRDn|
z-$;$tH#VWY(?sR%FKwmS($@J042_qlZu4``;L*j4VZL$&`zzmh2p~8HcD?>45|fm0
zyWMDMZKt#<<TAv^#ZuYOWTY_=7rP9J3OUs^x3+;LC3u{q;V(a)3J$=0r6HdMR?7rh
z8!!IP0Psh^SHfv>1kIOQDXoceITGUIW@Lq5h>wj$Qlf(Dj4iEjvu=!&KH%4%eh7|%
zhiUl087M1%RVc+QDW&~`*Xg-NlRhz$lwAMlxz<ZM%Ui9bG~zKVC8gRw`UVE!@p>^v
zO7ZFMj|CS4_bm-|xw&$$+t*KF-<~WqfQI^u9Ndl==57G?RP2Q+c?FzKC(fKbNAc;>
zQlU;(QTfElJIzY6N;#ufIDPhKxJU<NA-zDug>y5GprO9Lg*2tvcLlJJL0D5=W%>R5
z`6s$NS}r{h05@&cE$j2LYIzzNy*^*YGM+mf$8B;Kgn>fR5p64lhxM&qm7z8W0Fu(u
z;=)1}4bKk3&8076xl9h9kAu_YqUunI1QJOImHCg2|Ar?C2|;M6gz6mR{Q2&BWav7M
zAFV}U(Nm-)+t!q=Rps+nP5}V<iM9G{{$dgn2x307V0fSpW>Y(gOE&(tp}1tzn#>eh
zScD3aqRV=X5itk-w<G@9d;yrUTG3*-0EgXXvQLcW3=R%D10AKeV@-PIb}=J*oA9C}
zKtyu|z_KhpIeY{imyI@?)l%p3x_9?>c8<(G>c0VFp08n7nLcX(0000<MNUMnLSTYY
CEmEBT

literal 0
HcmV?d00001

diff --git a/messages/public/js/script.js b/messages/public/js/script.js
new file mode 100644
index 0000000..12f5e24
--- /dev/null
+++ b/messages/public/js/script.js
@@ -0,0 +1,109 @@
+function usernameValidation(username) {
+  const errors = [];
+  if (username.length < 5) {
+    errors.push("*username must be at least 8 characters");
+  }
+
+  if (username.includes(" ")) {
+    errors.push("*username must not contain any spaces");
+  }
+
+  return errors;
+}
+
+function checkStringForNumber(str) {
+  for (i = 0; i < str.length; i++) {
+    if (!isNaN(str[i])) return true;
+  }
+  return false;
+}
+
+function checkStringForChar(str) {
+  return /[!#%^,\.]/.test(str);
+}
+
+function passwordValidation(password) {
+  const errors = [];
+
+  if (password.length < 8) {
+    errors.push("*password must be at least 8 characters");
+  }
+
+  if (!checkStringForNumber(password)) {
+    errors.push("*password must include a number.");
+  }
+  if (!checkStringForChar(password)) {
+    errors.push("*password must include a special character. !#%^,.");
+  }
+
+  return errors;
+}
+
+function formValidator() {
+  const username = document.querySelector('input[name="username"]');
+  const password = document.querySelector('input[name="password"]');
+  const passwordConfirmation = document.querySelector(
+    'input[name="password-confirmation"]',
+  );
+
+  if (username && password && passwordConfirmation) {
+    username.addEventListener("keyup", () => {
+      let errors = usernameValidation(username.value);
+      let errorDiv = username.nextElementSibling;
+      errorDiv.textContent = "";
+
+      if (errors.length > 0) {
+        errors.forEach((err) => {
+          const p = document.createElement("p");
+          p.textContent = err;
+          errorDiv.appendChild(p);
+        });
+      } else {
+        errorDiv.textContent = "";
+      }
+    });
+
+    password.addEventListener("keyup", (e) => {
+      let errorDiv = password.nextElementSibling;
+      errorDiv.textContent = "";
+      let errors = passwordValidation(e.target.value);
+
+      if (errors.length > 0) {
+        errors.forEach((err) => {
+          const p = document.createElement("p");
+          p.textContent = err;
+          errorDiv.appendChild(p);
+        });
+      } else {
+        errorDiv.textContent = "";
+      }
+    });
+
+    passwordConfirmation.addEventListener("keyup", (e) => {
+      let error = "*passwords do not match!";
+      let errorDiv = passwordConfirmation.nextSibling;
+
+      if (password.value !== e.target.value) {
+        errorDiv.textContent = error;
+      } else {
+        errorDiv.textContent = "";
+      }
+    });
+  }
+}
+
+function navProfileHover() {
+  const profile = document.querySelector(".nav__profile");
+  if (profile) {
+    const profileHover = document.querySelector(".nav__hover");
+    profile.addEventListener("mouseleave", () => {
+      profileHover.classList.add("hidden");
+    });
+    profile.addEventListener("mouseenter", () => {
+      profileHover.classList.remove("hidden");
+    });
+  }
+}
+
+navProfileHover();
+formValidator();
diff --git a/messages/src/app.js b/messages/src/app.js
new file mode 100644
index 0000000..31a5a15
--- /dev/null
+++ b/messages/src/app.js
@@ -0,0 +1,53 @@
+const fs = require("node:fs");
+const { logging } = require("./middlewares/logging");
+const path = require("node:path");
+const express = require("express");
+const passport = require("passport");
+const session = require("express-session");
+const SQLiteStore = require("connect-sqlite3")(session);
+
+const { indexRouter } = require("./routes/indexRouter");
+const { authRouter } = require("./routes/authRouter");
+
+const app = express();
+
+const assetsPath = path.join(path.dirname(__dirname), "public");
+app.use(express.static(assetsPath));
+
+app.set("views", path.join(__dirname, "views"));
+app.set("view engine", "ejs");
+
+app.use(
+  session({
+    store: new SQLiteStore({ dir: "./src/db/", db: "keynotes.db" }),
+    secret: "keynotes.app",
+    cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
+    saveUninitialized: false,
+    resave: false,
+  }),
+);
+
+app.use(passport.session());
+app.use(express.urlencoded({ extended: false }));
+app.use(logging);
+app.use((req, res, next) => {
+  res.locals.currentUser = req.user;
+  next();
+});
+
+app.use("/", indexRouter);
+app.use("/auth", authRouter);
+
+const server = app.listen(5173, () => console.log(`App running on port: 5173`));
+
+const gracefulShutdownHandler = (signal) => {
+  console.log(`\n${signal} received.`);
+
+  server.close(() => {
+    console.log("Shutting down the server process...");
+    process.exit();
+  });
+};
+
+process.on("SIGINT", gracefulShutdownHandler);
+process.on("SIGTERM", gracefulShutdownHandler);
diff --git a/messages/src/config.js b/messages/src/config.js
new file mode 100644
index 0000000..e69de29
diff --git a/messages/src/controllers/authController.js b/messages/src/controllers/authController.js
new file mode 100644
index 0000000..19d7a62
--- /dev/null
+++ b/messages/src/controllers/authController.js
@@ -0,0 +1,46 @@
+const bcryptjs = require("bcryptjs");
+const db = require("../models/query");
+const { validationResult } = require("express-validator");
+
+function loginGet(req, res, next) {
+  res.render("login", {
+    pageTitle: "InspiredCliches | Login",
+    errors: req.session.messages,
+  });
+}
+
+function logOut(req, res, next) {
+  req.logout((err) => {
+    if (err) return next(err);
+  });
+  res.redirect("/");
+}
+
+function signUpGet(req, res, next) {
+  res.render("register", {
+    pageTitle: "InspiredCliches | Register",
+    errors: null,
+  });
+}
+
+async function signUpPost(req, res, next) {
+  const result = validationResult(req);
+  if (result.isEmpty()) {
+    const { username, password } = req.body;
+    const hashedPassword = await bcryptjs.hash(password, 10);
+    db.insertUser(username, hashedPassword);
+    res.redirect("/auth/login");
+  } else {
+    res.status(400).render("register", {
+      errors: result.array(),
+      pageTitle: "InspiredCliches | Register",
+    });
+  }
+}
+
+module.exports = {
+  loginGet,
+  logOut,
+  signUpPost,
+  signUpGet,
+};
diff --git a/messages/src/controllers/indexController.js b/messages/src/controllers/indexController.js
new file mode 100644
index 0000000..2314b6a
--- /dev/null
+++ b/messages/src/controllers/indexController.js
@@ -0,0 +1,90 @@
+const db = require("../models/query");
+
+function indexGet(req, res, next) {
+  if (res.locals.currentUser) {
+    const keynotes = db.getEveryNote();
+    const userLikedPosts = db.getLikesByUser(res.locals.currentUser.user_id);
+
+    res.render("feed", {
+      pageTitle: "InspiredCliches | Feed",
+      keynotes: keynotes,
+      userLikedPosts: userLikedPosts,
+    });
+  }
+  res.render("home", { pageTitle: "InspiredCliches" });
+}
+
+function addLike(req, res, next) {
+  const { noteId } = req.query;
+
+  const noteExits = noteId ? db.getNoteById(noteId) : null;
+
+  if (res.locals.currentUser && noteExits) {
+    const userId = res.locals.currentUser.user_id;
+    if (db.checkIfNotedLiked(noteId, userId)) {
+      db.deleteLike(noteId, userId);
+    } else {
+      db.insertLike(Number(noteId), userId);
+    }
+  }
+  res.redirect("/");
+}
+
+function addNotePost(req, res, next) {
+  const { message, media } = req.body;
+
+  if (res.locals.currentUser) {
+    const userId = res.locals.currentUser.user_id;
+
+    db.putNewNote({ message: message, media: null, userId: userId });
+  }
+
+  res.redirect("/");
+}
+
+function deleteNote(req, res) {
+  const { messageId, userId } = req.query;
+
+  if (res.locals.currentUser) {
+    const message = db.getNoteById(messageId);
+
+    if (message.user_id === Number(userId)) {
+      db.deleteNote(Number(messageId));
+    }
+    res.redirect("/");
+  }
+}
+
+function addNoteGet(req, res, next) {
+  res.render("note-form", { pageTitle: "InspiredCliches | New Note" });
+}
+
+function getProfile(req, res, next) {
+  const { userId } = req.query;
+
+  const userExists = userId ? db.getUserById(userId) : null;
+
+  if (res.locals.currentUser) {
+    if (userExists) {
+      const sumUserPosts = db.getSumNotesByUserId(userId);
+      const allPosts = db.getAllNotesByUserId(userId);
+      res.render("view-profile", {
+        pageTitle: "InspiredCliches | Edit Profile",
+        user: userExists,
+        totalPosts: sumUserPosts,
+        allPosts: allPosts,
+      });
+    } else {
+      res.redirect("/");
+    }
+  }
+}
+
+module.exports = {
+  indexGet,
+  addNoteGet,
+  addNotePost,
+  deleteNote,
+  addLike,
+  getProfile,
+};
diff --git a/messages/src/middlewares/auth.js b/messages/src/middlewares/auth.js
new file mode 100644
index 0000000..d62e7fd
--- /dev/null
+++ b/messages/src/middlewares/auth.js
@@ -0,0 +1,50 @@
+const bcryptjs = require("bcryptjs");
+const db = require("../models/db");
+const LocalStrategy = require("passport-local");
+const passport = require("passport");
+
+passport.use(
+  new LocalStrategy(
+    { passReqToCallback: true },
+    async (req, username, password, done) => {
+      try {
+        req.session.messages = [];
+        const query = db.query(`SELECT * FROM users WHERE username = $1`);
+        const user = query.get({ $1: username });
+
+        if (!user) {
+          return done(null, false, {
+            message: "Incorrect username or password.",
+          });
+        }
+
+        const match = await bcryptjs.compare(password, user.password);
+
+        if (!match) {
+          return done(null, false, {
+            message: "Incorrect username or password.",
+          });
+        }
+        return done(null, user);
+      } catch (err) {
+        return done(err);
+      }
+    },
+  ),
+);
+
+passport.serializeUser((user, done) => {
+  done(null, user.user_id);
+});
+
+passport.deserializeUser(async (id, done) => {
+  try {
+    const query = db.query("SELECT * FROM users WHERE user_id = $userId");
+    const results = query.get({ $userId: id });
+    done(null, results);
+  } catch (err) {
+    done(err);
+  }
+});
+
+module.exports = passport;
diff --git a/messages/src/middlewares/logging.js b/messages/src/middlewares/logging.js
new file mode 100644
index 0000000..ab8863d
--- /dev/null
+++ b/messages/src/middlewares/logging.js
@@ -0,0 +1,8 @@
+function logging(req, res, next) {
+  req.time = new Date(Date.now()).toISOString();
+  const clientIp = req.header("cf-connecting-ip") || req.socket.remoteAddress;
+  console.log(req.time, req.method, req.hostname, req.path, clientIp);
+  next();
+}
+
+module.exports = { logging };
diff --git a/messages/src/models/db.js b/messages/src/models/db.js
new file mode 100644
index 0000000..0394ecd
--- /dev/null
+++ b/messages/src/models/db.js
@@ -0,0 +1,35 @@
+const { Database } = require("bun:sqlite");
+const fs = require("node:fs");
+const path = require("node:path");
+const { mkdir } = require("node:fs/promises");
+const { stat } = require("node:fs");
+
+const dbDirPath = path.join(path.dirname(__dirname), "/db");
+const dbPath = path.join(dbDirPath, "/keynotes.db");
+
+async function makeDirectory(path) {
+  const dirCreation = await mkdir(path);
+  return dirCreation;
+}
+
+// Make sure DB path exists
+stat(dbDirPath, (err, stats) => {
+  if (err !== null) {
+    makeDirectory(dbDirPath);
+  }
+});
+
+const db = new Database(dbPath);
+// Enable WAL Mode
+db.exec("PRAGMA journal_mode = WAL;");
+
+const initSQL = path.join(__dirname, "./init.sql");
+fs.readFile(initSQL, "utf8", (err, data) => {
+  if (err) {
+    console.error(err);
+    return;
+  }
+  db.exec(data);
+});
+
+module.exports = db;
diff --git a/messages/src/models/init.sql b/messages/src/models/init.sql
new file mode 100644
index 0000000..5e2c3a9
--- /dev/null
+++ b/messages/src/models/init.sql
@@ -0,0 +1,73 @@
+CREATE TABLE IF NOT EXISTS users (
+	user_id INTEGER PRIMARY KEY ASC,
+	username VARCHAR(50) UNIQUE NOT NULL,
+	password TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS user_profile_info (
+	user_info_id INTEGER PRIMARY KEY ASC,
+	email VARCHAR(50),
+	user_id INTEGER REFERENCES users(user_id)
+);
+
+CREATE TABLE IF NOT EXISTS messages (
+	message_id INTEGER PRIMARY KEY ASC,
+	message TEXT,
+	media VARCHAR(100),
+	date NUMBER,
+	user_id INTEGER REFERENCES users(user_id)
+);
+
+CREATE TABLE IF NOT EXISTS likes (
+	likes_id INTEGER PRIMARY KEY ASC,
+	message_id INTEGER REFERENCES messages(message_id),
+	user_id INTEGER REFERENCES users(user_id),
+	liked_at NUMBER
+);
+
+
+
+INSERT OR IGNORE INTO users (username, password)
+VALUES ("jim", "$2a$10$Bmjre5WSpSSAi.nWBfLZFOlhQhbIAoY/MM7ikJz3Ho9tqeXCExaB6"),
+	("demo", "$2a$10$Vo.XmsVQVx9gGojRIdewpOap5SnhrgZ21/Im5IxhH3PC4FycM5uwC");
+
+INSERT OR IGNORE INTO messages (message_id, message, media, date, user_id)
+VALUES
+  (1, "When life gives you lemons, make lemonade.", NULL, strftime('%s', 'now')*1000, 1),
+  (2, "Rome wasn’t built in a day – take your time.", NULL, strftime('%s', 'now')*1000, 1),
+  (3, "The grass is always greener on the other side.", NULL, strftime('%s', 'now')*1000, 1),
+  (4, "If it ain’t broke, don’t fix it.", NULL, strftime('%s', 'now')*1000, 1),
+  (5, "You can’t have your cake and eat it too.", NULL, strftime('%s', 'now')*1000, 1),
+  (6, "Better late than never.", NULL, strftime('%s', 'now')*1000, 1),
+  (7, "What doesn’t kill you makes you stronger.", NULL, strftime('%s', 'now')*1000, 1),
+  (8, "Every cloud has a silver lining.", NULL, strftime('%s', 'now')*1000, 1),
+  (9, "Don’t bite the hand that feeds you.", NULL, strftime('%s', 'now')*1000, 1),
+  (10, "Absence makes the heart grow fonder.", NULL, strftime('%s', 'now')*1000, 1),
+  (11, "Always trust your gut feeling – it’s usually right.", NULL, strftime('%s', 'now')*1000, 2),
+  (12, "Money can’t buy happiness, but it sure helps.", NULL, strftime('%s', 'now')*1000, 2),
+  (13, "Everything happens for a reason.", NULL, strftime('%s', 'now')*1000, 2),
+  (14, "Don’t go to bed angry – resolve conflicts before sleeping.", NULL, strftime('%s', 'now')*1000, 2),
+  (15, "The early bird catches the worm.", NULL, strftime('%s', 'now')*1000, 2),
+  (16, "You only live once – make the most of it.", NULL, strftime('%s', 'now')*1000, 2),
+  (17, "Hard work pays off in the end.", NULL, strftime('%s', 'now')*1000, 2),
+  (18, "Family is everything – cherish them.", NULL, strftime('%s', 'now')*1000, 2),
+  (19, "Don’t put off until tomorrow what you can do today.", NULL, strftime('%s', 'now')*1000, 2),
+  (20, "A smile costs nothing but gives so much.", NULL, strftime('%s', 'now')*1000, 2),
+  (21, "You can’t please everyone – focus on yourself.", NULL, strftime('%s', 'now')*1000, 2),
+  (22, "Health is wealth – take care of your body.", NULL, strftime('%s', 'now')*1000, 2),
+  (23, "Patience is a virtue – good things take time.", NULL, strftime('%s', 'now')*1000, 2),
+  (24, "Don’t judge a book by its cover.", NULL, strftime('%s', 'now')*1000, 2),
+  (25, "Actions speak louder than words.", NULL, strftime('%s', 'now')*1000, 2),
+  (26, "Learn from your mistakes – they make you wiser.", NULL, strftime('%s', 'now')*1000, 2),
+  (27, "Kindness costs nothing but means everything.", NULL, strftime('%s', 'now')*1000, 2),
+  (28, "Life is short – eat the cake.", NULL, strftime('%s', 'now')*1000, 2),
+  (29, "Comparison is the thief of joy – focus on your journey.", NULL, strftime('%s', 'now')*1000, 2),
+  (30, "The best things in life are free.", NULL, strftime('%s', 'now')*1000, 2);
+
+-- ALTER TABLE likes
+-- ADD COLUMN user_id INTEGER REFERENCES users(user_id);
+
+-- ALTER TABLE likes 
+-- ADD COLUMN liked_at NUMBER;
+
+-- ALTER TABLE likes DROP COLUMN sum_likes;
diff --git a/messages/src/models/query.js b/messages/src/models/query.js
new file mode 100644
index 0000000..6ec5f50
--- /dev/null
+++ b/messages/src/models/query.js
@@ -0,0 +1,138 @@
+const db = require("./db");
+
+function getEveryNote() {
+  try {
+    const query = db.query(
+      `SELECT messages.message_id, message, media, date, users.username AS user, messages.user_id,  COUNT(likes.likes_id) AS likes_count 
+      FROM messages LEFT JOIN users ON messages.user_id = users.user_id 
+      LEFT JOIN likes ON messages.message_id = likes.message_id
+      GROUP BY messages.message_id, message, media, date, users.username
+      ORDER BY date DESC
+      `,
+    );
+
+    const data = query.all();
+    return data;
+  } catch (err) {
+    return err;
+  }
+}
+
+function putNewNote(note) {
+  const { message, media, userId } = note;
+  const date = Date.now();
+  const query = db.query(
+    `INSERT INTO messages (message, media, user_id, date)  VALUES ($1, $2, $3, $4)`,
+  );
+  query.run({ $1: message, $2: media, $3: userId, $4: date });
+}
+
+function deleteNote(messageId) {
+  try {
+    const query = db.query(`DELETE FROM likes WHERE message_id = $1`);
+    query.run({ $1: messageId });
+    const queryTwo = db.query(`DELETE FROM messages WHERE message_id = $1`);
+    queryTwo.run({ $1: messageId });
+  } catch (e) {
+    console.log(e);
+  }
+}
+
+function getLikesByUser(userId) {
+  const query = db.query(`SELECT message_id FROM likes WHERE user_id = $1`);
+
+  return query.all({ $1: userId });
+}
+
+function getTotalLikesByMessageId(messageId) {
+  const query = db.query(`SELECT COUNT(1) FROM likes WHERE message_id = $1`);
+  return query.get({ $1: messageId });
+}
+
+function getNoteById(noteId) {
+  const query = db.query(
+    `SELECT message, user_id from messages WHERE message_id = $1`,
+  );
+  return query.get({ $1: noteId });
+}
+
+function checkIfNotedLiked(messageId, userId) {
+  const query = db.query(
+    `SELECT * FROM likes WHERE message_id = $1 and user_id = $2`,
+  );
+
+  const res = query.get({ $1: messageId, $2: userId });
+  return res;
+}
+
+function insertLike(messageId, userId) {
+  try {
+    const query = db.query(
+      `INSERT INTO likes (message_id, user_id, liked_at) VALUES ($1, $2, $3)`,
+    );
+    query.run({ $1: messageId, $2: userId, $3: Date.now() });
+  } catch (err) {
+    console.log(err);
+    return err;
+  }
+}
+
+function deleteLike(messageId, userId) {
+  try {
+    const query = db.query(
+      `DELETE FROM likes WHERE message_id = $1 and user_id = $2`,
+    );
+    query.run({ $1: messageId, $2: userId });
+  } catch (err) {
+    console.log(err);
+    return err;
+  }
+}
+
+function insertUser(username, password) {
+  try {
+    const query = db.query(
+      "INSERT INTO users (username, password) VALUES ($username, $password)",
+    );
+    query.run({ $username: username, $password: password });
+  } catch (err) {
+    return err;
+  }
+}
+function getUserById(userId) {
+  const query = db.query(
+    `SELECT username, user_id FROM users WHERE user_id = $1`,
+  );
+  const res = query.get({ $1: userId });
+
+  return res;
+}
+
+function getSumNotesByUserId(userId) {
+  const query = db.query(`SELECT COUNT(1) FROM messages WHERE user_id = $1`);
+  const res = query.get({ $1: userId });
+
+  return res["COUNT(1)"];
+}
+function getAllNotesByUserId(userId) {
+  const query = db.query(`SELECT * FROM messages WHERE user_id = $1`);
+  const res = query.all({ $1: userId });
+
+  return res;
+}
+
+module.exports = {
+  getEveryNote,
+  getNoteById,
+  putNewNote,
+  insertLike,
+  deleteLike,
+  deleteNote,
+  checkIfNotedLiked,
+  insertUser,
+  getLikesByUser,
+  getTotalLikesByMessageId,
+  getUserById,
+  getSumNotesByUserId,
+  getAllNotesByUserId,
+};
diff --git a/messages/src/routes/authRouter.js b/messages/src/routes/authRouter.js
new file mode 100644
index 0000000..52912a7
--- /dev/null
+++ b/messages/src/routes/authRouter.js
@@ -0,0 +1,32 @@
+const { Router } = require("express");
+const authController = require("../controllers/authController");
+const passport = require("../middlewares/auth");
+const { body } = require("express-validator");
+
+const authRouter = Router();
+
+//authRouter.get("/", (req, res) => res.redirect("/auth/login"));
+authRouter.get("/login", authController.loginGet);
+authRouter.post(
+  "/login",
+  passport.authenticate("local", {
+    successRedirect: "/",
+    failureRedirect: "/auth/login",
+    failureMessage: true,
+  }),
+);
+authRouter.get("/logout", authController.logOut);
+authRouter.get("/register", authController.signUpGet);
+authRouter.post(
+  "/register",
+  body("password").isLength({ min: 5 }).withMessage("Password is too short"),
+  body("password-confirmation").custom((value, { req }) => {
+    if (value !== req.body.password) {
+      throw new Error("Passwords do not match!");
+    }
+    return true;
+  }),
+  authController.signUpPost,
+);
+
+module.exports = { authRouter };
diff --git a/messages/src/routes/indexRouter.js b/messages/src/routes/indexRouter.js
new file mode 100644
index 0000000..badc508
--- /dev/null
+++ b/messages/src/routes/indexRouter.js
@@ -0,0 +1,13 @@
+const { Router } = require("express");
+const indexController = require("../controllers/indexController");
+
+const indexRouter = Router();
+
+indexRouter.get("/", indexController.indexGet);
+indexRouter.get("/like", indexController.addLike);
+indexRouter.get("/new", indexController.addNoteGet);
+indexRouter.post("/new", indexController.addNotePost);
+indexRouter.get("/profile", indexController.getProfile);
+indexRouter.get("/delete", indexController.deleteNote);
+
+module.exports = { indexRouter };
diff --git a/messages/src/views/content.ejs b/messages/src/views/content.ejs
new file mode 100644
index 0000000..e69de29
diff --git a/messages/src/views/feed.ejs b/messages/src/views/feed.ejs
new file mode 100644
index 0000000..2188d2a
--- /dev/null
+++ b/messages/src/views/feed.ejs
@@ -0,0 +1,31 @@
+<%- include('header') %>
+<div class="grid">
+	<% keynotes.forEach((note) => { %>
+		<% const likedPost = userLikedPosts.find((post) =>  post.message_id == note.message_id) %>
+		<% let extraMessageClass = "" %>
+		<% if (note.message.length > 250) { %>
+			<% extraMessageClass = " grid__span_two"; %>
+		<% }; %>
+		<div class="grid-item card<%= extraMessageClass %>">
+			<p class="card__message"><%= note.message %></p>
+			<div class="container grid__card__metadata">
+			<div class="flex author__meta"> 
+			<p class="card__author"><a href="/profile?userId=<%= note.user_id %>"><%= note.user %></a></p>
+			<p class="card__likes">&bull;</p>
+			<p class="card__likes timestamp"><%= new Date(note.date).toLocaleString() %></p>
+			</div>
+			<% if (currentUser.user_id === note.user_id) { %>
+				<a href="/delete?userId=<%= currentUser.user_id %>&messageId=<%= note.message_id %>">delete post</a>
+			<% }; %>
+			<div class="likes">
+
+			<a href="/like?noteId=<%= note.message_id %>">
+				<% if (likedPost) { %><i class="fa-solid fa-heart red"></i><% } else { %><i class="fa-regular fa-heart red"></i><% }; %>
+			</a>
+			<p class="card__likes"><%= note.likes_count %></p>
+			</div>
+			</div>
+		</div>
+	<% }); %>
+</div>
+<%- include('footer') %>
diff --git a/messages/src/views/footer.ejs b/messages/src/views/footer.ejs
new file mode 100644
index 0000000..fc5b341
--- /dev/null
+++ b/messages/src/views/footer.ejs
@@ -0,0 +1,12 @@
+	<footer class="container">
+		<h2 class="foooter-heading">Inspired Cliches</h2>
+		<p>Making unsolicited advice inspiring.</p>
+		<div class="social-icons flex">
+			<a href="https://github.com/smiggiddy/odin-codeprojects" target="_blank"><i class="fa-brands fa-github"></i></a>
+			<a href="https://smig.tech" target="_blank"><i class="fa-solid fa-link"></i></a>
+		</div>
+		<p> © Copyright <%= new Date(Date.now()).getFullYear() %></p>
+	</footer>
+	<script src="/js/script.js"></script>
+	</body>
+</html>
diff --git a/messages/src/views/header.ejs b/messages/src/views/header.ejs
new file mode 100644
index 0000000..bbb49ed
--- /dev/null
+++ b/messages/src/views/header.ejs
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<link rel="preconnect" href="https://fonts.googleapis.com">
+		<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+		<link href="https://fonts.googleapis.com/css2?family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
+		<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
+		<title><%= pageTitle %></title>
+		<link href="/css/style.css" rel="stylesheet">
+		<link rel="icon" href="/images/favicon.png">
+		<!--<script src="https://kit.fontawesome.com/24f16b96cf.js" crossorigin="anonymous"></script>-->
+		<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
+	</head>
+	<body>
+		<nav>
+			<ul class="container flex">
+				<li><a class="hover__underline" href="/">Home</a></li>
+				<% if (currentUser) { %>
+				<li class="nav__profile"><%= currentUser.username %>
+				<div class="hidden nav__hover">
+					<ul class="container flex col">
+					<li><a class="hover__underline" href="/profile?userId=<%= currentUser.user_id %>">view profile</a></li>
+					<li><a class="hover__underline" href="/new" class="new-link">new cliche</a></li>
+					<li><a class="hover__underline" href="/auth/logout">logout</a></li>
+					</ul>
+					</div>
+				</li>
+				<% } else { %>
+				<li><a class="hover__underline" href="/auth/login">Login</a></li>
+				<% }; %>
+			</ul>
+		</nav>
+		<div class="divider"></div>
+	
diff --git a/messages/src/views/home.ejs b/messages/src/views/home.ejs
new file mode 100644
index 0000000..efbc401
--- /dev/null
+++ b/messages/src/views/home.ejs
@@ -0,0 +1,49 @@
+<%- include('header') %>
+	<% if (currentUser) { %>
+
+	<h1> Hi <%= currentUser.username %> </h1>
+
+	<% } %>
+<div class="container flex hero">
+
+	<div class="container hero">
+		<h1 class="hero__heading">Unlock Your Potential, One cliche at a time.</h1>
+		<p class="hero__subheading">Read a cliche a day, to keep the brain at bay</p>
+		<a href="/auth/login"  type="button" class="btn btn__link">Start Today</a>
+	</div>
+	
+</div>
+<section id="main">
+	<div class="container flex">
+		<div class="card">
+			<h2>Lorem</h2>
+			<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
+		</div>
+		<div class="img">
+			<p>This is an image</p>
+		</div>
+
+	</div>
+	<div class="container flex">
+		<div class="img">
+			<p>This is an image</p>
+		</div>
+		<div class="card">
+			<h2>Lorem</h2>
+			<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
+		</div>
+
+	</div>
+	<div class="container flex">
+		<div class="card">
+			<h2>Lorem</h2>
+			<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
+		</div>
+		<div class="img">
+			<p>This is an image</p>
+		</div>
+
+	</div>
+</section>
+
+<%- include('footer') %>
diff --git a/messages/src/views/login.ejs b/messages/src/views/login.ejs
new file mode 100644
index 0000000..265964f
--- /dev/null
+++ b/messages/src/views/login.ejs
@@ -0,0 +1,30 @@
+<%- include('header') %>
+<% if (locals.errors) {%>
+  <div class="center-horizontal">
+  <ul>
+    <% errors.forEach(function(error) { %>
+      <li><%= error %></li>
+    <% }); %>
+  </ul>
+  </div>
+<% }; %>
+<main class="container full-page">
+	<form class="center-horizontal" method="POST" action="/auth/login">
+		<div class="form-item flex">
+			<label for="username">Username</label>
+			<input type="text" name="username" required/>
+		</div>
+		<div class="form-item flex">
+			<label for="password">Password</label>
+			<input type="password" name="password" required/>
+		</div>
+			<div class="form-item-row flex">
+				<button type="submit" class="btn btn__form">Login</button>
+			</div>
+			<p>Need an account?
+				<a href="/auth/register">Click here!</a>
+			</p>
+		
+	</form>
+</main>
+<%- include('footer') %>
diff --git a/messages/src/views/note-form.ejs b/messages/src/views/note-form.ejs
new file mode 100644
index 0000000..b19f657
--- /dev/null
+++ b/messages/src/views/note-form.ejs
@@ -0,0 +1,19 @@
+<%- include("header") %>
+<form action="/new" method="POST" class="center-horizontal">
+	<div class="form-item flex">
+		<label for="message">Message</label>
+		<textarea type="text" name="message" required></textarea>
+	</div>
+	<div class="form-item flex">
+		<label for="media">Media</label>
+		<input type="text" name="media" />
+	</div>
+	<div class="form-item flex col">
+		<button type="submit" class="btn">Post</button>
+		<button type="button" class="btn" onClick="document.location.href='/'">Back</button>
+	</div>
+</form>
+
+
+
+<%- include("footer") %>
diff --git a/messages/src/views/register.ejs b/messages/src/views/register.ejs
new file mode 100644
index 0000000..0919146
--- /dev/null
+++ b/messages/src/views/register.ejs
@@ -0,0 +1,35 @@
+<%- include('header') %>
+<!-- views/partials/errors.ejs -->
+<% if (locals.errors) {%>
+  <div class="center-horizontal">
+  <ul>
+    <% errors.forEach(function(error) { %>
+      <li><%= error.msg %></li>
+    <% }); %>
+  </ul>
+  </div>
+<% }; %>
+<main class="container full-page">
+	<form class="register-form center-horizontal" method="POST" action="/auth/register">
+		<div class="form-item flex">
+			<label for="username">username</label>
+			<input type="text" name="username" required/>
+			<div class="error"></div>
+		</div>
+		<div class="form-item flex">
+			<label for="password">password</label>
+			<input type="password" name="password" required/>
+			<div class="error flex col"></div>
+		</div>
+		<div class="form-item flex">
+			<label for="password-confirmation">confirm password</label>
+			<input type="password" name="password-confirmation" required/>
+			<div class="error"></div>
+		</div>
+			<div class="form-item flex">
+				<button type="submit" class="btn btn__form">register</button>
+			</div>
+		
+	</form>
+</main>
+<%- include('footer') %>
diff --git a/messages/src/views/view-profile.ejs b/messages/src/views/view-profile.ejs
new file mode 100644
index 0000000..285154b
--- /dev/null
+++ b/messages/src/views/view-profile.ejs
@@ -0,0 +1,20 @@
+<%- include("header") %>
+
+<div class="full-page container center-horizontal">
+	<div class="container">
+		<h1 class="username"><%= user.username %></h1>
+		<% if(currentUser.user_id === user.user_id ) { %>
+			<p>This is your profile page.</p>
+		<% }; %>
+	</div>
+	<div class="container">
+		<h2><%= user.username %>'s <%= totalPosts %> post(s): </h2>
+		<% allPosts.forEach((post) => { %>
+			<div class="container">
+			<p>"<%= post.message %>"</p>
+			</div>
+		<% }); %>
+	</div>
+
+</div>
+<%- include("footer") %>