diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 906a333..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -public/javascript/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index d83cd91..43ddd2f 100644 --- a/.gitignore +++ b/.gitignore @@ -136,10 +136,4 @@ dist .pnp.* -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ - -# webpack bundles -/public/javascript/* \ No newline at end of file +tests-results/ \ No newline at end of file diff --git a/.sequelizerc b/.sequelizerc deleted file mode 100644 index c49d00f..0000000 --- a/.sequelizerc +++ /dev/null @@ -1,6 +0,0 @@ -const path = require('path'); - -module.exports = { - config: path.resolve('src', 'database', 'config.js'), - 'migrations-path': path.resolve('src', 'database', 'migrations'), -}; diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index fa244c9..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,10 +0,0 @@ -# secajs - -## Architecture - -This repository contains a webapp. It covers the full stack which consists of: -- The client side code, in `src/front/`. -- The backend service in `src/`, except for `src/front/`. -- A Postgres database. Database connections and migrations are in `src/database/`. -- Besides, there is an admin CLI, with entrypoint in `src/cli.js` and commands in `src/commands/`. - diff --git a/Dockerfile b/Dockerfile index 42d2c5c..2337aa3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,10 @@ -FROM debian:12 +FROM debian:latest # Install dependencies -RUN apt-get update - -RUN apt-get install -y \ - curl gnupg2 ca-certificates lsb-release apt-transport-https - -RUN apt-get install -y \ - postgresql - -RUN apt-get install -y \ - caddy - -RUN apt-get install -y \ - nodejs npm - - -RUN rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y \ + curl gnupg2 ca-certificates lsb-release apt-transport-https \ + postgresql caddy nodejs npm && \ + rm -rf /var/lib/apt/lists/* RUN echo "listen_addresses='*'" >> /etc/postgresql/15/main/postgresql.conf && \ echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/15/main/pg_hba.conf && \ diff --git a/README.md b/README.md index 6098e02..1cfe8a9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ laseca is a social bitcoin to cash exchange, implemented as a webapp. +## Upcoming stories + +* [ ] Create offer +* [ ] Display existing offers +* [ ] Archive offer + ## How to set up dev environment * Pre-requisites @@ -10,16 +16,11 @@ laseca is a social bitcoin to cash exchange, implemented as a webapp. * Installing + Run `npm install` + You can now start the app in a container by running `npm run start:container` (and shut it down with `npm run stop:container`). -* Building - + The front-end code gets built with webpack. You can build it anytime with `npm run build`. - + For development, it's useful to build continuously. You can run `npm run watch` and webpack will build every time you edit a monitored file. * Running + Copy the `.env.dist` file into `.env` and set any values you like. + The app will run in a single container, with a Postgres database, a caddy webserver and the nodejs app. + Note that the container doesn't come with a volume for Postgres: default behaviour is to start from scratch every time you create the container, delete everything every time you delete the container. - + You probably want to run migrations to get the database into proper state. You can do so with `npx sequelize-cli db:migrate`. + The docker image launches the nodejs app with nodemon, so changes to the code will be available immediately. - + Furthermore, since the git repository gets mounted live into the docker container, the live changes made by webpack watch mode will also be available as you work on front end files. + The Postgres database is reachable from the host, so you can use your favourite SQL client to access it. + You can format with `npm run format` and lint with `npm run lint`. diff --git a/package-lock.json b/package-lock.json index 4068beb..5490e56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "laseca", - "version": "0.1.0", + "name": "express-hello-world", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "laseca", - "version": "0.1.0", + "name": "express-hello-world", + "version": "1.0.0", "license": "ISC", "dependencies": { "commander": "^13.1.0", @@ -29,19 +29,7 @@ "globals": "^15.15.0", "playwright": "^1.50.1", "prettier": "^3.5.1", - "prettier-plugin-ejs": "^1.0.3", - "sequelize-cli": "^6.6.2", - "webpack": "^5.98.0", - "webpack-cli": "^6.0.1" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", - "dev": true, - "engines": { - "node": ">=14.17.0" + "prettier-plugin-ejs": "^1.0.3" } }, "node_modules/@eslint-community/eslint-utils": { @@ -205,160 +193,6 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@noble/ciphers": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", @@ -456,22 +290,6 @@ "node": ">=10" } }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "dev": true - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -563,38 +381,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -618,208 +404,6 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -932,45 +516,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/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==", - "dev": true - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1028,15 +573,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1079,12 +615,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1117,38 +647,6 @@ "concat-map": "0.0.1" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1172,12 +670,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1250,26 +742,6 @@ "node": ">=6" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001703", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz", - "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1293,15 +765,6 @@ "node": ">=10" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -1311,47 +774,6 @@ "node": ">=6" } }, - "node_modules/cli-color": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", - "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", - "dev": true, - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1377,12 +799,6 @@ "color-support": "bin.js" } }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -1396,16 +812,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1477,19 +883,6 @@ "node": ">= 8" } }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1596,63 +989,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", - "dev": true, - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "9.0.1", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/editorconfig/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1672,17 +1008,11 @@ "node": ">=0.10.0" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.114", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz", - "integrity": "sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==", - "dev": true - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true + "optional": true }, "node_modules/encodeurl": { "version": "2.0.0", @@ -1721,19 +1051,6 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -1743,18 +1060,6 @@ "node": ">=6" } }, - "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -1777,12 +1082,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -1794,67 +1093,6 @@ "node": ">= 0.4" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "dev": true, - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2037,21 +1275,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dev": true, - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -2114,25 +1337,6 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -2186,15 +1390,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dev": true, - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2216,31 +1411,6 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -2324,15 +1494,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -2351,34 +1512,6 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2400,21 +1533,6 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -2473,15 +1591,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -2553,12 +1662,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, "node_modules/globals": { "version": "15.15.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", @@ -2586,7 +1689,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "devOptional": true + "optional": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -2785,25 +1888,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2855,15 +1939,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -2885,21 +1960,6 @@ "node": ">= 0.10" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2912,7 +1972,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, + "optional": true, "engines": { "node": ">=8" } @@ -2942,53 +2002,11 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -3006,142 +2024,6 @@ "node": ">=10" } }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-beautify": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", - "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", - "dev": true, - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.4.2", - "js-cookie": "^3.0.5", - "nopt": "^7.2.1" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/js-beautify/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/js-beautify/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/js-beautify/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "dev": true, - "engines": { - "node": ">=14" - } - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3164,12 +2046,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3180,18 +2056,6 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3200,15 +2064,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3221,15 +2076,6 @@ "node": ">= 0.8.0" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3266,15 +2112,6 @@ "node": ">=10" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dev": true, - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/make-fetch-happen": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", @@ -3318,25 +2155,6 @@ "node": ">= 0.6" } }, - "node_modules/memoizee": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", - "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", - "dev": true, - "dependencies": { - "d": "^1.0.2", - "es5-ext": "^0.10.64", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -3345,12 +2163,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -3565,18 +2377,6 @@ "node": ">= 0.6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, "node_modules/node-abi": { "version": "3.74.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", @@ -3617,12 +2417,6 @@ "node": ">= 10.12.0" } }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -3773,21 +2567,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3831,43 +2610,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -3954,76 +2696,6 @@ "split2": "^4.1.0" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/playwright": { "version": "1.50.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", @@ -4177,12 +2849,6 @@ "node": ">=10" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -4245,15 +2911,6 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4303,77 +2960,6 @@ "node": ">= 6" } }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4466,59 +3052,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/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==", - "dev": true - }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -4627,28 +3160,6 @@ } } }, - "node_modules/sequelize-cli": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", - "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", - "dev": true, - "dependencies": { - "cli-color": "^2.0.3", - "fs-extra": "^9.1.0", - "js-beautify": "^1.14.5", - "lodash": "^4.17.21", - "resolve": "^1.22.1", - "umzug": "^2.3.0", - "yargs": "^16.2.0" - }, - "bin": { - "sequelize": "lib/sequelize", - "sequelize-cli": "lib/sequelize" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/sequelize-pool": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", @@ -4686,15 +3197,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -4720,18 +3222,6 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4929,25 +3419,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "optional": true }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -5017,22 +3488,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5053,19 +3509,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -5085,18 +3528,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -5113,15 +3544,6 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -5177,82 +3599,11 @@ "node": ">=8" } }, - "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -5283,12 +3634,6 @@ "node": "*" } }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5323,18 +3668,6 @@ "node": ">= 0.6" } }, - "node_modules/umzug": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", - "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", - "dev": true, - "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -5358,15 +3691,6 @@ "imurmurhash": "^0.1.4" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -5375,36 +3699,6 @@ "node": ">= 0.8" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5454,161 +3748,6 @@ "node": ">= 0.8" } }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5632,12 +3771,6 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, "node_modules/wkx": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", @@ -5654,41 +3787,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -5702,47 +3800,11 @@ "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 568a109..05c2f32 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,10 @@ "start": "node src/app.js", "start:container": "docker compose up -d --build", "stop:container": "docker compose down", - "migrate": "npx sequelize-cli db:migrate", - "build": "webpack", - "watch": "webpack --watch", "cli": "node src/cli.js", + "test": "playwright test", "lint": "eslint . --fix", - "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,html,ejs}\"", - "test": "playwright test" + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,html,ejs}\"" }, "keywords": [], "author": "", @@ -39,9 +36,6 @@ "globals": "^15.15.0", "playwright": "^1.50.1", "prettier": "^3.5.1", - "prettier-plugin-ejs": "^1.0.3", - "sequelize-cli": "^6.6.2", - "webpack": "^5.98.0", - "webpack-cli": "^6.0.1" + "prettier-plugin-ejs": "^1.0.3" } } diff --git a/playwright.config.js b/playwright.config.js deleted file mode 100644 index c7f57c8..0000000 --- a/playwright.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { defineConfig } = require('@playwright/test'); - -module.exports = defineConfig({ - testDir: './tests', - use: { - baseURL: 'http://localhost:3000', - }, - webServer: { - command: 'npm start', - url: 'http://localhost:3000', - reuseExistingServer: !process.env.CI, - }, -}); diff --git a/public/css/createProfile.css b/public/css/createProfile.css deleted file mode 100644 index e9d02c1..0000000 --- a/public/css/createProfile.css +++ /dev/null @@ -1,64 +0,0 @@ -@media (max-width: 768px) { - .over-background > *:first-child { - margin-bottom: 5px; - } - - .over-background > *:last-child { - margin-top: 5px; - } - - .over-background > * { - margin-bottom: 5px; - margin-top: 5px; - } -} - -@media (min-width: 769px) { - .over-background > *:first-child { - margin-bottom: 10px; - } - - .over-background > *:last-child { - margin-top: 10px; - } - - .over-background > * { - margin-bottom: 10px; - margin-top: 10px; - } -} - -.badges { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(3, 1fr); -} - -.badge { - border: 2px solid #e1c300; - border-radius: 10px; - padding: 2%; - margin: 1%; -} - -.badge input { - max-width: 80%; -} - -.create-profile-card-section > * { - text-align: center; - margin-bottom: 10px; - margin-right: auto; - margin-left: auto; - display: block; - max-width: 1000px; -} - -.create-profile-card-section .badge img { - width: 10%; -} - -h2 { - margin-top: 0; - margin-bottom: 5px; -} diff --git a/public/css/invite.css b/public/css/invite.css deleted file mode 100644 index bdb4779..0000000 --- a/public/css/invite.css +++ /dev/null @@ -1,39 +0,0 @@ -@media (max-width: 768px) { - #nostr-signup-button { - width: 200px; - } - - #invite-card-content { - width: 100%; - } -} - -@media (min-width: 769px) { - #nostr-signup-button { - width: 300px; - } - - #invite-card-content { - width: 40%; - min-width: min-content; - } -} - -#laseca-logo { - width: 200px; -} - -#nostr-signup-button p { - font-size: 1.5rem; -} - -#invite-card-content { - margin-right: auto; - margin-left: auto; - padding: 20px; -} - -#invite-card-content > * { - margin: 1vh auto; - text-align: center; -} diff --git a/public/css/login.css b/public/css/login.css deleted file mode 100644 index f457888..0000000 --- a/public/css/login.css +++ /dev/null @@ -1,39 +0,0 @@ -@media (max-width: 768px) { - #login-button { - width: 200px; - } - - .logo { - width: 350px; - } - - #login-card-content { - width: 100%; - } -} - -@media (min-width: 769px) { - #login-button { - width: 300px; - } - - .logo { - width: 500px; - } - - #login-card-content { - width: 40%; - min-width: min-content; - } -} - -#login-card-content { - margin-right: auto; - margin-left: auto; - padding: 20px; -} - -#login-card-content > * { - margin: 1vh auto; - text-align: center; -} diff --git a/public/img/bolt-lightning-black.svg b/public/img/bolt-lightning-black.svg deleted file mode 100644 index abd020b..0000000 --- a/public/img/bolt-lightning-black.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/public/img/bolt-lightning-gray.svg b/public/img/bolt-lightning-gray.svg deleted file mode 100644 index f688130..0000000 --- a/public/img/bolt-lightning-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/bolt-lightning-lasecagold.svg b/public/img/bolt-lightning-lasecagold.svg deleted file mode 100644 index ec20813..0000000 --- a/public/img/bolt-lightning-lasecagold.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/public/img/bolt-lightning.svg b/public/img/bolt-lightning.svg deleted file mode 100644 index 14c065b..0000000 --- a/public/img/bolt-lightning.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/chains-black.svg b/public/img/chains-black.svg deleted file mode 100644 index ea8862e..0000000 --- a/public/img/chains-black.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/public/img/chains-gray.svg b/public/img/chains-gray.svg deleted file mode 100644 index 708794c..0000000 --- a/public/img/chains-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/chains-lasecagold.svg b/public/img/chains-lasecagold.svg deleted file mode 100644 index 00f3766..0000000 --- a/public/img/chains-lasecagold.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/chains.svg b/public/img/chains.svg deleted file mode 100644 index a87a535..0000000 --- a/public/img/chains.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/img/circle-check-green.svg b/public/img/circle-check-green.svg deleted file mode 100644 index 75a0c96..0000000 --- a/public/img/circle-check-green.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/circle-check-white.svg b/public/img/circle-check-white.svg deleted file mode 100644 index e5427dc..0000000 --- a/public/img/circle-check-white.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/circle-xmark-gray.svg b/public/img/circle-xmark-gray.svg deleted file mode 100644 index c6a219a..0000000 --- a/public/img/circle-xmark-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/edit.svg b/public/img/edit.svg deleted file mode 100644 index 814fd37..0000000 --- a/public/img/edit.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/img/eur-bill-gray.svg b/public/img/eur-bill-gray.svg deleted file mode 100644 index 28aefbc..0000000 --- a/public/img/eur-bill-gray.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - diff --git a/public/img/eur-bill-lasecagold.svg b/public/img/eur-bill-lasecagold.svg deleted file mode 100644 index b2d06a3..0000000 --- a/public/img/eur-bill-lasecagold.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - diff --git a/public/img/eur-bill.svg b/public/img/eur-bill.svg deleted file mode 100644 index 44bfd57..0000000 --- a/public/img/eur-bill.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - diff --git a/public/img/laseca-logo-transparent-textonly.svg b/public/img/laseca-logo-transparent-textonly.svg deleted file mode 100644 index 9884298..0000000 --- a/public/img/laseca-logo-transparent-textonly.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - -LASECA diff --git a/public/img/many-users-gray.svg b/public/img/many-users-gray.svg deleted file mode 100644 index d60a47e..0000000 --- a/public/img/many-users-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/many-users-lasecagold.svg b/public/img/many-users-lasecagold.svg deleted file mode 100644 index eed7477..0000000 --- a/public/img/many-users-lasecagold.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/many-users.svg b/public/img/many-users.svg deleted file mode 100644 index 4add8ad..0000000 --- a/public/img/many-users.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/img/trash-can-darkred.svg b/public/img/trash-can-darkred.svg deleted file mode 100644 index 09fefd0..0000000 --- a/public/img/trash-can-darkred.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/trash-can-lasecagold.svg b/public/img/trash-can-lasecagold.svg deleted file mode 100644 index 611dfed..0000000 --- a/public/img/trash-can-lasecagold.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/trash-can.svg b/public/img/trash-can.svg deleted file mode 100644 index d37fa55..0000000 --- a/public/img/trash-can.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/img/user-gray.svg b/public/img/user-gray.svg deleted file mode 100644 index d9813d5..0000000 --- a/public/img/user-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/user-group-gray.svg b/public/img/user-group-gray.svg deleted file mode 100644 index e798ca5..0000000 --- a/public/img/user-group-gray.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/user-group-lasecagold.svg b/public/img/user-group-lasecagold.svg deleted file mode 100644 index bad367b..0000000 --- a/public/img/user-group-lasecagold.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/user-group.svg b/public/img/user-group.svg deleted file mode 100644 index a0c3ce4..0000000 --- a/public/img/user-group.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/img/user-lasecagold.svg b/public/img/user-lasecagold.svg deleted file mode 100644 index db06cb4..0000000 --- a/public/img/user-lasecagold.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/public/img/user.svg b/public/img/user.svg deleted file mode 100644 index 7c2600b..0000000 --- a/public/img/user.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/app.js b/src/app.js index 76a3146..47430d0 100644 --- a/src/app.js +++ b/src/app.js @@ -1,35 +1,29 @@ const express = require('express'); const cookieParser = require('cookie-parser'); const path = require('path'); -const { buildDependencies } = require('./dependencies'); -function createApp(dependencies) { - const app = express(); - const port = 3000; +const app = express(); +const port = 3000; - app.set('port', port); +app.use(cookieParser()); - app.use(cookieParser()); +app.use(express.json()); - app.use(express.json()); +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); - app.set('view engine', 'ejs'); - app.set('views', path.join(__dirname, 'views')); +const createSessionMiddleware = require('./middlewares/sessionMiddleware'); - app.use(dependencies.middlewares.createSessionMiddleware); +app.use(createSessionMiddleware); - app.use('/', dependencies.webRoutes); - app.use('/api', dependencies.apiRoutes); +const webRoutes = require('./routes/webRoutes'); +const apiRoutes = require('./routes/apiRoutes'); - app.use(express.static(path.join(__dirname, '../public'))); +app.use('/', webRoutes); +app.use('/api', apiRoutes); - app.disable('etag'); //avoids 304 responses +app.use(express.static(path.join(__dirname, 'public'))); - return app; -} - -const dependencies = buildDependencies(); -const app = createApp(dependencies); -app.listen(app.get('port'), () => { - console.log(`Server started on port ${app.get('port')}`); +app.listen(port, () => { + console.log(`Server started on port ${port}`); }); diff --git a/src/cli.js b/src/cli.js index dae7913..3776ee3 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,31 +1,13 @@ const { Command } = require('commander'); -const { buildDependencies } = require('./dependencies'); +const program = new Command(); -function buildCLIDependencies() { - const appDependencies = buildDependencies(); +const createAppInviteCommand = require('./commands/createAppInvite'); - const CreateAppInviteProvider = require('./commands/createAppInvite'); - const createAppInvite = new CreateAppInviteProvider({ - invitesService: appDependencies.services.invitesService, - }).provide(); +program.version('1.0.0').description('CLI for managing web app tasks'); - return { createAppInviteCommand: createAppInvite }; -} +program + .command('createAppInvite ') + .description('Create an invite') + .action(createAppInviteCommand); -function buildCLI({ createAppInviteCommand }) { - const wipCli = new Command(); - wipCli.version('1.0.0').description('CLI for managing web app tasks'); - - wipCli - .command('createAppInvite ') - .description('Create an invite') - .action(createAppInviteCommand); - - return wipCli; -} - -const cliDependencies = buildCLIDependencies(); - -const cli = buildCLI(cliDependencies); - -cli.parse(process.argv); +program.parse(process.argv); diff --git a/src/commands/createAppInvite.js b/src/commands/createAppInvite.js index 610c098..0767123 100644 --- a/src/commands/createAppInvite.js +++ b/src/commands/createAppInvite.js @@ -1,17 +1,7 @@ -class CreateAppInviteProvider { - constructor({ invitesService }) { - this.invitesService = invitesService; - } +const invitesService = require('../services/invitesService'); - provide() { - const createAppInvite = async (inviterNpub) => { - const appInvite = await this.invitesService.createAppInvite(inviterNpub); - console.log('Invite created'); - console.log(`Check at http://localhost/invite/${appInvite.uuid}`); - }; - - return createAppInvite; - } -} - -module.exports = CreateAppInviteProvider; +module.exports = async function createAppInvite(inviterNpub) { + const appInvite = await invitesService.createAppInvite(inviterNpub); + console.log('Invite created'); + console.log(`Check at http://localhost/invite/${appInvite.uuid}`); +}; diff --git a/src/constants.js b/src/constants.js index 7af448b..8bec536 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,15 +1,7 @@ const DEFAULT_SESSION_DURATION_SECONDS = 60 * 60 * 24 * 30; const DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS = 60 * 60 * 24 * 30; -const DEFAULT_REDIRECT_DELAY = 3 * 1000; // 3seconds times milliseconds; - -const API_PATHS = { - createProfile: '/createProfile', - home: '/home', -}; module.exports = { DEFAULT_SESSION_DURATION_SECONDS, DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS, - API_PATHS, - DEFAULT_REDIRECT_DELAY, }; diff --git a/src/database/database.js b/src/database.js similarity index 74% rename from src/database/database.js rename to src/database.js index ed4ef6c..b90f77f 100644 --- a/src/database/database.js +++ b/src/database.js @@ -10,11 +10,6 @@ const sequelize = new Sequelize({ database: process.env.POSTGRES_DB, username: process.env.POSTGRES_USER, password: process.env.POSTGRES_PASSWORD, - /* logging: (msg) => { - if (msg && (msg.includes('ERROR') || msg.includes('error'))) { - console.error(msg); - } - }, */ define: { timestamps: false, freezeTableName: true, @@ -23,4 +18,13 @@ const sequelize = new Sequelize({ }, }); +sequelize + .sync() + .then(() => { + console.log('Database synced'); + }) + .catch((err) => { + console.error('Error syncing the database:', err); + }); + module.exports = sequelize; diff --git a/src/database/associations.js b/src/database/associations.js deleted file mode 100644 index 45747be..0000000 --- a/src/database/associations.js +++ /dev/null @@ -1,36 +0,0 @@ -class AssociationsDefiner { - constructor({ models, DataTypes }) { - this.models = models; - this.DataTypes = DataTypes; - } - - define() { - this.models.NostrChallengeCreated.hasOne( - this.models.NostrChallengeCompleted, - { - foreignKey: 'challenge', - } - ); - this.models.NostrChallengeCompleted.belongsTo( - this.models.NostrChallengeCreated, - { - foreignKey: { - name: 'challenge', - }, - } - ); - - this.models.OfferCreated.hasOne(this.models.OfferDeleted, { - foreignKey: 'offer_uuid', - }); - this.models.OfferDeleted.belongsTo(this.models.OfferCreated, { - foreignKey: { - name: 'offer_uuid', - type: this.DataTypes.UUID, - allowNull: false, - }, - }); - } -} - -module.exports = AssociationsDefiner; diff --git a/src/database/config.js b/src/database/config.js deleted file mode 100644 index 2800b22..0000000 --- a/src/database/config.js +++ /dev/null @@ -1,21 +0,0 @@ -const dotenv = require('dotenv'); - -dotenv.config(); - -module.exports = { - development: { - dialect: 'postgres', - host: process.env.POSTGRES_HOST, - port: 5432, - database: process.env.POSTGRES_DB, - username: process.env.POSTGRES_USER, - password: process.env.POSTGRES_PASSWORD, - logging: console.log, - define: { - timestamps: false, - freezeTableName: true, - underscored: true, - quoteIdentifiers: false, - }, - }, -}; diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js deleted file mode 100644 index 3fb5cb9..0000000 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ /dev/null @@ -1,379 +0,0 @@ -'use strict'; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.sequelize.transaction((t) => { - return Promise.all([ - queryInterface.createTable( - 'app_invite_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - inviter_pub_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'contact_details_set', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - encrypted_contact_details: { - type: Sequelize.TEXT, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'nostr_challenge_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - challenge: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }, - expires_at: { - type: Sequelize.DATE, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'login_challenge_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'nostr_challenge_completed', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - challenge: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }, - signed_event: { - type: Sequelize.JSONB, - allowNull: false, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'login_challenge_completed', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_completed_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'nym_set', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - nym: { - type: Sequelize.TEXT, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'offer_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'offer_deleted', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - offer_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'offer_details_set', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - offer_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - wants: { - type: Sequelize.STRING, - allowNull: false, - }, - premium: { - type: Sequelize.DECIMAL(5, 2), - allowNull: false, - }, - trade_amount_eur: { - type: Sequelize.INTEGER, - allowNull: false, - }, - location_details: { - type: Sequelize.TEXT, - allowNull: false, - }, - time_availability_details: { - type: Sequelize.TEXT, - allowNull: false, - }, - show_offer_to_trusted: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - show_offer_to_trusted_trusted: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - show_offer_to_all_members: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - is_onchain_accepted: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - is_lightning_accepted: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - are_big_notes_accepted: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'session_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - expires_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'session_related_to_public_key', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - session_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'sign_up_challenge_created', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - app_invite_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - queryInterface.createTable( - 'sign_up_challenge_completed', - { - uuid: { - type: Sequelize.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_completed_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - app_invite_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - public_key: { - type: Sequelize.STRING, - allowNull: false, - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, - }, - { transaction: t } - ), - ]); - }); - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Users'); - }, -}; diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js deleted file mode 100644 index 2052b0a..0000000 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ /dev/null @@ -1,152 +0,0 @@ -'use strict'; -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.sequelize.transaction((t) => { - return Promise.all([ - queryInterface.addConstraint( - 'login_challenge_created', - { - fields: ['nostr_challenge_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'nostr_challenge_completed', - { - fields: ['challenge'], - type: 'foreign key', - references: { - table: 'nostr_challenge_created', - field: 'challenge', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'login_challenge_completed', - { - fields: ['nostr_challenge_completed_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_completed', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'offer_deleted', - { - fields: ['offer_uuid'], - type: 'foreign key', - references: { - table: 'offer_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'offer_details_set', - { - fields: ['offer_uuid'], - type: 'foreign key', - references: { - table: 'offer_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'session_related_to_public_key', - { - fields: ['session_uuid'], - type: 'foreign key', - references: { - table: 'session_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'sign_up_challenge_created', - { - fields: ['nostr_challenge_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'sign_up_challenge_created', - { - fields: ['app_invite_uuid'], - type: 'foreign key', - references: { - table: 'app_invite_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'sign_up_challenge_completed', - { - fields: ['nostr_challenge_completed_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_completed', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - queryInterface.addConstraint( - 'sign_up_challenge_completed', - { - fields: ['app_invite_uuid'], - type: 'foreign key', - references: { - table: 'app_invite_created', - field: 'uuid', - }, - onDelete: 'cascade', - onUpdate: 'cascade', - }, - { transaction: t } - ), - ]); - }); - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Users'); - }, -}; diff --git a/src/dependencies.js b/src/dependencies.js deleted file mode 100644 index af0495b..0000000 --- a/src/dependencies.js +++ /dev/null @@ -1,52 +0,0 @@ -const express = require('express'); - -function buildDependencies() { - const dependencies = {}; - const errors = require('./errors'); - const constants = require('./constants'); - - const sequelize = require('./database/database'); - const { DataTypes } = require('sequelize'); - const ModelsProvider = require('./models'); - const models = new ModelsProvider({ sequelize, DataTypes }).provide(); - - const AssociationsDefiner = require('./database/associations'); - new AssociationsDefiner({ models, DataTypes }).define(); - - const ServicesProvider = require('./services'); - const services = new ServicesProvider({ - models, - constants, - errors, - sequelize, - }).provide(); - dependencies.services = services; - - const MiddlewaresProvider = require('./middlewares'); - const middlewares = new MiddlewaresProvider({ - constants, - sessionService: services.sessionService, - profileService: services.profileService, - }).provide(); - dependencies.middlewares = middlewares; - - const WebRoutesProvider = require('./routes/webRoutes'); - const webRoutesProvider = new WebRoutesProvider({ - express, - middlewares, - invitesService: services.invitesService, - }); - dependencies.webRoutes = webRoutesProvider.provide(); - - const ApiRoutesProvider = require('./routes/apiRoutes'); - const apiRoutesProvider = new ApiRoutesProvider({ - express, - middlewares, - services, - errors, - }); - dependencies.apiRoutes = apiRoutesProvider.provide(); - return dependencies; -} - -module.exports = { buildDependencies }; diff --git a/src/front/components/BuyOrSellButtonGroup.js b/src/front/components/BuyOrSellButtonGroup.js deleted file mode 100644 index d563e54..0000000 --- a/src/front/components/BuyOrSellButtonGroup.js +++ /dev/null @@ -1,58 +0,0 @@ -class BuyOrSellButtonGroup { - constructor({ parentElement, id }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - - this.buyButton = null; - this.sellButton = null; - } - - render() { - const groupDiv = document.createElement('div'); - groupDiv.className = 'button-group'; - groupDiv.id = this.id; - - const buyButton = document.createElement('button'); - buyButton.dataset.value = 'buy-bitcoin'; - buyButton.id = 'button-buy-bitcoin'; - buyButton.className = 'selected'; - buyButton.textContent = 'Quiero comprar Bitcoin'; - this.buyButton = buyButton; - - const sellButton = document.createElement('button'); - sellButton.dataset.value = 'sell-bitcoin'; - sellButton.id = 'button-sell-bitcoin'; - sellButton.textContent = 'Quiero vender Bitcoin'; - this.sellButton = sellButton; - - groupDiv.appendChild(this.buyButton); - groupDiv.appendChild(this.sellButton); - - for (const button of [this.buyButton, this.sellButton]) { - button.addEventListener('click', () => { - [this.buyButton, this.sellButton].forEach((aButton) => { - if (aButton.classList.contains('selected')) { - aButton.classList.remove('selected'); - } else { - aButton.classList.add('selected'); - } - }); - }); - } - - this.element = groupDiv; - this.parentElement.appendChild(this.element); - } - - wants() { - if (this.buyButton.classList.contains('selected')) { - return 'BTC'; - } - if (this.sellButton.classList.contains('selected')) { - return 'EUR'; - } - } -} - -module.exports = BuyOrSellButtonGroup; diff --git a/src/front/components/PopupNotification.js b/src/front/components/PopupNotification.js deleted file mode 100644 index e2a0588..0000000 --- a/src/front/components/PopupNotification.js +++ /dev/null @@ -1,25 +0,0 @@ -class PopupNotification { - constructor({ parentElement, id, text }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.text = text; - } - - render() { - const div = document.createElement('div'); - div.id = this.id; - div.className = 'top-notification-good'; - - div.innerHTML = ` -

${this.text}

`; - - this.element = div; - this.parentElement.appendChild(div); - } - - display() { - this.element.classList.add('revealed'); - } -} -module.exports = PopupNotification; diff --git a/src/front/components/PremiumSelector.js b/src/front/components/PremiumSelector.js deleted file mode 100644 index d2defdd..0000000 --- a/src/front/components/PremiumSelector.js +++ /dev/null @@ -1,68 +0,0 @@ -class PremiumSelector { - constructor({ parentElement, id, eventSink }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.eventSink = eventSink; - - this.premiumValue = null; - } - - render() { - const premiumSelectorArea = document.createElement('div'); - premiumSelectorArea.id = this.id; - - const premiumValue = document.createElement('div'); - premiumValue.id = 'premium-value'; - premiumValue.textContent = '0%'; - this.premiumValue = premiumValue; - - const premiumButtonsContainer = document.createElement('div'); - premiumButtonsContainer.id = 'premium-buttons-container'; - - const increaseButton = document.createElement('button'); - increaseButton.classList.add('premium-button'); - increaseButton.id = 'button-increase-premium'; - increaseButton.textContent = '+'; - - const decreaseButton = document.createElement('button'); - decreaseButton.classList.add('premium-button'); - decreaseButton.id = 'button-decrease-premium'; - decreaseButton.textContent = '-'; - - premiumButtonsContainer.appendChild(increaseButton); - premiumButtonsContainer.appendChild(decreaseButton); - - premiumSelectorArea.appendChild(premiumValue); - premiumSelectorArea.appendChild(premiumButtonsContainer); - - increaseButton.addEventListener('click', () => { - this.modifyPremiumValue(1); - }); - - decreaseButton.addEventListener('click', () => { - this.modifyPremiumValue(-1); - }); - - this.element = premiumSelectorArea; - this.parentElement.appendChild(this.element); - } - - modifyPremiumValue(delta) { - const regexExpression = /-*\d+/; - const numValue = parseInt( - this.premiumValue.innerText.match(regexExpression)[0] - ); - - const newValue = `${numValue + delta}%`; - - this.premiumValue.innerText = newValue; - this.eventSink.dispatchEvent(new Event('premium-changed')); - } - - getPremium() { - return parseInt(this.premiumValue.textContent.match(/-?\d+/)[0]) / 100; - } -} - -module.exports = PremiumSelector; diff --git a/src/front/components/PriceDisplay.js b/src/front/components/PriceDisplay.js deleted file mode 100644 index 39eb16d..0000000 --- a/src/front/components/PriceDisplay.js +++ /dev/null @@ -1,64 +0,0 @@ -const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); - -class PriceDisplay { - constructor({ - parentElement, - id, - premiumProvidingCallback, - priceProvidingCallback, - }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.premiumProvidingCallback = premiumProvidingCallback; - this.priceProvidingCallback = priceProvidingCallback; - } - - render() { - const container = document.createElement('div'); - container.id = 'premium-price-display-area'; - - const offerParagraph = document.createElement('p'); - offerParagraph.id = 'offer-price-paragraph'; - offerParagraph.textContent = 'Tu precio: '; - - const offerSpan = document.createElement('span'); - offerSpan.id = 'offer-price'; - this.offerPriceSpan = offerSpan; - - offerParagraph.appendChild(offerSpan); - offerParagraph.append('€/BTC'); - - const marketParagraph = document.createElement('p'); - marketParagraph.id = 'market-price-paragraph'; - marketParagraph.textContent = '(Precio mercado: '; - - const marketSpan = document.createElement('span'); - marketSpan.id = 'market-price'; - this.marketPriceSpan = marketSpan; - - marketParagraph.appendChild(marketSpan); - marketParagraph.append('€/BTC)'); - - container.appendChild(offerParagraph); - container.appendChild(marketParagraph); - - this.updatePrices(); - - this.element = container; - this.parentElement.appendChild(this.element); - } - - updatePrices() { - const marketPrice = this.priceProvidingCallback(); - const marketPriceString = formatNumberWithSpaces(marketPrice); - const offerPriceString = formatNumberWithSpaces( - Math.round(marketPrice * (1 + this.premiumProvidingCallback())) - ); - - this.marketPriceSpan.innerText = marketPriceString; - this.offerPriceSpan.innerText = offerPriceString; - } -} - -module.exports = PriceDisplay; diff --git a/src/front/components/PublishOfferButton.js b/src/front/components/PublishOfferButton.js deleted file mode 100644 index d43f8c1..0000000 --- a/src/front/components/PublishOfferButton.js +++ /dev/null @@ -1,21 +0,0 @@ -class PublishOfferButton { - constructor({ parentElement, id, onClickCallback }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.onClickCallback = onClickCallback; - } - - render() { - const button = document.createElement('button'); - button.id = this.id; - button.className = 'button-primary button-large'; - button.innerText = 'Publicar oferta'; - button.addEventListener('click', this.onClickCallback); - - this.element = button; - this.parentElement.appendChild(this.element); - } -} - -module.exports = PublishOfferButton; diff --git a/src/front/components/WarningDiv.js b/src/front/components/WarningDiv.js deleted file mode 100644 index ab4fa93..0000000 --- a/src/front/components/WarningDiv.js +++ /dev/null @@ -1,25 +0,0 @@ -class WarningDiv { - constructor({ parentElement, id, innerHTML }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.innerHTML = innerHTML; - } - - render() { - const div = document.createElement('div'); - div.id = this.id; - div.className = 'card-secondary'; - div.style.display = 'none'; - - div.innerHTML = this.innerHTML; - - this.element = div; - this.parentElement.appendChild(div); - } - - display() { - this.element.style.display = 'block'; - } -} -module.exports = WarningDiv; diff --git a/src/front/components/nostrButtons.js b/src/front/components/nostrButtons.js deleted file mode 100644 index 31513f5..0000000 --- a/src/front/components/nostrButtons.js +++ /dev/null @@ -1,63 +0,0 @@ -class NostrButton { - constructor({ parentElement, id, onClickCallback, buttonText }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.onClickCallback = onClickCallback; - this.buttonText = buttonText; - } - - render() { - const thisButton = document.createElement('button'); - thisButton.id = this.id; - thisButton.type = 'submit'; - thisButton.className = 'button-large button-nostr'; - - const figure = document.createElement('figure'); - - const img = document.createElement('img'); - img.src = '/img/white_ostrich.svg'; - img.style.width = '40%'; - img.style.margin = '-5% -5%'; - - figure.appendChild(img); - - const paragraph = document.createElement('p'); - paragraph.textContent = this.buttonText; - - thisButton.appendChild(figure); - thisButton.appendChild(paragraph); - - thisButton.addEventListener('click', () => { - this.onClickCallback(); - }); - - this.element = thisButton; - this.parentElement.appendChild(this.element); - } - - disable() { - if (this.element) { - this.element.disabled = true; - } - } -} - -class NostrSignupButton extends NostrButton { - constructor({ parentElement, id, onClickCallback }) { - super({ parentElement, id, onClickCallback, buttonText: 'Alta con Nostr' }); - } -} - -class NostrLoginButton extends NostrButton { - constructor({ parentElement, id, onClickCallback }) { - super({ - parentElement, - id, - onClickCallback, - buttonText: 'Login con Nostr', - }); - } -} - -module.exports = { NostrSignupButton, NostrLoginButton }; diff --git a/src/front/pages/createProfile.js b/src/front/pages/createProfile.js deleted file mode 100644 index 51e61dd..0000000 --- a/src/front/pages/createProfile.js +++ /dev/null @@ -1,125 +0,0 @@ -const createProfilesFunction = () => { - const createProfileConfirmation = document.querySelector( - '#create-profile-success' - ); - - function debounce(func, wait) { - let timeout; - return function (...args) { - clearTimeout(timeout); - timeout = setTimeout(() => func.apply(this, args), wait); - }; - } - - const validateNymInput = debounce(() => { - const nymValue = nymInput.value.trim(); - const isValid = nymValue.length >= 3 && nymValue.length <= 128; - if (isValid) { - nymInputValidationWarning.style.display = 'none'; - } else { - nymInputValidationWarning.style.display = 'block'; - } - }, 500); - - const checkIfSubmittable = debounce((allInputs) => { - const nymIsFilled = allInputs.nymInput.value !== ''; - let atLeastOneContactIsFilled = false; - - for (const contactInput of allInputs.contactInputs) { - if (contactInput.value !== '') { - atLeastOneContactIsFilled = true; - } - } - - const buttonShouldBeDisabled = !(nymIsFilled && atLeastOneContactIsFilled); - submitProfileButton.disabled = buttonShouldBeDisabled; - }, 500); - - async function createProfile(allInputs) { - const contactDetails = []; - for (const someInput of allInputs.contactInputs) { - contactDetails.push({ - type: someInput.getAttribute('data-type'), - value: someInput.value, - }); - } - const encryptedContactDetails = await window.nostr.nip04.encrypt( - await window.nostr.getPublicKey(), - JSON.stringify(contactDetails) - ); - await fetch('/api/set-contact-details', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ encryptedContactDetails }), - }); - - const nym = allInputs.nymInput.value; - await fetch('/api/set-nym', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ nym }), - }); - - createProfileConfirmation.classList.add('revealed'); - setTimeout(() => { - window.location.href = '/home'; - }, 5000); - } - - function onLoadErrands(allInputs, submitProfileButton) { - allInputs.nymInput.addEventListener('input', validateNymInput); - - for (const someInput of allInputs.allInputs) { - someInput.addEventListener('input', () => { - checkIfSubmittable(allInputs); - }); - } - - checkIfSubmittable(allInputs); - - submitProfileButton.addEventListener('click', () => { - createProfile(allInputs); - }); - } - - const nymInput = document.getElementById('nym-input'); - const nymInputValidationWarning = document.getElementById( - 'nym-input-validation-warning' - ); - const phoneInput = document.getElementById('phone-input'); - const whatsappInput = document.getElementById('whatsapp-input'); - const telegramInput = document.getElementById('telegram-input'); - const emailInput = document.getElementById('email-input'); - const nostrInput = document.getElementById('nostr-input'); - const signalInput = document.getElementById('signal-input'); - const submitProfileButton = document.getElementById('submit-profile-button'); - - const allInputs = { - nymInput: nymInput, - contactInputs: [ - phoneInput, - whatsappInput, - telegramInput, - emailInput, - nostrInput, - signalInput, - ], - allInputs: [ - nymInput, - phoneInput, - whatsappInput, - telegramInput, - emailInput, - nostrInput, - signalInput, - ], - }; - - onLoadErrands(allInputs, submitProfileButton); -}; - -createProfilesFunction(); diff --git a/src/front/pages/home.js b/src/front/pages/home.js deleted file mode 100644 index 92bd6bb..0000000 --- a/src/front/pages/home.js +++ /dev/null @@ -1,14 +0,0 @@ -const homeFunction = () => { - const navbuttonHome = document.getElementById('navbutton-home'); - const navbuttonOffers = document.getElementById('navbutton-offers'); - - navbuttonHome.addEventListener('click', () => { - window.location.href = '/home'; - }); - - navbuttonOffers.addEventListener('click', () => { - window.location.href = '/offers'; - }); -}; - -homeFunction(); diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js deleted file mode 100644 index 24bc3b5..0000000 --- a/src/front/pages/invite.js +++ /dev/null @@ -1,110 +0,0 @@ -const checkNostrExtension = require('../utils/checkNostrExtension'); -const inviteService = require('../services/inviteService'); -const constants = require('../../constants'); -const { NostrSignupButton } = require('../components/nostrButtons'); -const WarningDiv = require('../components/WarningDiv'); -const PopupNotification = require('../components/PopupNotification'); - -const invitesFunction = () => { - const body = document.querySelector('body'); - const warningsContainer = document.getElementById('warnings-container'); - const nostrSignupArea = document.getElementById('nostr-signup-area'); - - const signupButton = new NostrSignupButton({ - parentElement: nostrSignupArea, - id: 'nostr-signup-button', - onClickCallback: async () => { - const verifyResponse = - await inviteService.requestAndRespondSignUpChallenge({ - onNostrErrorCallback: () => { - rejectedNostrWarning.display(); - }, - }); - - if (verifyResponse.ok) { - signUpSuccessPopup.display(); - setTimeout(() => { - window.location.href = constants.API_PATHS.createProfile; - }, constants.DEFAULT_REDIRECT_DELAY); - } - }, - }); - signupButton.render(); - - const noExtensionWarning = new WarningDiv({ - parentElement: warningsContainer, - id: 'no-extension-nudges', - innerHTML: `

- ¡Atención! No se ha encontrado una extensión de Nostr en tu - navegador. Puedes usar: -

-

Firefox

-

- Alby -

-

- nos2x-fox -

-

Chrome

-

- Alby -

-

- nos2x -

`, - }); - noExtensionWarning.render(); - - const rejectedNostrWarning = new WarningDiv({ - parentElement: warningsContainer, - id: 'rejected-nostr-nudges', - innerHTML: `

- Ups, parece que no has aceptado que usemos tus claves. Si te has - equivocado, puedes intentarlo de nuevo. -

`, - }); - rejectedNostrWarning.render(); - - const signUpSuccessPopup = new PopupNotification({ - parentElement: body, - id: 'sign-up-success', - text: '¡Bien! Hemos dado de alta tu clave de Nostr. Te vamos a redirigir a la seca, espera un momento.', - }); - signUpSuccessPopup.render(); - - window.onload = () => { - checkNostrExtension({ - window, - successCallback: () => { - console.log('Nostr extension present'); - }, - failureCallback: () => { - console.log('Nostr extension not present'); - signupButton.disable(); - noExtensionWarning.display(); - }, - }); - }; -}; - -invitesFunction(); diff --git a/src/front/pages/login.js b/src/front/pages/login.js deleted file mode 100644 index 5620a99..0000000 --- a/src/front/pages/login.js +++ /dev/null @@ -1,127 +0,0 @@ -const checkNostrExtension = require('../utils/checkNostrExtension'); -const loginService = require('../services/loginService'); -const WarningDiv = require('../components/WarningDiv'); -const { NostrLoginButton } = require('../components/nostrButtons'); -const PopupNotification = require('../components/PopupNotification'); -const constants = require('../../constants'); - -const loginFunction = () => { - const loginButtonArea = document.getElementById('login-button-area'); - const warningsArea = document.getElementById('warnings-area'); - - const successPopup = new PopupNotification({ - parentElement: document.querySelector('body'), - id: 'login-success-popup', - text: '¡Éxito! Te estamos llevando a la app...', - }); - successPopup.render(); - const nostrLoginButton = new NostrLoginButton({ - parentElement: loginButtonArea, - id: 'login-button', - onClickCallback: async () => { - const verifyResponse = await loginService.requestAndRespondLoginChallenge( - { - onRejectedPubKeyCallback: () => { - nostrRejectedWarning.display(); - }, - onRejectedSignatureCallback: () => { - nostrRejectedWarning.display(); - }, - } - ); - - if (verifyResponse.status === 403) { - notRegisteredPubkeyWarning.display(); - } - - if (verifyResponse.ok) { - nostrLoginButton.disable(); - successPopup.display(); - setTimeout(() => { - window.location.href = constants.API_PATHS.home; - }, constants.DEFAULT_REDIRECT_DELAY); - } - }, - }); - nostrLoginButton.render(); - - const notRegisteredPubkeyWarning = new WarningDiv({ - parentElement: warningsArea, - id: 'rejected-public-key', - innerHTML: `

- Ups, esa clave no está registrada en la seca. ¿Quizás estás usando un - perfil equivocado? -

`, - }); - notRegisteredPubkeyWarning.render(); - - const noExtensionWarning = new WarningDiv({ - parentElement: warningsArea, - id: 'no-extension-nudges', - innerHTML: `

- ¡Atención! No se ha encontrado una extensión de Nostr en tu navegador. - Puedes usar: -

-

Firefox

-

- Alby -

-

- nos2x-fox -

-

Chrome

-

- Alby -

-

- nos2x -

`, - }); - noExtensionWarning.render(); - - const nostrRejectedWarning = new WarningDiv({ - parentElement: warningsArea, - id: 'rejected-nostr-nudges', - innerHTML: `

- Ups, parece que no has aceptado que usemos tus claves. Si te has - equivocado, puedes intentarlo de nuevo. -

`, - }); - nostrRejectedWarning.render(); - - window.onload = () => { - checkNostrExtension({ - window, - successCallback: () => { - console.log('Nostr extension present'); - }, - failureCallback: () => { - console.log('Nostr extension not present'); - nostrLoginButton.disable(); - noExtensionWarning.display(); - }, - }); - }; -}; - -loginFunction(); diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js deleted file mode 100644 index 1913e7a..0000000 --- a/src/front/pages/offers.js +++ /dev/null @@ -1,694 +0,0 @@ -const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); -const PublishOfferButton = require('../components/PublishOfferButton'); -const BuyOrSellButtonGroup = require('../components/BuyOrSellButtonGroup'); -const PremiumSelector = require('../components/PremiumSelector'); -const PriceDisplay = require('../components/PriceDisplay'); - -function offersPage() { - const createOfferEventBus = new EventTarget(); - - const publishOfferButton = new PublishOfferButton({ - parentElement: document.getElementById('submit-button-area'), - id: 'button-submit-offer', - onClickCallback: async () => { - await publishOffer(); - await myOffers.getOffersFromApi(); - await myOffers.render(); - }, - }); - publishOfferButton.render(); - - const buyOrSellButtonGroup = new BuyOrSellButtonGroup({ - parentElement: document.getElementById('buy-or-sell-area'), - id: 'button-group-buy-or-sell', - }); - buyOrSellButtonGroup.render(); - - const premiumSelector = new PremiumSelector({ - parentElement: document.getElementById('premium-content-area'), - id: 'premium-selector-area', - eventSink: createOfferEventBus, - }); - premiumSelector.render(); - - const priceDisplay = new PriceDisplay({ - parentElement: document.getElementById('premium-content-area'), - id: 'premium-price-display-area', - premiumProvidingCallback: () => { - return premiumSelector.getPremium(); - }, - priceProvidingCallback: () => { - return Math.floor(Math.random() * (95000 - 70000 + 1) + 70000); - }, - }); - priceDisplay.render(); - createOfferEventBus.addEventListener('premium-changed', () => { - priceDisplay.updatePrices(); - }); - - // ----------- - const navbuttonHome = document.getElementById('navbutton-home'); - const navbuttonOffers = document.getElementById('navbutton-offers'); - - navbuttonHome.addEventListener('click', () => { - window.location.href = '/home'; - }); - - navbuttonOffers.addEventListener('click', () => { - window.location.href = '/offers'; - }); - - const buttonStartCreateOffer = document.getElementById( - 'button-start-create-offer' - ); - const buttonViewMyOffers = document.getElementById('button-view-my-offers'); - const closeOffer = document.getElementById('close-offer'); - const createOfferModalRoot = document.getElementById( - 'create-offer-modal-root' - ); - const viewMyOffersRoot = document.getElementById('view-my-offers-root'); - - const eurAmountInput = document.getElementById('input-eur-amount'); - const btcAmountInput = document.getElementById('input-btc-amount'); - - const placeInput = document.getElementById('place-input'); - const timeInput = document.getElementById('time-input'); - - const onchainCheckbox = document.getElementById('onchain-checkbox'); - const lightningCheckbox = document.getElementById('lightning-checkbox'); - - const btcMethodCheckboxes = [onchainCheckbox, lightningCheckbox]; - - const myTrustedCheckbox = document.getElementById('my-trusted-checkbox'); - const myTrustedTrustedCheckbox = document.getElementById( - 'my-trusted-trusted-checkbox' - ); - const allMembersCheckbox = document.getElementById('all-members-checkbox'); - - const bigNotesAcceptedCheckbox = document.getElementById( - 'large-bills-checkbox' - ); - - const offerCreatedPopup = document.getElementById( - 'offer-created-confirmation' - ); - const offerDeletedPopup = document.getElementById( - 'offer-deleted-confirmation' - ); - - const ownOffersContainer = document.getElementById('own-offers-container'); - - function toggleCreateOfferModal() { - createOfferModalRoot.classList.toggle('shown'); - } - - function toggleViewMyOffersPanel() { - viewMyOffersRoot.style.display = - viewMyOffersRoot.style.display === 'block' ? 'none' : 'block'; - } - - function readIntFromEurAmountInput() { - const eurAmountFieldValue = eurAmountInput.value; - const regularExpression = /([\d\s]+)/; - const matchResult = eurAmountFieldValue.match(regularExpression); - - if (!matchResult) { - return null; - } - - const numberString = matchResult[1]; - const cleanInputNumber = parseInt(numberString.replace(/\s/gi, '')); - - return cleanInputNumber; - } - - function validateAndFormatEurAmountInput() { - const cleanInputNumber = readIntFromEurAmountInput(); - eurAmountInput.classList.remove('input-is-valid', 'input-is-invalid'); - if (cleanInputNumber) { - eurAmountInput.value = formatNumberWithSpaces(cleanInputNumber); - eurAmountInput.classList.add('input-is-valid'); - return; - } - - eurAmountInput.classList.add('input-is-invalid'); - } - - function updateBtcInput() { - const eurToSatRate = 1021; - const cleanEurAmount = readIntFromEurAmountInput(); - - const satsAmount = cleanEurAmount * eurToSatRate; - const formattedSatsAmount = formatNumberWithSpaces(satsAmount); - btcAmountInput.value = formattedSatsAmount; - } - - function validateBitcoinMethodCheckboxes(clickedCheckbox) { - let checkedCount = btcMethodCheckboxes.filter((cb) => cb.checked).length; - if (checkedCount === 0) { - clickedCheckbox.checked = true; - } - } - - function applyTrustCheckboxConstraints(pressedCheckbox) { - if (pressedCheckbox === myTrustedTrustedCheckbox) { - console.log('first case!'); - if (!myTrustedTrustedCheckbox.checked && allMembersCheckbox.checked) { - allMembersCheckbox.checked = false; - } - } - - if (pressedCheckbox === allMembersCheckbox) { - console.log('second case!'); - if (!myTrustedTrustedCheckbox.checked && allMembersCheckbox.checked) { - myTrustedTrustedCheckbox.checked = true; - } - } - } - - async function publishOffer() { - const wants = buyOrSellButtonGroup.wants(); - - const premium = premiumSelector.getPremium(); - const trade_amount_eur = eurAmountInput.value; - const location_details = placeInput.value; - const time_availability_details = timeInput.value; - const is_onchain_accepted = onchainCheckbox.checked; - const is_lightning_accepted = lightningCheckbox.checked; - const show_offer_to_trusted = myTrustedCheckbox.checked; - const show_offer_to_trusted_trusted = myTrustedTrustedCheckbox.checked; - const show_offer_to_all_members = allMembersCheckbox.checked; - const are_big_notes_accepted = bigNotesAcceptedCheckbox.checked; - - const offerDetails = { - wants, - premium, - trade_amount_eur, - location_details, - time_availability_details, - is_onchain_accepted, - is_lightning_accepted, - show_offer_to_trusted, - show_offer_to_trusted_trusted, - show_offer_to_all_members, - are_big_notes_accepted, - }; - - await fetch('/api/offer', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ offerDetails }), - }); - - toggleOfferCreatedAlert(); - - toggleCreateOfferModal(); - } - - function toggleOfferCreatedAlert() { - offerCreatedPopup.classList.remove('max-size-zero'); - offerCreatedPopup.classList.add('revealed'); - setTimeout(() => { - offerCreatedPopup.classList.remove('revealed'); - }, 3000); - setTimeout(() => { - offerCreatedPopup.classList.add('max-size-zero'); - }, 4000); - } - - function toggleOfferDeletedAlert() { - offerDeletedPopup.classList.remove('max-size-zero'); - offerDeletedPopup.classList.add('revealed'); - setTimeout(() => { - offerDeletedPopup.classList.remove('revealed'); - }, 3000); - setTimeout(() => { - offerDeletedPopup.classList.add('max-size-zero'); - }, 4000); - } - - class Offer { - constructor(offerData) { - this.uuid = offerData.uuid; - this.public_key = offerData.public_key; - this.wants = offerData.wants; - this.premium = offerData.premium; - this.trade_amount_eur = offerData.trade_amount_eur; - this.location_details = offerData.location_details; - this.time_availability_details = offerData.time_availability_details; - this.show_offer_to_trusted = offerData.show_offer_to_trusted; - this.show_offer_to_trusted_trusted = - offerData.show_offer_to_trusted_trusted; - this.show_offer_to_all_members = offerData.show_offer_to_all_members; - this.is_onchain_accepted = offerData.is_onchain_accepted; - this.is_lightning_accepted = offerData.is_lightning_accepted; - this.are_big_notes_accepted = offerData.are_big_notes_accepted; - this.created_at = offerData.created_at; - this.last_updated_at = offerData.last_updated_at; - } - - buildHTML() { - const offerCard = document.createElement('div'); - offerCard.classList.add('myoffer-card'); - offerCard.classList.add('shadowed-round-area'); - - const tradeDescDiv = document.createElement('div'); - tradeDescDiv.classList.add('trade-desc'); - - const youBuyText = document.createElement('p'); - youBuyText.classList.add('offer-card-content-title'); - youBuyText.innerText = 'Compras'; - tradeDescDiv.append(youBuyText); - - const youBuyData = document.createElement('p'); - youBuyData.classList.add('offer-card-content-data'); - if (this.wants === 'BTC') { - youBuyData.innerText = `${this.trade_amount_eur * 1021} sats`; - } - if (this.wants === 'EUR') { - youBuyData.innerText = `${this.trade_amount_eur} €`; - } - tradeDescDiv.append(youBuyData); - - const youSellText = document.createElement('p'); - youSellText.id = 'you-sell-title'; - youSellText.classList.add('offer-card-content-title'); - youSellText.innerText = 'Vendes'; - tradeDescDiv.append(youSellText); - - const youSellData = document.createElement('p'); - youSellData.classList.add('offer-card-content-data'); - if (this.wants === 'BTC') { - youSellData.innerText = `${this.trade_amount_eur} €`; - } - if (this.wants === 'EUR') { - youSellData.innerText = `${this.trade_amount_eur * 1021} sats`; - } - tradeDescDiv.append(youSellData); - - const premiumDescDiv = document.createElement('div'); - premiumDescDiv.classList.add('premium-desc'); - - const premiumTitle = document.createElement('p'); - premiumTitle.classList.add('offer-card-content-title'); - premiumTitle.innerText = 'Premium'; - premiumDescDiv.append(premiumTitle); - - const premiumData = document.createElement('p'); - premiumData.classList.add('offer-card-content-data'); - premiumData.innerText = `${this.premium * 100} %`; - premiumDescDiv.append(premiumData); - - const offerPriceTitle = document.createElement('p'); - offerPriceTitle.classList.add('offer-card-content-title'); - offerPriceTitle.innerText = 'Precio oferta'; - premiumDescDiv.append(offerPriceTitle); - - const offerPriceData = document.createElement('p'); - offerPriceData.classList.add('offer-card-content-data'); - offerPriceData.innerText = `90000 €/BTC`; - premiumDescDiv.append(offerPriceData); - - const marketPriceTitle = document.createElement('p'); - marketPriceTitle.classList.add('offer-card-content-title'); - marketPriceTitle.innerText = 'Precio mercado'; - premiumDescDiv.append(marketPriceTitle); - - const marketPriceData = document.createElement('p'); - marketPriceData.innerText = `88000 €/BTC`; - premiumDescDiv.append(marketPriceData); - - const whereDescDiv = document.createElement('div'); - whereDescDiv.classList.add('where-desc'); - - const whereDescTitle = document.createElement('p'); - whereDescTitle.classList.add('offer-card-content-title'); - whereDescTitle.innerText = 'Dónde'; - whereDescDiv.append(whereDescTitle); - - const whereDescData = document.createElement('p'); - whereDescData.classList.add('offer-long-text'); - whereDescData.innerText = `${this.location_details}`; - whereDescDiv.append(whereDescData); - - const whenDescDiv = document.createElement('div'); - whenDescDiv.classList.add('when-desc'); - - const whenDescTitle = document.createElement('p'); - whenDescTitle.classList.add('offer-card-content-title'); - whenDescTitle.innerText = 'Cúando'; - whenDescDiv.append(whenDescTitle); - - const whenDescData = document.createElement('p'); - whenDescData.classList.add('offer-long-text'); - whenDescData.innerText = `${this.time_availability_details}`; - whenDescDiv.append(whenDescData); - - const bitcoinMethodsDiv = document.createElement('div'); - bitcoinMethodsDiv.classList.add('bitcoin-methods-desc'); - - const bitcoinMethodsTitle = document.createElement('p'); - bitcoinMethodsTitle.classList.add('offer-card-content-title'); - bitcoinMethodsTitle.innerText = 'Protocolos Bitcoin aceptados'; - bitcoinMethodsDiv.append(bitcoinMethodsTitle); - - const onchainAcceptedContainer = document.createElement('div'); - onchainAcceptedContainer.classList.add('left-icon-checkboxed-field'); - if (this.is_onchain_accepted) { - const onchainIcon = document.createElement('img'); - onchainIcon.src = '/img/chains-lasecagold.svg'; - const onchainText = document.createElement('p'); - onchainText.innerText = 'Onchain'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon); - } else { - const onchainIcon = document.createElement('img'); - onchainIcon.src = '/img/chains-gray.svg'; - const onchainText = document.createElement('p'); - onchainText.innerText = 'Onchain'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon); - } - const lightningAcceptedContainer = document.createElement('div'); - - lightningAcceptedContainer.classList.add('left-icon-checkboxed-field'); - if (this.is_lightning_accepted) { - const lightningIcon = document.createElement('img'); - lightningIcon.src = '/img/bolt-lightning-lasecagold.svg'; - const lightningText = document.createElement('p'); - lightningText.innerText = 'Lightning'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - lightningAcceptedContainer.append( - lightningIcon, - lightningText, - checkIcon - ); - } else { - const lightningIcon = document.createElement('img'); - lightningIcon.src = '/img/bolt-lightning-gray.svg'; - const lightningText = document.createElement('p'); - lightningText.innerText = 'Lightning'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - lightningAcceptedContainer.append( - lightningIcon, - lightningText, - checkIcon - ); - } - - bitcoinMethodsDiv.append( - onchainAcceptedContainer, - lightningAcceptedContainer - ); - - const visibilityDiv = document.createElement('div'); - visibilityDiv.classList.add('visibility-desc'); - - const visibilityTitle = document.createElement('p'); - visibilityTitle.classList.add('offer-card-content-title'); - visibilityTitle.innerText = 'Visibilidad'; - visibilityDiv.append(visibilityTitle); - - const showOfferToTrustedContainer = document.createElement('div'); - showOfferToTrustedContainer.classList.add('right-icon-checkboxed-field'); - - if (this.show_offer_to_trusted) { - const showOfferToTrustedIcon = document.createElement('img'); - showOfferToTrustedIcon.src = '/img/user-lasecagold.svg'; - const showOfferToTrustedText = document.createElement('p'); - showOfferToTrustedText.innerText = 'Confiados'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - showOfferToTrustedContainer.append( - showOfferToTrustedIcon, - showOfferToTrustedText, - checkIcon - ); - } else { - const showOfferToTrustedIcon = document.createElement('img'); - showOfferToTrustedIcon.src = '/img/user-gray.svg'; - const showOfferToTrustedText = document.createElement('p'); - showOfferToTrustedText.innerText = 'Confiados'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - showOfferToTrustedContainer.append( - showOfferToTrustedIcon, - showOfferToTrustedText, - checkIcon - ); - } - - const showOfferToTrustedTrustedContainer = document.createElement('div'); - showOfferToTrustedTrustedContainer.classList.add( - 'right-icon-checkboxed-field' - ); - - if (this.show_offer_to_trusted_trusted) { - const showOfferToTrustedTrustedIcon = document.createElement('img'); - showOfferToTrustedTrustedIcon.src = '/img/user-group-lasecagold.svg'; - const showOfferToTrustedTrustedText = document.createElement('p'); - showOfferToTrustedTrustedText.innerText = 'Sus confiados'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - showOfferToTrustedTrustedContainer.append( - showOfferToTrustedTrustedIcon, - showOfferToTrustedTrustedText, - checkIcon - ); - } else { - const showOfferToTrustedTrustedIcon = document.createElement('img'); - showOfferToTrustedTrustedIcon.src = '/img/user-group-gray.svg'; - const showOfferToTrustedTrustedText = document.createElement('p'); - showOfferToTrustedTrustedText.innerText = 'Sus confiados'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - showOfferToTrustedTrustedContainer.append( - showOfferToTrustedTrustedIcon, - showOfferToTrustedTrustedText, - checkIcon - ); - } - - const showOfferToAllMembersContainer = document.createElement('div'); - showOfferToAllMembersContainer.classList.add( - 'right-icon-checkboxed-field' - ); - - if (this.show_offer_to_all_members) { - const showOfferToAllMembersIcon = document.createElement('img'); - showOfferToAllMembersIcon.src = '/img/many-users-lasecagold.svg'; - const showOfferToAllMembersText = document.createElement('p'); - showOfferToAllMembersText.innerText = 'Todos'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - showOfferToAllMembersContainer.append( - showOfferToAllMembersIcon, - showOfferToAllMembersText, - checkIcon - ); - } else { - const showOfferToAllMembersIcon = document.createElement('img'); - showOfferToAllMembersIcon.src = '/img/many-users-gray.svg'; - const showOfferToAllMembersText = document.createElement('p'); - showOfferToAllMembersText.innerText = 'Todos'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - showOfferToAllMembersContainer.append( - showOfferToAllMembersIcon, - showOfferToAllMembersText, - checkIcon - ); - } - visibilityDiv.append( - showOfferToTrustedContainer, - showOfferToTrustedTrustedContainer, - showOfferToAllMembersContainer - ); - - const otherOfferFeaturesDiv = document.createElement('div'); - otherOfferFeaturesDiv.classList.add('other-desc'); - - const otherOfferFeaturesTitle = document.createElement('p'); - otherOfferFeaturesTitle.classList.add('offer-card-content-title'); - otherOfferFeaturesTitle.innerText = 'Otros'; - otherOfferFeaturesDiv.append(otherOfferFeaturesTitle); - - const areBigNotesAcceptedContainer = document.createElement('div'); - areBigNotesAcceptedContainer.classList.add('left-icon-checkboxed-field'); - - if (this.are_big_notes_accepted) { - const areBigNotesAcceptedIcon = document.createElement('img'); - areBigNotesAcceptedIcon.src = '/img/eur-bill-lasecagold.svg'; - const areBigNotesAcceptedText = document.createElement('p'); - areBigNotesAcceptedText.innerText = 'Billetes grandes'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-check-green.svg'; - - areBigNotesAcceptedContainer.append( - areBigNotesAcceptedIcon, - areBigNotesAcceptedText, - checkIcon - ); - } else { - const areBigNotesAcceptedIcon = document.createElement('img'); - areBigNotesAcceptedIcon.src = '/img/eur-bill-gray.svg'; - const areBigNotesAcceptedText = document.createElement('p'); - areBigNotesAcceptedText.innerText = 'Billetes grandes'; - const checkIcon = document.createElement('img'); - checkIcon.src = '/img/circle-xmark-gray.svg'; - - areBigNotesAcceptedContainer.append( - areBigNotesAcceptedIcon, - areBigNotesAcceptedText, - checkIcon - ); - } - - otherOfferFeaturesDiv.append(areBigNotesAcceptedContainer); - - const actionButtonsArea = document.createElement('p'); - actionButtonsArea.classList.add('offer-action-buttons-area'); - - const editActionArea = document.createElement('div'); - editActionArea.classList.add('offer-action-area'); - editActionArea.classList.add('subtle-box'); - const editActionIcon = document.createElement('img'); - editActionIcon.src = '/img/edit.svg'; - const editActionText = document.createElement('p'); - editActionText.innerText = 'Editar'; - editActionArea.append(editActionIcon, editActionText); - - const deleteActionArea = document.createElement('div'); - deleteActionArea.classList.add('offer-action-area'); - deleteActionArea.classList.add('subtle-box'); - const deleteActionIcon = document.createElement('img'); - deleteActionIcon.src = '/img/trash-can-darkred.svg'; - const deleteActionText = document.createElement('p'); - deleteActionText.innerText = 'Eliminar'; - deleteActionArea.append(deleteActionIcon, deleteActionText); - deleteActionArea.addEventListener('click', async () => { - await deleteOfferByUuid(this.uuid); - await myOffers.getOffersFromApi(); - await myOffers.render(); - toggleOfferDeletedAlert(); - }); - - actionButtonsArea.append(editActionArea, deleteActionArea); - - offerCard.append( - tradeDescDiv, - premiumDescDiv, - whereDescDiv, - whenDescDiv, - bitcoinMethodsDiv, - visibilityDiv, - otherOfferFeaturesDiv, - actionButtonsArea - ); - - return offerCard; - } - } - - class MyOffers { - constructor(ownOffersContainerElement) { - this.ownOffersContainerElement = ownOffersContainerElement; - this.offers = []; - } - - async getOffersFromApi() { - const offersResponse = await fetch('/api/publickey-offers'); - - this.offers = []; - - const offersData = (await offersResponse.json()).data; - if (offersResponse.ok) { - for (const record of offersData) { - this.offers.push(new Offer(record)); - } - } - } - - async render() { - if (this.offers.length === 0) { - this.ownOffersContainerElement.innerHTML = - '

Vaya, no hay nada por aquí...

'; - return; - } - this.ownOffersContainerElement.innerHTML = ''; - - for (const someOffer of this.offers) { - this.ownOffersContainerElement.append(someOffer.buildHTML()); - } - } - } - - async function deleteOfferByUuid(offerUuid) { - await fetch(`/api/offer/${offerUuid}`, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - }); - } - - buttonStartCreateOffer.addEventListener('click', () => { - toggleCreateOfferModal(); - }); - - buttonViewMyOffers.addEventListener('click', async () => { - await myOffers.getOffersFromApi(); - await myOffers.render(); - toggleViewMyOffersPanel(); - }); - - closeOffer.addEventListener('click', () => { - toggleCreateOfferModal(); - }); - - eurAmountInput.addEventListener('blur', () => { - validateAndFormatEurAmountInput(); - updateBtcInput(); - }); - - eurAmountInput.addEventListener('input', () => { - eurAmountInput.value = eurAmountInput.value.replace(/[^0-9]/g, ''); - updateBtcInput(); - }); - - for (const btcMethodCheckbox of btcMethodCheckboxes) { - btcMethodCheckbox.addEventListener('click', () => { - validateBitcoinMethodCheckboxes(btcMethodCheckbox); - }); - } - - myTrustedTrustedCheckbox.addEventListener('click', () => { - applyTrustCheckboxConstraints(myTrustedTrustedCheckbox); - }); - - allMembersCheckbox.addEventListener('click', () => { - applyTrustCheckboxConstraints(allMembersCheckbox); - }); - - updateBtcInput(); - - const myOffers = new MyOffers(ownOffersContainer); -} - -offersPage(); diff --git a/src/front/utils/checkNostrExtension.js b/src/front/utils/checkNostrExtension.js deleted file mode 100644 index b16d293..0000000 --- a/src/front/utils/checkNostrExtension.js +++ /dev/null @@ -1,9 +0,0 @@ -function checkNostrExtension({ window, successCallback, failureCallback }) { - if (!window.nostr) { - failureCallback(); - } else { - successCallback(); - } -} - -module.exports = checkNostrExtension; diff --git a/src/middlewares/attachPublicKeyMiddleware.js b/src/middlewares/attachPublicKeyMiddleware.js index 05c7758..c6ad835 100644 --- a/src/middlewares/attachPublicKeyMiddleware.js +++ b/src/middlewares/attachPublicKeyMiddleware.js @@ -1,20 +1,14 @@ -class AttachPublicKeyMiddlewareProvider { - constructor({ sessionService }) { - this.sessionService = sessionService; - } +const sessionService = require('../services/sessionService'); - provide() { - return async (req, res, next) => { - const publicKey = await this.sessionService.getPublicKeyRelatedToSession( - req.cookies.sessionUuid - ); +async function attachPublicKeyMiddleware(req, res, next) { + const publicKey = await sessionService.getPublicKeyRelatedToSession( + req.cookies.sessionUuid + ); - if (publicKey) { - req.cookies.publicKey = publicKey; - } - next(); - }; + if (publicKey) { + req.cookies.publicKey = publicKey; } + next(); } -module.exports = AttachPublicKeyMiddlewareProvider; +module.exports = attachPublicKeyMiddleware; diff --git a/src/middlewares/createSessionMiddleware.js b/src/middlewares/createSessionMiddleware.js deleted file mode 100644 index 53e15dd..0000000 --- a/src/middlewares/createSessionMiddleware.js +++ /dev/null @@ -1,39 +0,0 @@ -const uuid = require('uuid'); - -class CreateSessionMiddlewareProvider { - constructor({ constants, sessionService }) { - this.constants = constants; - this.sessionService = sessionService; - } - - provide() { - return async (req, res, next) => { - const sessionUuid = req.cookies.sessionUuid; - - if (!sessionUuid) { - const newSession = await this.setAndPersistNewSession(res); - req.cookies.sessionUuid = newSession.uuid; - } - - if (sessionUuid) { - if (!(await this.sessionService.isSessionValid(sessionUuid))) { - const newSession = await this.setAndPersistNewSession(res); - req.cookies.sessionUuid = newSession.uuid; - } - } - - next(); - }; - } - - async setAndPersistNewSession(res) { - const sessionUuid = uuid.v7(); - res.cookie('sessionUuid', sessionUuid, { - httpOnly: true, - maxAge: this.constants.DEFAULT_SESSION_DURATION_SECONDS * 1000, - }); - return await this.sessionService.createSession(sessionUuid); - } -} - -module.exports = CreateSessionMiddlewareProvider; diff --git a/src/middlewares/index.js b/src/middlewares/index.js deleted file mode 100644 index 1a316a1..0000000 --- a/src/middlewares/index.js +++ /dev/null @@ -1,55 +0,0 @@ -class MiddlewaresProvider { - constructor({ constants, sessionService, profileService }) { - this.constants = constants; - this.sessionService = sessionService; - this.profileService = profileService; - } - - provide() { - const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); - const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider({ - sessionService: this.sessionService, - }).provide(); - - const CreateSessionMiddlewareProvider = require('./createSessionMiddleware'); - const createSessionMiddleware = new CreateSessionMiddlewareProvider({ - constants: this.constants, - sessionService: this.sessionService, - }).provide(); - - const RejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); - const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware( - { - sessionService: this.sessionService, - } - ).provide(); - - const RedirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); - const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized({ - sessionService: this.sessionService, - }).provide(); - - const RedirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); - const redirectIfNotAuthorizedMiddleware = - new RedirectIfNotAuthorizedMiddleware({ - sessionService: this.sessionService, - }).provide(); - - const RedirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); - const redirectIfMissingProfileDetailsMiddleware = - new RedirectIfMissingProfileDetailsMiddleware({ - profileService: this.profileService, - }).provide(); - - return { - redirectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - redirectIfMissingProfileDetailsMiddleware, - redirectHomeIfAuthorized, - rejectIfNotAuthorizedMiddleware, - createSessionMiddleware, - }; - } -} - -module.exports = MiddlewaresProvider; diff --git a/src/middlewares/redirectHomeIfAuthorized.js b/src/middlewares/redirectHomeIfAuthorized.js index f3aedf2..9619c68 100644 --- a/src/middlewares/redirectHomeIfAuthorized.js +++ b/src/middlewares/redirectHomeIfAuthorized.js @@ -1,18 +1,10 @@ -class RedirectHomeIfAuthorized { - constructor({ sessionService }) { - this.sessionService = sessionService; - } +const sessionService = require('../services/sessionService'); - provide() { - return async (req, res, next) => { - if ( - await this.sessionService.isSessionAuthorized(req.cookies.sessionUuid) - ) { - return res.redirect('/home'); - } - next(); - }; +async function redirectHomeIfAuthorized(req, res, next) { + if (await sessionService.isSessionAuthorized(req.cookies.sessionUuid)) { + return res.redirect('/home'); } + next(); } -module.exports = RedirectHomeIfAuthorized; +module.exports = redirectHomeIfAuthorized; diff --git a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js index d7e758f..934048d 100644 --- a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js +++ b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js @@ -1,17 +1,12 @@ -class RedirectIfMissingProfileDetailsMiddleware { - constructor({ profileService }) { - this.profileService = profileService; +const profileService = require('../services/profileService'); + +async function redirectIfMissingProfileDetailsMiddleware(req, res, next) { + const publicKey = req.cookies.publicKey; + if (!(await profileService.areProfileDetailsComplete(publicKey))) { + res.redirect('/createProfile'); } - provide() { - return async (req, res, next) => { - const publicKey = req.cookies.publicKey; - if (!(await this.profileService.areProfileDetailsComplete(publicKey))) { - res.redirect('/createProfile'); - } - - next(); - }; - } + next(); } -module.exports = RedirectIfMissingProfileDetailsMiddleware; + +module.exports = redirectIfMissingProfileDetailsMiddleware; diff --git a/src/middlewares/redirectIfNotAuthorizedMiddleware.js b/src/middlewares/redirectIfNotAuthorizedMiddleware.js index 353f21c..f4b9e47 100644 --- a/src/middlewares/redirectIfNotAuthorizedMiddleware.js +++ b/src/middlewares/redirectIfNotAuthorizedMiddleware.js @@ -1,20 +1,10 @@ -class RedirectIfNotAuthorizedMiddleware { - constructor({ sessionService }) { - this.sessionService = sessionService; - } +const sessionService = require('../services/sessionService'); - provide() { - return async (req, res, next) => { - if ( - !(await this.sessionService.isSessionAuthorized( - req.cookies.sessionUuid - )) - ) { - return res.redirect('/login'); - } - next(); - }; +async function redirectIfNotAuthorizedMiddleware(req, res, next) { + if (!(await sessionService.isSessionAuthorized(req.cookies.sessionUuid))) { + return res.redirect('/login'); } + next(); } -module.exports = RedirectIfNotAuthorizedMiddleware; +module.exports = redirectIfNotAuthorizedMiddleware; diff --git a/src/middlewares/rejectIfNotAuthorizedMiddleware.js b/src/middlewares/rejectIfNotAuthorizedMiddleware.js index 548830e..85e67ad 100644 --- a/src/middlewares/rejectIfNotAuthorizedMiddleware.js +++ b/src/middlewares/rejectIfNotAuthorizedMiddleware.js @@ -1,23 +1,13 @@ -class RejectIfNotAuthorizedMiddleware { - constructor({ sessionService }) { - this.sessionService = sessionService; - } +const sessionService = require('../services/sessionService'); - provide() { - return async (req, res, next) => { - if ( - !(await this.sessionService.isSessionAuthorized( - req.cookies.sessionUuid - )) - ) { - return res.status(403).json({ - success: false, - message: 'Your session is not authorized.', - }); - } - next(); - }; +async function rejectIfNotAuthorizedMiddleware(req, res, next) { + if (!(await sessionService.isSessionAuthorized(req.cookies.sessionUuid))) { + return res.status(403).json({ + success: false, + message: 'Your session is not authorized.', + }); } + next(); } -module.exports = RejectIfNotAuthorizedMiddleware; +module.exports = rejectIfNotAuthorizedMiddleware; diff --git a/src/middlewares/sessionMiddleware.js b/src/middlewares/sessionMiddleware.js new file mode 100644 index 0000000..8d181d6 --- /dev/null +++ b/src/middlewares/sessionMiddleware.js @@ -0,0 +1,33 @@ +const uuid = require('uuid'); + +const sessionService = require('../services/sessionService'); +const constants = require('../constants'); + +async function setAndPersistNewSession(res) { + const sessionUuid = uuid.v7(); + res.cookie('sessionUuid', sessionUuid, { + httpOnly: true, + maxAge: constants.DEFAULT_SESSION_DURATION_SECONDS * 1000, + }); + return await sessionService.createSession(sessionUuid); +} + +async function createSessionMiddleware(req, res, next) { + const sessionUuid = req.cookies.sessionUuid; + + if (!sessionUuid) { + const newSession = await setAndPersistNewSession(res); + req.cookies.sessionUuid = newSession.uuid; + } + + if (sessionUuid) { + if (!(await sessionService.isSessionValid(sessionUuid))) { + const newSession = await setAndPersistNewSession(res); + req.cookies.sessionUuid = newSession.uuid; + } + } + + next(); +} + +module.exports = createSessionMiddleware; diff --git a/src/models/AppInviteCreated.js b/src/models/AppInviteCreated.js index 4062fb5..49967b3 100644 --- a/src/models/AppInviteCreated.js +++ b/src/models/AppInviteCreated.js @@ -1,34 +1,27 @@ -class AppInviteCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const AppInviteCreated = this.sequelize.define( - 'AppInviteCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - inviter_pub_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'app_invite_created', - } - ); - return AppInviteCreated; +const AppInviteCreated = sequelize.define( + 'AppInviteCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + inviter_pub_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'app_invite_created', } -} +); -module.exports = AppInviteCreatedProvider; +module.exports = AppInviteCreated; diff --git a/src/models/ContactDetailsSet.js b/src/models/ContactDetailsSet.js index 4a1a6a1..8722e89 100644 --- a/src/models/ContactDetailsSet.js +++ b/src/models/ContactDetailsSet.js @@ -1,38 +1,31 @@ -class ContactDetailsSetProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const ContactDetailsSet = this.sequelize.define( - 'ContactDetailsSet', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - encrypted_contact_details: { - type: this.DataTypes.TEXT, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'contact_details_set', - } - ); - return ContactDetailsSet; +const ContactDetailsSet = sequelize.define( + 'ContactDetailsSet', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + encrypted_contact_details: { + type: DataTypes.TEXT, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'contact_details_set', } -} +); -module.exports = ContactDetailsSetProvider; +module.exports = ContactDetailsSet; diff --git a/src/models/LoginChallengeCompleted.js b/src/models/LoginChallengeCompleted.js index bce56e8..b4774bb 100644 --- a/src/models/LoginChallengeCompleted.js +++ b/src/models/LoginChallengeCompleted.js @@ -1,38 +1,31 @@ -class LoginChallengeCompletedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const LoginChallengeCompleted = this.sequelize.define( - 'LoginChallengeCompleted', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_completed_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'login_challenge_completed', - } - ); - return LoginChallengeCompleted; +const LoginChallengeCompleted = sequelize.define( + 'LoginChallengeCompleted', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + nostr_challenge_completed_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'login_challenge_completed', } -} +); -module.exports = LoginChallengeCompletedProvider; +module.exports = LoginChallengeCompleted; diff --git a/src/models/LoginChallengeCreated.js b/src/models/LoginChallengeCreated.js index 3d29c16..c1c1792 100644 --- a/src/models/LoginChallengeCreated.js +++ b/src/models/LoginChallengeCreated.js @@ -1,34 +1,27 @@ -class LoginChallengeCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const LoginChallengeCreated = this.sequelize.define( - 'LoginChallengeCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'login_challenge_created', - } - ); - return LoginChallengeCreated; +const LoginChallengeCreated = sequelize.define( + 'LoginChallengeCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + nostr_challenge_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'login_challenge_created', } -} +); -module.exports = LoginChallengeCreatedProvider; +module.exports = LoginChallengeCreated; diff --git a/src/models/NostrChallengeCompleted.js b/src/models/NostrChallengeCompleted.js index e569097..a656edd 100644 --- a/src/models/NostrChallengeCompleted.js +++ b/src/models/NostrChallengeCompleted.js @@ -1,43 +1,35 @@ -class NostrChallengeCompletedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const NostrChallengeCompleted = this.sequelize.define( - 'NostrChallengeCompleted', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - challenge: { - type: this.DataTypes.STRING, - allowNull: false, - unique: true, - }, - signed_event: { - type: this.DataTypes.JSONB, - allowNull: false, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'nostr_challenge_completed', - } - ); - return NostrChallengeCompleted; +const NostrChallengeCompleted = sequelize.define( + 'NostrChallengeCompleted', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + challenge: { + type: DataTypes.STRING, + allowNull: false, + }, + signed_event: { + type: DataTypes.JSONB, + allowNull: false, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'nostr_challenge_completed', } -} +); -module.exports = NostrChallengeCompletedProvider; +module.exports = NostrChallengeCompleted; diff --git a/src/models/NostrChallengeCreated.js b/src/models/NostrChallengeCreated.js index f35ddc7..97a4c64 100644 --- a/src/models/NostrChallengeCreated.js +++ b/src/models/NostrChallengeCreated.js @@ -1,39 +1,31 @@ -class NostrChallengeCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const NostrChallengeCreated = this.sequelize.define( - 'NostrChallengeCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - challenge: { - type: this.DataTypes.STRING, - allowNull: false, - unique: true, - }, - expires_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'nostr_challenge_created', - } - ); - return NostrChallengeCreated; +const NostrChallengeCreated = sequelize.define( + 'NostrChallengeCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + challenge: { + type: DataTypes.STRING, + allowNull: false, + }, + expires_at: { + type: DataTypes.DATE, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'nostr_challenge_created', } -} +); -module.exports = NostrChallengeCreatedProvider; +module.exports = NostrChallengeCreated; diff --git a/src/models/NymSet.js b/src/models/NymSet.js index 4a9f26e..d59a310 100644 --- a/src/models/NymSet.js +++ b/src/models/NymSet.js @@ -1,38 +1,31 @@ -class NymSetProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const NymSet = this.sequelize.define( - 'NymSet', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - nym: { - type: this.DataTypes.TEXT, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'nym_set', - } - ); - return NymSet; +const NymSet = sequelize.define( + 'NymSet', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + nym: { + type: DataTypes.TEXT, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'nym_set', } -} +); -module.exports = NymSetProvider; +module.exports = NymSet; diff --git a/src/models/OfferCreated.js b/src/models/OfferCreated.js index 13eeb54..fd67249 100644 --- a/src/models/OfferCreated.js +++ b/src/models/OfferCreated.js @@ -1,34 +1,27 @@ -class OfferCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const OfferCreated = this.sequelize.define( - 'OfferCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'offer_created', - } - ); - return OfferCreated; +const OfferCreated = sequelize.define( + 'OfferCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'offer_created', } -} +); -module.exports = OfferCreatedProvider; +module.exports = OfferCreated; diff --git a/src/models/OfferDeleted.js b/src/models/OfferDeleted.js deleted file mode 100644 index 019c61f..0000000 --- a/src/models/OfferDeleted.js +++ /dev/null @@ -1,34 +0,0 @@ -class OfferDeletedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } - - provide() { - const OfferDeleted = this.sequelize.define( - 'OfferDeleted', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - offer_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'offer_deleted', - } - ); - return OfferDeleted; - } -} - -module.exports = OfferDeletedProvider; diff --git a/src/models/OfferDetailsSet.js b/src/models/OfferDetailsSet.js index d2023d7..20ac456 100644 --- a/src/models/OfferDetailsSet.js +++ b/src/models/OfferDetailsSet.js @@ -1,78 +1,71 @@ -class OfferDetailsSetProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const OfferDetailsSet = this.sequelize.define( - 'OfferDetailsSet', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - offer_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - wants: { - type: this.DataTypes.STRING, - allowNull: false, - }, - premium: { - type: this.DataTypes.DECIMAL(5, 2), - allowNull: false, - }, - trade_amount_eur: { - type: this.DataTypes.INTEGER, - allowNull: false, - }, - location_details: { - type: this.DataTypes.TEXT, - allowNull: false, - }, - time_availability_details: { - type: this.DataTypes.TEXT, - allowNull: false, - }, - show_offer_to_trusted: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - show_offer_to_trusted_trusted: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - show_offer_to_all_members: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - is_onchain_accepted: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - is_lightning_accepted: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - are_big_notes_accepted: { - type: this.DataTypes.BOOLEAN, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'offer_details_set', - } - ); - return OfferDetailsSet; +const OfferDetailsSet = sequelize.define( + 'OfferDetailsSet', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + offer_uuid: { + type: DataTypes.STRING, + allowNull: false, + }, + wants: { + type: DataTypes.STRING, + allowNull: false, + }, + premium: { + type: DataTypes.DECIMAL(5, 2), + allowNull: false, + }, + trade_amount_eur: { + type: DataTypes.INTEGER, + allowNull: false, + }, + location_details: { + type: DataTypes.TEXT, + allowNull: false, + }, + time_availability_details: { + type: DataTypes.TEXT, + allowNull: false, + }, + show_offer_to_trusted: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + show_offer_to_trusted_trusted: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + show_offer_to_all_members: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + is_onchain_accepted: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + is_lightning_accepted: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + are_big_notes_accepted: { + type: DataTypes.BOOLEAN, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'offer_details_set', } -} +); -module.exports = OfferDetailsSetProvider; +module.exports = OfferDetailsSet; diff --git a/src/models/SessionCreated.js b/src/models/SessionCreated.js index a97c1fb..8d0de50 100644 --- a/src/models/SessionCreated.js +++ b/src/models/SessionCreated.js @@ -1,34 +1,27 @@ -class SessionCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const SessionCreated = this.sequelize.define( - 'SessionCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - expires_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'session_created', - } - ); - return SessionCreated; +const SessionCreated = sequelize.define( + 'SessionCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + expires_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'session_created', } -} +); -module.exports = SessionCreatedProvider; +module.exports = SessionCreated; diff --git a/src/models/SessionRelatedToPublickey.js b/src/models/SessionRelatedToPublickey.js index 321da32..4802267 100644 --- a/src/models/SessionRelatedToPublickey.js +++ b/src/models/SessionRelatedToPublickey.js @@ -1,37 +1,31 @@ -class SessionRelatedToPublickeyProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const SessionRelatedToPublickey = this.sequelize.define( - 'SessionRelatedToPublickey', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - session_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'session_related_to_public_key', - } - ); - return SessionRelatedToPublickey; +const SessionRelatedToPublickey = sequelize.define( + 'SessionRelatedToPublickey', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + session_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'session_related_to_public_key', } -} -module.exports = SessionRelatedToPublickeyProvider; +); + +module.exports = SessionRelatedToPublickey; diff --git a/src/models/SignUpChallengeCompleted.js b/src/models/SignUpChallengeCompleted.js index afd65f2..2960421 100644 --- a/src/models/SignUpChallengeCompleted.js +++ b/src/models/SignUpChallengeCompleted.js @@ -1,42 +1,35 @@ -class SignUpChallengeCompletedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); - provide() { - const SignUpChallengeCompleted = this.sequelize.define( - 'SignUpChallengeCompleted', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_completed_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - app_invite_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - public_key: { - type: this.DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'sign_up_challenge_completed', - } - ); - return SignUpChallengeCompleted; +const SignUpChallengeCompleted = sequelize.define( + 'SignUpChallengeCompleted', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + nostr_challenge_completed_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + app_invite_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + public_key: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'sign_up_challenge_completed', } -} +); -module.exports = SignUpChallengeCompletedProvider; +module.exports = SignUpChallengeCompleted; diff --git a/src/models/SignUpChallengeCreated.js b/src/models/SignUpChallengeCreated.js index 3da9ec4..1d3e22e 100644 --- a/src/models/SignUpChallengeCreated.js +++ b/src/models/SignUpChallengeCreated.js @@ -1,39 +1,31 @@ -class SignUpChallengeCreatedProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); + +const SignUpChallengeCreated = sequelize.define( + 'SignUpChallengeCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + nostr_challenge_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + app_invite_uuid: { + type: DataTypes.UUID, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'sign_up_challenge_created', } +); - provide() { - const SignUpChallengeCreated = this.sequelize.define( - 'SignUpChallengeCreated', - { - uuid: { - type: this.DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - nostr_challenge_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - app_invite_uuid: { - type: this.DataTypes.UUID, - allowNull: false, - }, - created_at: { - type: this.DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'sign_up_challenge_created', - } - ); - - return SignUpChallengeCreated; - } -} - -module.exports = SignUpChallengeCreatedProvider; +module.exports = SignUpChallengeCreated; diff --git a/src/models/index.js b/src/models/index.js deleted file mode 100644 index 02b4028..0000000 --- a/src/models/index.js +++ /dev/null @@ -1,97 +0,0 @@ -class ModelsProvider { - constructor({ sequelize, DataTypes }) { - this.sequelize = sequelize; - this.DataTypes = DataTypes; - } - provide() { - const AppInviteCreatedProvider = require('./AppInviteCreated'); - const AppInviteCreated = new AppInviteCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const ContactDetailsSetProvider = require('./ContactDetailsSet'); - const ContactDetailsSet = new ContactDetailsSetProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const LoginChallengeCompletedProvider = require('./LoginChallengeCompleted'); - const LoginChallengeCompleted = new LoginChallengeCompletedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const LoginChallengeCreatedProvider = require('./LoginChallengeCreated'); - const LoginChallengeCreated = new LoginChallengeCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const NostrChallengeCompletedProvider = require('./NostrChallengeCompleted'); - const NostrChallengeCompleted = new NostrChallengeCompletedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const NostrChallengeCreatedProvider = require('./NostrChallengeCreated'); - const NostrChallengeCreated = new NostrChallengeCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const NymSetProvider = require('./NymSet'); - const NymSet = new NymSetProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const OfferCreatedProvider = require('./OfferCreated'); - const OfferCreated = new OfferCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const OfferDeletedProvider = require('./OfferDeleted'); - const OfferDeleted = new OfferDeletedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const OfferDetailsSetProvider = require('./OfferDetailsSet'); - const OfferDetailsSet = new OfferDetailsSetProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const SessionCreatedProvider = require('./SessionCreated'); - const SessionCreated = new SessionCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const SessionRelatedToPublickeyProvider = require('./SessionRelatedToPublickey'); - const SessionRelatedToPublickey = new SessionRelatedToPublickeyProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const SignUpChallengeCompletedProvider = require('./SignUpChallengeCompleted'); - const SignUpChallengeCompleted = new SignUpChallengeCompletedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - const SignUpChallengeCreatedProvider = require('./SignUpChallengeCreated'); - const SignUpChallengeCreated = new SignUpChallengeCreatedProvider({ - sequelize: this.sequelize, - DataTypes: this.DataTypes, - }).provide(); - - return { - SignUpChallengeCreated, - SignUpChallengeCompleted, - SessionRelatedToPublickey, - SessionCreated, - OfferDeleted, - OfferDetailsSet, - OfferCreated, - NymSet, - NostrChallengeCreated, - NostrChallengeCompleted, - LoginChallengeCompleted, - LoginChallengeCreated, - ContactDetailsSet, - AppInviteCreated, - }; - } -} - -module.exports = ModelsProvider; diff --git a/public/css/offers.css b/src/public/css/offers.css similarity index 52% rename from public/css/offers.css rename to src/public/css/offers.css index 17ac4b5..16fad27 100644 --- a/public/css/offers.css +++ b/src/public/css/offers.css @@ -19,8 +19,8 @@ width: 50%; } - .create-offer-step { - width: 95%; + #create-offer-controls > * { + width: 100%; } .checkbox-row { @@ -31,13 +31,6 @@ flex-shrink: 0; margin-left: 5%; } - - .myoffer-card { - width: 100%; - height: 500px; - margin: 5px 5px; - padding: 10px; - } } @media (min-width: 769px) { @@ -58,12 +51,10 @@ width: 33%; } - .create-offer-step { + #create-offer-controls > * { margin-left: auto; margin-right: auto; width: 70%; - min-width: 500px; - max-width: 95%; } .checkbox-row { @@ -74,175 +65,12 @@ flex-shrink: 0; margin-left: 20%; } - - .myoffer-card { - width: 23%; - min-width: 339px; - height: 500px; - margin: 10px 10px; - padding: 10px; - } } -#view-my-offers-root { - display: none; +#create-offer-controls > * { text-align: center; } -#view-my-offers-root > * { - margin-left: auto; - margin-right: auto; -} - -#own-offers-container { - display: flex; - flex-wrap: wrap; - justify-content: space-evenly; -} - -.myoffer-card { - display: grid; - grid-template-columns: 48% 4% 48%; - grid-template-rows: 25% 1% 30% 1% 18% 1% 14% 1% 10%; -} - -.myoffer-card > div { - padding: 5px; -} - -.myoffer-card p { - margin: 0; -} - -.trade-desc { - grid-column: 1; - grid-row: 1; - text-align: left; -} - -.left-icon-checkboxed-field { - display: flex; - align-items: center; - margin-bottom: 2px; - font-size: 0.9em; -} - -.left-icon-checkboxed-field > * { - margin-right: 2px; - margin-left: 2px; -} -.left-icon-checkboxed-field img { - height: 1.1em; -} - -.right-icon-checkboxed-field { - display: flex; - align-items: center; - justify-content: end; - margin-bottom: 2px; - font-size: 0.9em; -} - -.right-icon-checkboxed-field > * { - margin-right: 2px; - margin-left: 2px; -} -.right-icon-checkboxed-field img { - height: 1.2em; -} - -.offer-card-content-title { - color: gray; - font-size: 0.8em; - font-weight: 100; -} - -.offer-card-content-data { - font-size: 1.1em; - font-weight: bold; -} - -.premium-desc { - grid-column: 3; - grid-row: 1; - text-align: right; -} - -.where-desc { - grid-column: 1; - grid-row: 3; - text-align: left; - overflow-y: auto; - overflow-x: hidden; -} - -.when-desc { - grid-column: 3; - grid-row: 3; - text-align: right; - overflow-y: auto; - overflow-x: hidden; -} - -.bitcoin-methods-desc { - grid-column: 1; - grid-row: 5; - text-align: left; -} - -.visibility-desc { - grid-column: 3; - grid-row: 5; - text-align: right; -} - -.other-desc { - grid-column: 1; - grid-row: 7; - text-align: left; -} - -.offer-action-buttons-area { - grid-column-start: 1; - grid-column-end: 4; - grid-row: 9; - display: flex; - align-items: center; - justify-content: flex-end; -} - -.offer-action-area { - cursor: pointer; - margin-right: 20px; - padding: 3px; -} - -.offer-long-text { - font-size: 0.9em; -} - -#create-offer-controls { - text-align: center; - overflow-y: auto; - max-height: 800px; - padding: 20px; -} - -.create-offer-step { - text-align: center; - border-radius: 20px; - box-shadow: 0 0 13px #ccc; - padding: 20px 0; - margin-top: 10px; - margin-bottom: 10px; - margin-left: auto; - margin-right: auto; -} - -.create-offer-step h3 { - margin-top: 0; -} - #close-offer-controls-area { display: flex; justify-content: end; @@ -280,9 +108,6 @@ .premium-button { background: white; border: 2px solid #e1c300; - width: 100%; - height: 50%; - font-size: 1em; } .premium-button:hover { @@ -297,6 +122,12 @@ border-bottom-right-radius: 10px; } +.premium-button { + width: 100%; + height: 50%; + font-size: 1em; +} + #premium-price-display-area { margin-left: 0; margin-right: auto; @@ -385,8 +216,3 @@ #button-submit-offer { width: 33%; } - -#close-offer { - margin-left: auto; - margin-right: auto; -} diff --git a/public/css/seca.css b/src/public/css/seca.css similarity index 55% rename from public/css/seca.css rename to src/public/css/seca.css index 1cc3034..629ba1a 100644 --- a/public/css/seca.css +++ b/src/public/css/seca.css @@ -13,17 +13,10 @@ } .button-medium { - padding: 0.5em 1em; + height: 3em; + padding: 1em; border-radius: 10px; } - - .top-notification-good { - top: 25px; - } - - #app-nav-bar > #laseca-logo { - height: 30px; - } } @media (min-width: 769px) { @@ -36,22 +29,23 @@ } .button-medium { + height: 3em; padding: 1em; border-radius: 10px; } - - .top-notification-good { - top: 50px; - } - - #app-nav-bar > #laseca-logo { - height: 30px; - } } -#app-nav-bar > #laseca-logo { - margin-top: 0; - transform: translateY(20%); +.badges { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(3, 1fr); +} + +.badge { + border: 2px solid #e1c300; + border-radius: 10px; + padding: 2%; + margin: 2%; } body { @@ -78,72 +72,6 @@ h1 { border: 2px solid red; } -.max-size-zero { - max-width: 0; - max-height: 0; -} - -.top-notification-good { - border-radius: 10px; - background-color: rgb(9, 165, 9); - color: white; - font-weight: 800; - opacity: 0.8; - position: fixed; - padding: 20px; - display: flex; - justify-content: space-evenly; - align-items: center; - left: 50%; - transform: translate(-50%, -500px); - transition: transform 1s ease-in-out; -} - -.top-notification-good.revealed { - transform: translate(-50%, 0%); -} -.top-notification-good > * { - margin: 0 20px; -} -.top-notification-good > *:first-child { - margin-left: 0; -} -.top-notification-good > *:last-child { - margin-right: 0; -} - -.top-notification-good img { - height: 50px; -} - -.full-screen-modal-background { - position: fixed; - width: 100%; - height: 100%; - top: 0; - left: 0; - background-color: rgba(0, 0, 0, 0.75); - transition: all 0.5s ease-in-out; - opacity: 0; - visibility: hidden; - pointer-events: none; -} - -.full-screen-modal-background.shown { - opacity: 1; - visibility: visible; - pointer-events: all; -} - -.full-screen-modal { - background-color: white; - border-radius: 20px; - padding: 10px; - width: fit-content; - max-width: 95%; - margin: 20px auto; -} - .button-group button { border: 0; padding: 1em; @@ -179,21 +107,10 @@ h1 { .over-background { background-color: white; border-radius: 1vw; - padding: 10px; + padding: 2vw; margin: 1%; } -.shadowed-round-area { - border-radius: 20px; - box-shadow: 0 0 13px #ccc; - padding: 20px; -} - -.subtle-box { - border-radius: 10px; - border: 1px solid rgb(202, 202, 202); -} - #login-card > * { text-align: center; margin-right: auto; @@ -202,6 +119,18 @@ h1 { margin-bottom: 1vh; } +.create-profile-card-section > * { + text-align: center; + margin-right: auto; + margin-left: auto; + display: block; + max-width: 1000px; +} + +.create-profile-card-section .badge img { + width: 10%; +} + .text-warning { color: red; font-weight: 800; @@ -223,25 +152,27 @@ h1 { cursor: default; } -.button-secondary { - background: white; - border: 0; - color: #e1c300; - cursor: pointer; - border: 3px solid #e1c300; - transition: all 0.5 ease-in-out; -} - -.button-secondary:hover { - font-weight: bold; -} - .button-large { font-weight: bold; font-size: 1.5em; padding: 0.5em; } +.invite-card { + margin: 5vh 20vw; + height: 100%; +} + +.invite-card-content { + margin: 3% 5%; + padding: 0 10%; +} + +.invite-card-content * { + margin: 1vh auto; + text-align: center; +} + .card-secondary { background-color: #369; border-radius: 1vw; @@ -263,7 +194,6 @@ h1 { color: white; padding: 1%; cursor: pointer; - border: 0; } .button-nostr:hover { @@ -275,3 +205,12 @@ h1 { cursor: default; border: 0; } + +#nostr-signup-button { + width: 20vw; + border: 0; +} + +#nostr-signup-button p { + font-size: 1.5rem; +} diff --git a/public/img/circle-xmark.svg b/src/public/img/circle-xmark.svg similarity index 100% rename from public/img/circle-xmark.svg rename to src/public/img/circle-xmark.svg diff --git a/public/img/envelope.svg b/src/public/img/envelope.svg similarity index 100% rename from public/img/envelope.svg rename to src/public/img/envelope.svg diff --git a/public/img/laseca_logo_white.png b/src/public/img/laseca_logo_white.png similarity index 100% rename from public/img/laseca_logo_white.png rename to src/public/img/laseca_logo_white.png diff --git a/public/img/lasecagold_ostrich.svg b/src/public/img/lasecagold_ostrich.svg similarity index 100% rename from public/img/lasecagold_ostrich.svg rename to src/public/img/lasecagold_ostrich.svg diff --git a/public/img/message-exclamation.svg b/src/public/img/message-exclamation.svg similarity index 100% rename from public/img/message-exclamation.svg rename to src/public/img/message-exclamation.svg diff --git a/public/img/phone.svg b/src/public/img/phone.svg similarity index 100% rename from public/img/phone.svg rename to src/public/img/phone.svg diff --git a/public/img/signal-messenger.svg b/src/public/img/signal-messenger.svg similarity index 100% rename from public/img/signal-messenger.svg rename to src/public/img/signal-messenger.svg diff --git a/public/img/square-whatsapp.svg b/src/public/img/square-whatsapp.svg similarity index 100% rename from public/img/square-whatsapp.svg rename to src/public/img/square-whatsapp.svg diff --git a/public/img/telegram.svg b/src/public/img/telegram.svg similarity index 100% rename from public/img/telegram.svg rename to src/public/img/telegram.svg diff --git a/public/img/white_ostrich.svg b/src/public/img/white_ostrich.svg similarity index 100% rename from public/img/white_ostrich.svg rename to src/public/img/white_ostrich.svg diff --git a/src/public/javascript/app.js b/src/public/javascript/app.js new file mode 100644 index 0000000..aa28857 --- /dev/null +++ b/src/public/javascript/app.js @@ -0,0 +1,10 @@ +const navbuttonHome = document.getElementById('navbutton-home'); +const navbuttonOffers = document.getElementById('navbutton-offers'); + +navbuttonHome.addEventListener('click', () => { + window.location.href = '/home'; +}); + +navbuttonOffers.addEventListener('click', () => { + window.location.href = '/offers'; +}); diff --git a/src/public/javascript/createProfile.js b/src/public/javascript/createProfile.js new file mode 100644 index 0000000..f7af1f1 --- /dev/null +++ b/src/public/javascript/createProfile.js @@ -0,0 +1,116 @@ +function debounce(func, wait) { + let timeout; + return function (...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; +} + +const validateNymInput = debounce(() => { + const nymValue = nymInput.value.trim(); + const isValid = nymValue.length >= 3 && nymValue.length <= 128; + if (isValid) { + nymInputValidationWarning.style.display = 'none'; + } else { + nymInputValidationWarning.style.display = 'block'; + } +}, 500); + +const checkIfSubmittable = debounce((allInputs) => { + const nymIsFilled = allInputs.nymInput.value !== ''; + let atLeastOneContactIsFilled = false; + + for (const contactInput of allInputs.contactInputs) { + if (contactInput.value !== '') { + atLeastOneContactIsFilled = true; + } + } + + const buttonShouldBeDisabled = !(nymIsFilled && atLeastOneContactIsFilled); + submitProfileButton.disabled = buttonShouldBeDisabled; +}, 500); + +async function createProfile(allInputs) { + const contactDetails = []; + for (const someInput of allInputs.contactInputs) { + contactDetails.push({ + type: someInput.getAttribute('data-type'), + value: someInput.value, + }); + } + const encryptedContactDetails = await window.nostr.nip04.encrypt( + await window.nostr.getPublicKey(), + JSON.stringify(contactDetails) + ); + await fetch('/api/set-contact-details', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ encryptedContactDetails }), + }); + + const nym = allInputs.nymInput.value; + await fetch('/api/set-nym', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ nym }), + }); + + setTimeout(() => { + window.location.href = '/home'; + }, 1000); +} + +function onLoadErrands(allInputs, submitProfileButton) { + allInputs.nymInput.addEventListener('input', validateNymInput); + + for (const someInput of allInputs.allInputs) { + someInput.addEventListener('input', () => { + checkIfSubmittable(allInputs); + }); + } + + checkIfSubmittable(allInputs); + + submitProfileButton.addEventListener('click', () => { + createProfile(allInputs); + }); +} + +const nymInput = document.getElementById('nym-input'); +const nymInputValidationWarning = document.getElementById( + 'nym-input-validation-warning' +); +const phoneInput = document.getElementById('phone-input'); +const whatsappInput = document.getElementById('whatsapp-input'); +const telegramInput = document.getElementById('telegram-input'); +const emailInput = document.getElementById('email-input'); +const nostrInput = document.getElementById('nostr-input'); +const signalInput = document.getElementById('signal-input'); +const submitProfileButton = document.getElementById('submit-profile-button'); + +const allInputs = { + nymInput: nymInput, + contactInputs: [ + phoneInput, + whatsappInput, + telegramInput, + emailInput, + nostrInput, + signalInput, + ], + allInputs: [ + nymInput, + phoneInput, + whatsappInput, + telegramInput, + emailInput, + nostrInput, + signalInput, + ], +}; + +onLoadErrands(allInputs, submitProfileButton); diff --git a/src/public/javascript/home.js b/src/public/javascript/home.js new file mode 100644 index 0000000..e69de29 diff --git a/src/front/services/inviteService.js b/src/public/javascript/invite.js similarity index 61% rename from src/front/services/inviteService.js rename to src/public/javascript/invite.js index d494c6c..04ef5c8 100644 --- a/src/front/services/inviteService.js +++ b/src/public/javascript/invite.js @@ -1,4 +1,14 @@ -const requestAndRespondSignUpChallenge = async ({ onNostrErrorCallback }) => { +window.onload = function () { + if (!window.nostr) { + console.log('Nostr extension not present'); + document.querySelector('#nostr-signup-button').disabled = true; + document.querySelector('#no-extension-nudges').style.display = 'block'; + } else { + console.log('Nostr extension present'); + } +}; + +async function acceptInvite() { let challengeResponse; try { challengeResponse = await fetch('/api/signup/nostr-challenge', { @@ -18,7 +28,7 @@ const requestAndRespondSignUpChallenge = async ({ onNostrErrorCallback }) => { try { pubkey = await window.nostr.getPublicKey(); } catch (error) { - onNostrErrorCallback(); + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; return; } const event = { @@ -33,7 +43,7 @@ const requestAndRespondSignUpChallenge = async ({ onNostrErrorCallback }) => { try { signedEvent = await window.nostr.signEvent(event); } catch (error) { - onNostrErrorCallback(); + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; return; } @@ -49,9 +59,10 @@ const requestAndRespondSignUpChallenge = async ({ onNostrErrorCallback }) => { return; } - return verifyResponse; -}; - -module.exports = { - requestAndRespondSignUpChallenge, -}; + if (verifyResponse.ok) { + document.querySelector('#sign-up-success').style.display = 'block'; + setTimeout(() => { + window.location.href = '/createProfile'; + }, 1000); + } +} diff --git a/src/front/services/loginService.js b/src/public/javascript/login.js similarity index 62% rename from src/front/services/loginService.js rename to src/public/javascript/login.js index d1f5815..36175b4 100644 --- a/src/front/services/loginService.js +++ b/src/public/javascript/login.js @@ -1,12 +1,14 @@ -const requestAndRespondLoginChallenge = async ({ - onRejectedPubKeyCallback, - onRejectedSignatureCallback, -}) => { - onRejectedPubKeyCallback = () => { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - }; - onRejectedSignatureCallback = onRejectedPubKeyCallback; +window.onload = function () { + if (!window.nostr) { + console.log('Nostr extension not present'); + document.querySelector('#login-button').disabled = true; + document.querySelector('#no-extension-nudges').style.display = 'block'; + } else { + console.log('Nostr extension present'); + } +}; +async function login() { let challengeResponse; try { challengeResponse = await fetch('/api/login/nostr-challenge', { @@ -26,7 +28,7 @@ const requestAndRespondLoginChallenge = async ({ try { pubkey = await window.nostr.getPublicKey(); } catch (error) { - onRejectedPubKeyCallback(); + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; return; } const event = { @@ -41,7 +43,7 @@ const requestAndRespondLoginChallenge = async ({ try { signedEvent = await window.nostr.signEvent(event); } catch (error) { - onRejectedSignatureCallback(); + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; return; } @@ -57,9 +59,14 @@ const requestAndRespondLoginChallenge = async ({ return; } - return verifyResponse; -}; + if (verifyResponse.status === 403) { + document.querySelector('#rejected-public-key').style.display = 'block'; + } -module.exports = { - requestAndRespondLoginChallenge, -}; + if (verifyResponse.ok) { + document.querySelector('#sign-up-success').style.display = 'block'; + setTimeout(() => { + window.location.href = '/home'; + }, 1000); + } +} diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js new file mode 100644 index 0000000..3a73d12 --- /dev/null +++ b/src/public/javascript/offers.js @@ -0,0 +1,201 @@ +const buttonStartCreateOffer = document.getElementById( + 'button-start-create-offer' +); +const closeOfferControls = document.getElementById('close-offer-controls-x'); +const createOfferControls = document.getElementById('create-offer-controls'); + +const buyOrSellButtonGroup = document.getElementById( + 'button-group-buy-or-sell' +); +const buyOrSellButtons = buyOrSellButtonGroup.querySelectorAll('button'); +const buyButton = document.getElementById('button-buy-bitcoin'); +const sellButton = document.getElementById('button-sell-bitcoin'); + +const premiumValue = document.getElementById('premium-value'); +const buttonIncreasePremium = document.getElementById( + 'button-increase-premium' +); + +const buttonDecreasePremium = document.getElementById( + 'button-decrease-premium' +); + +const eurAmountInput = document.getElementById('input-eur-amount'); +const btcAmountInput = document.getElementById('input-btc-amount'); + +const placeInput = document.getElementById('place-input'); +const timeInput = document.getElementById('time-input'); + +const onchainCheckbox = document.getElementById('onchain-checkbox'); +const lightningCheckbox = document.getElementById('lightning-checkbox'); + +const btcMethodCheckboxes = [onchainCheckbox, lightningCheckbox]; + +const myTrustedCheckbox = document.getElementById('my-trusted-checkbox'); +const myTrustedTrustedCheckbox = document.getElementById( + 'my-trusted-trusted-checkbox' +); +const allMembersCheckbox = document.getElementById('all-members-checkbox'); + +const bigNotesAcceptedCheckbox = document.getElementById( + 'large-bills-checkbox' +); + +const publishOfferButton = document.getElementById('button-submit-offer'); + +function toggleCreateOfferControls() { + createOfferControls.style.display = + createOfferControls.style.display === 'block' ? 'none' : 'block'; + + buttonStartCreateOffer.style.display = + buttonStartCreateOffer.style.display === 'block' ? 'none' : 'block'; +} + +function modifyPremiumValue(delta) { + const regexExpression = /-*\d+/; + const numValue = parseInt(premiumValue.innerText.match(regexExpression)[0]); + + const newValue = `${numValue + delta}%`; + + premiumValue.innerText = newValue; +} + +function toggleBuyOrSellButtonGroup() { + buyOrSellButtons.forEach((button) => { + if (button.classList.contains('selected')) { + button.classList.remove('selected'); + } else { + button.classList.add('selected'); + } + }); +} + +function readIntFromEurAmountInput() { + const eurAmountFieldValue = eurAmountInput.value; + const regularExpression = /([\d\s]+)/; + const matchResult = eurAmountFieldValue.match(regularExpression); + + if (!matchResult) { + return null; + } + + const numberString = matchResult[1]; + const cleanInputNumber = parseInt(numberString.replace(/\s/gi, '')); + + return cleanInputNumber; +} + +function validateAndFormatEurAmountInput() { + const cleanInputNumber = readIntFromEurAmountInput(); + eurAmountInput.classList.remove('input-is-valid', 'input-is-invalid'); + if (cleanInputNumber) { + eurAmountInput.value = formatNumberWithSpaces(cleanInputNumber); + eurAmountInput.classList.add('input-is-valid'); + return; + } + + eurAmountInput.classList.add('input-is-invalid'); +} + +function updateBtcInput() { + const eurToSatRate = 1021; + const cleanEurAmount = readIntFromEurAmountInput(); + + const satsAmount = cleanEurAmount * eurToSatRate; + const formattedSatsAmount = formatNumberWithSpaces(satsAmount); + btcAmountInput.value = formattedSatsAmount; +} + +function validateBitcoinMethodCheckboxes(clickedCheckbox) { + let checkedCount = btcMethodCheckboxes.filter((cb) => cb.checked).length; + if (checkedCount === 0) { + clickedCheckbox.checked = true; + } +} + +async function publishOffer() { + let wants; + if (buyButton.classList.contains('selected')) { + wants = 'BTC'; + } + if (sellButton.classList.contains('selected')) { + wants = 'EUR'; + } + + const premium = parseInt(premiumValue.innerText.match(/\d+/)[0]) / 100; + const trade_amount_eur = eurAmountInput.value; + const location_details = placeInput.value; + const time_availability_details = timeInput.value; + const is_onchain_accepted = onchainCheckbox.checked; + const is_lightning_accepted = lightningCheckbox.checked; + const show_offer_to_trusted = myTrustedCheckbox.checked; + const show_offer_to_trusted_trusted = myTrustedTrustedCheckbox.checked; + const show_offer_to_all_members = allMembersCheckbox.checked; + const are_big_notes_accepted = bigNotesAcceptedCheckbox.checked; + + const offerDetails = { + wants, + premium, + trade_amount_eur, + location_details, + time_availability_details, + is_onchain_accepted, + is_lightning_accepted, + show_offer_to_trusted, + show_offer_to_trusted_trusted, + show_offer_to_all_members, + are_big_notes_accepted, + }; + + await fetch('/api/offer', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ offerDetails }), + }); +} + +buttonStartCreateOffer.addEventListener('click', () => { + toggleCreateOfferControls(); +}); + +closeOfferControls.addEventListener('click', () => { + toggleCreateOfferControls(); +}); + +buyOrSellButtons.forEach((button) => { + button.addEventListener('click', () => { + toggleBuyOrSellButtonGroup(); + }); +}); + +buttonIncreasePremium.addEventListener('click', () => { + modifyPremiumValue(1); +}); + +buttonDecreasePremium.addEventListener('click', () => { + modifyPremiumValue(-1); +}); + +eurAmountInput.addEventListener('blur', () => { + validateAndFormatEurAmountInput(); + updateBtcInput(); +}); + +eurAmountInput.addEventListener('input', () => { + eurAmountInput.value = eurAmountInput.value.replace(/[^0-9]/g, ''); + updateBtcInput(); +}); + +for (const btcMethodCheckbox of btcMethodCheckboxes) { + btcMethodCheckbox.addEventListener('click', () => { + validateBitcoinMethodCheckboxes(btcMethodCheckbox); + }); +} + +publishOfferButton.addEventListener('click', () => { + publishOffer(); +}); + +updateBtcInput(); diff --git a/src/front/utils/formatNumbersWithSpaces.js b/src/public/javascript/utils.js similarity index 71% rename from src/front/utils/formatNumbersWithSpaces.js rename to src/public/javascript/utils.js index 1d1ee44..e8964e2 100644 --- a/src/front/utils/formatNumbersWithSpaces.js +++ b/src/public/javascript/utils.js @@ -1,5 +1,3 @@ function formatNumberWithSpaces(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); } - -module.exports = formatNumberWithSpaces; diff --git a/src/public/kitty.jpeg b/src/public/kitty.jpeg new file mode 100644 index 0000000..ed1ff6f Binary files /dev/null and b/src/public/kitty.jpeg differ diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 102cf6a..547cdb3 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -1,301 +1,237 @@ -class ApiRoutesProvider { - constructor({ express, middlewares, services, errors }) { - this.router = express.Router(); - this.middlewares = middlewares; - this.services = services; - this.errors = errors; +const express = require('express'); + +const invitesService = require('../services/invitesService'); +const nostrService = require('../services/nostrService'); +const loginService = require('../services/loginService'); +const sessionService = require('../services/sessionService'); +const profileService = require('../services/profileService'); +const offerService = require('../services/offerService'); +const errors = require('../errors'); +const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddleware'); +const rejectIfNotAuthorizedMiddleware = require('../middlewares/rejectIfNotAuthorizedMiddleware'); + +const router = express.Router(); + +router.get('/signup/nostr-challenge', async (req, res) => { + const inviteUuid = req.cookies.inviteUuid; + + let signUpChallenge; + try { + signUpChallenge = await invitesService.createSignUpChallenge(inviteUuid); + } catch (error) { + if (error instanceof errors.NotFoundError) { + return res.status(404).json({ + success: false, + message: 'Could not find invite with that id.', + }); + } + + if (error instanceof errors.AlreadyUsedError) { + return res.status(410).json({ + success: false, + message: 'That invite has already been used.', + }); + } + + return res.status(500).json({ + success: false, + message: 'Unexpected error.', + }); } - provide() { - this.router.get('/signup/nostr-challenge', async (req, res) => { - const inviteUuid = req.cookies.inviteUuid; - - let signUpChallenge; - try { - signUpChallenge = - await this.services.invitesService.createSignUpChallenge(inviteUuid); - } catch (error) { - if (error instanceof this.errors.NotFoundError) { - return res.status(404).json({ - success: false, - message: 'Could not find invite with that id.', - }); - } - - if (error instanceof this.errors.AlreadyUsedError) { - return res.status(410).json({ - success: false, - message: 'That invite has already been used.', - }); - } - - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } - - let relatedNostrChallenge; - try { - relatedNostrChallenge = - await this.services.nostrService.getNostrChallenge( - signUpChallenge.nostr_challenge_uuid - ); - } catch (error) { - return res.status(500).json({ - success: false, - message: `Unexpected error: ${error}`, - }); - } - - return res - .status(200) - .json({ challenge: relatedNostrChallenge.challenge }); + let relatedNostrChallenge; + try { + relatedNostrChallenge = await nostrService.getNostrChallenge( + signUpChallenge.nostr_challenge_uuid + ); + } catch (error) { + return res.status(500).json({ + success: false, + message: `Unexpected error: ${error}`, }); - - this.router.post('/signup/nostr-verify', async (req, res) => { - const signedEvent = req.body; - const sessionUuid = req.cookies.sessionUuid; - - let completedSignUpChallenge; - try { - completedSignUpChallenge = - await this.services.invitesService.verifySignUpChallenge(signedEvent); - } catch (error) { - if (error instanceof this.errors.ExpiredError) { - return res.status(410).json({ - success: false, - message: 'The challenge has expired, request a new one.', - }); - } - if (error instanceof this.errors.AlreadyUsedError) { - return res.status(410).json({ - success: false, - message: 'The challenge has been used, request a new one.', - }); - } - if (error instanceof this.errors.InvalidSignatureError) { - return res.status(400).json({ - success: false, - message: 'The challenge signature is not valid.', - }); - } - } - - await this.services.sessionService.relateSessionToPublicKey( - sessionUuid, - completedSignUpChallenge.public_key - ); - - return res.status(200).json({ success: true }); - }); - - this.router.get('/login/nostr-challenge', async (req, res) => { - let loginChallenge; - try { - loginChallenge = - await this.services.loginService.createLoginChallenge(); - } catch (error) { - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } - - let relatedNostrChallenge; - try { - relatedNostrChallenge = - await this.services.nostrService.getNostrChallenge( - loginChallenge.nostr_challenge_uuid - ); - } catch (error) { - return res.status(500).json({ - success: false, - message: `Unexpected error: ${error}`, - }); - } - - return res - .status(200) - .json({ challenge: relatedNostrChallenge.challenge }); - }); - - this.router.post('/login/nostr-verify', async (req, res) => { - const signedEvent = req.body; - const sessionUuid = req.cookies.sessionUuid; - - let completedLoginChallenge; - try { - completedLoginChallenge = - await this.services.loginService.verifyLoginChallenge(signedEvent); - } catch (error) { - if (error instanceof this.errors.ExpiredError) { - return res.status(410).json({ - success: false, - message: 'The challenge has expired, request a new one.', - }); - } - if (error instanceof this.errors.AlreadyUsedError) { - return res.status(410).json({ - success: false, - message: 'The challenge has been used, request a new one.', - }); - } - if (error instanceof this.errors.InvalidSignatureError) { - return res.status(400).json({ - success: false, - message: 'The challenge signature is not valid.', - }); - } - if (error instanceof this.errors.ForbiddenError) { - return res.status(403).json({ - success: false, - message: 'This public key is not authorized.', - }); - } - - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } - - await this.services.sessionService.relateSessionToPublicKey( - sessionUuid, - completedLoginChallenge.public_key - ); - - return res.status(200).json({ success: true }); - }); - - this.router.post( - '/set-contact-details', - this.middlewares.rejectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - async (req, res) => { - const encryptedContactDetails = req.body.encryptedContactDetails; - const publicKey = req.cookies.publicKey; - - if (!encryptedContactDetails) { - return res.status(400).json({ - success: false, - message: 'Missing contact details.', - }); - } - - await this.services.profileService.setContactDetails( - publicKey, - encryptedContactDetails - ); - - return res.status(200).json({ - success: true, - message: 'Contact details set successfully.', - }); - } - ); - - this.router.post( - '/set-nym', - this.middlewares.rejectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - async (req, res) => { - const nym = req.body.nym; - const publicKey = req.cookies.publicKey; - - if (!nym) { - return res.status(400).json({ - success: false, - message: 'Missing nym', - }); - } - - await this.services.profileService.setNym(publicKey, nym); - - return res.status(200).json({ - success: true, - message: 'Nym set successfully.', - }); - } - ); - - this.router.post( - '/offer', - this.middlewares.rejectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - async (req, res) => { - const publicKey = req.cookies.publicKey; - const offerDetails = req.body.offerDetails; - - await this.services.offerService.createOffer(publicKey, offerDetails); - - return res.status(200).json({ - success: true, - message: 'Offer created successfully', - }); - } - ); - - this.router.delete( - '/offer/:offerUuid', - this.middlewares.rejectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - async (req, res) => { - const offerUuid = req.params.offerUuid; - - try { - await this.services.offerService.deleteOffer(offerUuid); - } catch (error) { - if (error instanceof this.errors.NotFoundError) { - return res.status(404).json({ - success: false, - message: 'Offer not found for the given public key.', - }); - } - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } - - return res.status(204).json({ - success: true, - message: 'Offer deleted successfully', - }); - } - ); - - this.router.get( - '/publickey-offers', - this.middlewares.rejectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - async (req, res) => { - console.log('elo'); - const publicKey = req.cookies.publicKey; - - const offers = - await this.services.offerService.getActiveOffersByPublicKey( - publicKey - ); - - if (!offers) { - return res.status(404).json({ - success: true, - message: 'No offers posted by this public key.', - }); - } - - if (offers) { - return res.status(200).json({ - success: true, - message: 'Offers found', - data: offers, - }); - } - } - ); - - return this.router; } -} -module.exports = ApiRoutesProvider; + + return res.status(200).json({ challenge: relatedNostrChallenge.challenge }); +}); + +router.post('/signup/nostr-verify', async (req, res) => { + const signedEvent = req.body; + const sessionUuid = req.cookies.sessionUuid; + + let completedSignUpChallenge; + try { + completedSignUpChallenge = + await invitesService.verifySignUpChallenge(signedEvent); + } catch (error) { + if (error instanceof errors.ExpiredError) { + return res.status(410).json({ + success: false, + message: 'The challenge has expired, request a new one.', + }); + } + if (error instanceof errors.AlreadyUsedError) { + return res.status(410).json({ + success: false, + message: 'The challenge has been used, request a new one.', + }); + } + if (error instanceof errors.InvalidSignatureError) { + return res.status(400).json({ + success: false, + message: 'The challenge signature is not valid.', + }); + } + } + + await sessionService.relateSessionToPublicKey( + sessionUuid, + completedSignUpChallenge.public_key + ); + + return res.status(200).json({ success: true }); +}); + +router.get('/login/nostr-challenge', async (req, res) => { + let loginChallenge; + try { + loginChallenge = await loginService.createLoginChallenge(); + } catch (error) { + return res.status(500).json({ + success: false, + message: 'Unexpected error.', + }); + } + + let relatedNostrChallenge; + try { + relatedNostrChallenge = await nostrService.getNostrChallenge( + loginChallenge.nostr_challenge_uuid + ); + } catch (error) { + return res.status(500).json({ + success: false, + message: `Unexpected error: ${error}`, + }); + } + + return res.status(200).json({ challenge: relatedNostrChallenge.challenge }); +}); + +router.post('/login/nostr-verify', async (req, res) => { + const signedEvent = req.body; + const sessionUuid = req.cookies.sessionUuid; + + let completedLoginChallenge; + try { + completedLoginChallenge = + await loginService.verifyLoginChallenge(signedEvent); + } catch (error) { + console.log('helo5'); + console.log(error); + if (error instanceof errors.ExpiredError) { + return res.status(410).json({ + success: false, + message: 'The challenge has expired, request a new one.', + }); + } + if (error instanceof errors.AlreadyUsedError) { + return res.status(410).json({ + success: false, + message: 'The challenge has been used, request a new one.', + }); + } + if (error instanceof errors.InvalidSignatureError) { + return res.status(400).json({ + success: false, + message: 'The challenge signature is not valid.', + }); + } + if (error instanceof errors.ForbiddenError) { + console.log('helo?1'); + return res.status(403).json({ + success: false, + message: 'This public key is not authorized.', + }); + } + + return res.status(500).json({ + success: false, + message: 'Unexpected error.', + }); + } + console.log('helo?2'); + console.log(completedLoginChallenge); + await sessionService.relateSessionToPublicKey( + sessionUuid, + completedLoginChallenge.public_key + ); + + return res.status(200).json({ success: true }); +}); + +router.post( + '/set-contact-details', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + const encryptedContactDetails = req.body.encryptedContactDetails; + const publicKey = req.cookies.publicKey; + + if (!encryptedContactDetails) { + return res.status(400).json({ + success: false, + message: 'Missing contact details.', + }); + } + + await profileService.setContactDetails(publicKey, encryptedContactDetails); + + return res.status(200).json({ + success: true, + message: 'Contact details set successfully.', + }); + } +); + +router.post( + '/set-nym', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + const nym = req.body.nym; + const publicKey = req.cookies.publicKey; + + if (!nym) { + return res.status(400).json({ + success: false, + message: 'Missing nym', + }); + } + + await profileService.setNym(publicKey, nym); + + return res.status(200).json({ + success: true, + message: 'Nym set successfully.', + }); + } +); + +router.post( + '/offer', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + const publicKey = req.cookies.publicKey; + const offerDetails = req.body.offerDetails; + + await offerService.createOffer(publicKey, offerDetails); + + return res.status(200).json({ + success: true, + message: 'Offer created successfully', + }); + } +); + +module.exports = router; diff --git a/src/routes/webRoutes.js b/src/routes/webRoutes.js index b215aa5..fd6585e 100644 --- a/src/routes/webRoutes.js +++ b/src/routes/webRoutes.js @@ -1,88 +1,74 @@ -class WebRoutesProvider { - constructor({ express, middlewares, invitesService }) { - this.router = express.Router(); - this.middlewares = middlewares; - this.invitesService = invitesService; +const express = require('express'); +const router = express.Router(); + +const redirectIfNotAuthorizedMiddleware = require('../middlewares/redirectIfNotAuthorizedMiddleware'); +const invitesService = require('../services/invitesService'); +const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddleware'); +const redirectIfMissingProfileDetailsMiddleware = require('../middlewares/redirectIfMissingProfileDetailsMiddleware'); +const redirectHomeIfAuthorized = require('../middlewares/redirectHomeIfAuthorized'); + +router.get( + '/', + redirectHomeIfAuthorized, + redirectIfNotAuthorizedMiddleware, + (req, res) => { + res.redirect('/login'); + } +); + +router.get('/login', redirectHomeIfAuthorized, (req, res) => { + res.render('login', { uuid: req.cookies.sessionUuid }); +}); + +router.get('/invite/:inviteUuid', async (req, res) => { + const { inviteUuid } = req.params; + + res.cookie('inviteUuid', inviteUuid, { httpOnly: true, maxAge: 86400000 }); + + let invite; + try { + invite = await invitesService.getAppInvite(inviteUuid); + if (!invite) { + return res.status(404).render('error', { message: 'Invite not found.' }); + } + + if (await invitesService.isAppInviteSpent(inviteUuid)) { + return res.status(410).render('invite_spent', { invite }); + } + } catch (error) { + console.error('Error fetching invite:', error); + return res.status(500).render('error', { message: 'An error occurred' }); } - provide() { - this.router.get( - '/', - this.middlewares.redirectHomeIfAuthorized, - this.middlewares.redirectIfNotAuthorizedMiddleware, - (req, res) => { - res.redirect('/login'); - } - ); + return res.render('invite', { invite }); +}); - this.router.get( - '/login', - this.middlewares.redirectHomeIfAuthorized, - (req, res) => { - res.render('login', { uuid: req.cookies.sessionUuid }); - } - ); - - this.router.get('/invite/:inviteUuid', async (req, res) => { - const { inviteUuid } = req.params; - - res.cookie('inviteUuid', inviteUuid, { - httpOnly: true, - maxAge: 86400000, - }); - - let invite; - try { - invite = await this.invitesService.getAppInvite(inviteUuid); - if (!invite) { - return res - .status(404) - .render('error', { message: 'Invite not found.' }); - } - - if (await this.invitesService.isAppInviteSpent(inviteUuid)) { - return res.status(410).render('invite_spent', { invite }); - } - } catch (error) { - console.error('Error fetching invite:', error); - return res - .status(500) - .render('error', { message: 'An error occurred' }); - } - - return res.render('invite', { invite }); - }); - - this.router.get( - '/createProfile', - this.middlewares.redirectIfNotAuthorizedMiddleware, - async (req, res) => { - return res.status(200).render('createProfile'); - } - ); - - this.router.get( - '/home', - this.middlewares.redirectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - this.middlewares.redirectIfMissingProfileDetailsMiddleware, - (req, res) => { - res.render('home', {}); - } - ); - - this.router.get( - '/offers', - this.middlewares.redirectIfNotAuthorizedMiddleware, - this.middlewares.attachPublicKeyMiddleware, - this.middlewares.redirectIfMissingProfileDetailsMiddleware, - (req, res) => { - res.render('offers', {}); - } - ); - - return this.router; +router.get( + '/createProfile', + redirectIfNotAuthorizedMiddleware, + async (req, res) => { + return res.status(200).render('createProfile'); } -} +); -module.exports = WebRoutesProvider; +router.get( + '/home', + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + (req, res) => { + res.render('home', {}); + } +); + +router.get( + '/offers', + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + (req, res) => { + res.render('offers', {}); + } +); + +module.exports = router; diff --git a/src/services/index.js b/src/services/index.js deleted file mode 100644 index 1ec7ea2..0000000 --- a/src/services/index.js +++ /dev/null @@ -1,61 +0,0 @@ -class ServicesProvider { - constructor({ models, constants, errors, sequelize }) { - this.models = models; - this.constants = constants; - this.errors = errors; - this.sequelize = sequelize; - } - - provide() { - const NostrServiceProvider = require('../services/nostrService'); - const nostrService = new NostrServiceProvider({ - models: this.models, - constants: this.constants, - errors: this.errors, - }).provide(); - - const InvitesServiceProvider = require('../services/invitesService'); - const invitesService = new InvitesServiceProvider({ - models: this.models, - errors: this.errors, - nostrService: nostrService, - }).provide(); - - const LoginServiceProvider = require('../services/loginService'); - const loginService = new LoginServiceProvider({ - models: this.models, - errors: this.errors, - nostrService, - invitesService, - }).provide(); - - const SessionServiceProvider = require('../services/sessionService'); - const sessionService = new SessionServiceProvider({ - models: this.models, - constants: this.constants, - invitesService, - }).provide(); - - const ProfileServiceProvider = require('../services/profileService'); - const profileService = new ProfileServiceProvider({ - models: this.models, - }).provide(); - const OfferServiceProvider = require('../services/offerService'); - const offerService = new OfferServiceProvider({ - models: this.models, - errors: this.errors, - sequelize: this.sequelize, - }).provide(); - - return { - invitesService, - nostrService, - loginService, - sessionService, - profileService, - offerService, - }; - } -} - -module.exports = ServicesProvider; diff --git a/src/services/invitesService.js b/src/services/invitesService.js index df357e3..432633c 100644 --- a/src/services/invitesService.js +++ b/src/services/invitesService.js @@ -1,134 +1,119 @@ const uuid = require('uuid'); -class InvitesServiceProvider { - constructor({ models, errors, nostrService }) { - this.models = models; - this.errors = errors; - this.nostrService = nostrService; - } - - provide() { - const appInviteExists = async (inviteUuid) => { - const invite = await this.models.AppInviteCreated.findOne({ - where: { uuid: inviteUuid }, - }); - if (invite) { - return true; - } - return false; - }; - - const getAppInvite = async (inviteUuid) => { - const invite = await this.models.AppInviteCreated.findOne({ - where: { uuid: inviteUuid }, - }); - return invite; - }; - - const isAppInviteSpent = async (appInviteUuid) => { - const signUpChallengeCompleted = - await this.models.SignUpChallengeCompleted.findOne({ - where: { - app_invite_uuid: appInviteUuid, - }, - }); - - if (signUpChallengeCompleted) { - return true; - } - return false; - }; - - const createAppInvite = async (inviterPubKey) => { - return await this.models.AppInviteCreated.create({ - uuid: uuid.v7(), - inviter_pub_key: inviterPubKey, - created_at: new Date().toISOString(), - }); - }; - - const createSignUpChallenge = async (appInviteUuid) => { - if (!(await appInviteExists(appInviteUuid))) { - throw new this.errors.NotFoundError("Invite doesn't exist."); - } - - if (await isAppInviteSpent(appInviteUuid)) { - throw new this.errors.AlreadyUsedError('Invite has already been used.'); - } - - const nostrChallenge = await this.nostrService.createNostrChallenge(); - - return await this.models.SignUpChallengeCreated.create({ - uuid: uuid.v7(), - nostr_challenge_uuid: nostrChallenge.uuid, - app_invite_uuid: appInviteUuid, - created_at: new Date().toISOString(), - }); - }; - - const verifySignUpChallenge = async (signedEvent) => { - const challengeTag = signedEvent.tags.find( - (tag) => tag[0] === 'challenge' - ); - const challenge = challengeTag[1]; - - const nostrChallenge = await this.nostrService.getNostrChallenge( - null, - challenge - ); - - const signUpChallenge = await this.models.SignUpChallengeCreated.findOne({ - where: { - nostr_challenge_uuid: nostrChallenge.uuid, - }, - }); - - if (await this.nostrService.hasNostrChallengeBeenCompleted(challenge)) { - throw new this.errors.AlreadyUsedError( - 'This challenge has already been used.' - ); - } - - const completedNostrChallenge = - await this.nostrService.verifyNostrChallenge(signedEvent); - - const completedSignUpChallenge = - await this.models.SignUpChallengeCompleted.create({ - uuid: uuid.v7(), - nostr_challenge_completed_uuid: completedNostrChallenge.uuid, - app_invite_uuid: signUpChallenge.app_invite_uuid, - public_key: completedNostrChallenge.public_key, - created_at: new Date().toISOString(), - }); - - return completedSignUpChallenge; - }; - - const isPublicKeySignedUp = async (publicKey) => { - const signUpChallengeCompleted = - await this.models.SignUpChallengeCompleted.findOne({ - where: { - public_key: publicKey, - }, - }); - - if (signUpChallengeCompleted) { - return true; - } - - return false; - }; - - return { - appInviteExists, - getAppInvite, - isAppInviteSpent, - createAppInvite, - createSignUpChallenge, - verifySignUpChallenge, - isPublicKeySignedUp, - }; +const nostrService = require('./nostrService'); +const AppInviteCreated = require('../models/AppInviteCreated'); +const SignUpChallengeCreated = require('../models/SignUpChallengeCreated'); +const SignUpChallengeCompleted = require('../models/SignUpChallengeCompleted'); + +const errors = require('../errors'); + +async function appInviteExists(inviteUuid) { + const invite = await AppInviteCreated.findOne({ + where: { uuid: inviteUuid }, + }); + if (invite) { + return true; } + return false; } -module.exports = InvitesServiceProvider; +async function getAppInvite(inviteUuid) { + const invite = await AppInviteCreated.findOne({ + where: { uuid: inviteUuid }, + }); + return invite; +} + +async function isAppInviteSpent(appInviteUuid) { + const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({ + where: { + app_invite_uuid: appInviteUuid, + }, + }); + + if (signUpChallengeCompleted) { + return true; + } + return false; +} + +async function createAppInvite(inviterPubKey) { + return await AppInviteCreated.create({ + uuid: uuid.v7(), + inviter_pub_key: inviterPubKey, + created_at: new Date().toISOString(), + }); +} + +async function createSignUpChallenge(appInviteUuid) { + if (!(await appInviteExists(appInviteUuid))) { + throw new errors.NotFoundError("Invite doesn't exist."); + } + + if (await isAppInviteSpent(appInviteUuid)) { + throw new errors.AlreadyUsedError('Invite has already been used.'); + } + + const nostrChallenge = await nostrService.createNostrChallenge(); + + return await SignUpChallengeCreated.create({ + uuid: uuid.v7(), + nostr_challenge_uuid: nostrChallenge.uuid, + app_invite_uuid: appInviteUuid, + created_at: new Date().toISOString(), + }); +} + +async function verifySignUpChallenge(signedEvent) { + const challengeTag = signedEvent.tags.find((tag) => tag[0] === 'challenge'); + const challenge = challengeTag[1]; + + const nostrChallenge = await nostrService.getNostrChallenge(null, challenge); + + const signUpChallenge = await SignUpChallengeCreated.findOne({ + where: { + nostr_challenge_uuid: nostrChallenge.uuid, + }, + }); + + if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) { + throw new errors.AlreadyUsedError('This challenge has already been used.'); + } + + const completedNostrChallenge = + await nostrService.verifyNostrChallenge(signedEvent); + + const completedSignUpChallenge = await SignUpChallengeCompleted.create({ + uuid: uuid.v7(), + nostr_challenge_completed_uuid: completedNostrChallenge.uuid, + app_invite_uuid: signUpChallenge.app_invite_uuid, + public_key: completedNostrChallenge.public_key, + created_at: new Date().toISOString(), + }); + + return completedSignUpChallenge; +} + +async function isPublicKeySignedUp(publicKey) { + const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({ + where: { + public_key: publicKey, + }, + }); + + if (signUpChallengeCompleted) { + return true; + } + + return false; +} + +module.exports = { + appInviteExists, + getAppInvite, + isAppInviteSpent, + createAppInvite, + createSignUpChallenge, + verifySignUpChallenge, + isPublicKeySignedUp, +}; diff --git a/src/services/loginService.js b/src/services/loginService.js index 73a0b58..9d5f973 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -1,64 +1,58 @@ const uuid = require('uuid'); -class LoginServiceProvider { - constructor({ models, errors, nostrService, invitesService }) { - this.models = models; - this.errors = errors; - this.nostrService = nostrService; - this.invitesService = invitesService; - } +const nostrService = require('./nostrService'); +const invitesService = require('./invitesService'); +const LoginChallengeCreated = require('../models/LoginChallengeCreated'); +const LoginChallengeCompleted = require('../models/LoginChallengeCompleted'); - provide() { - const createLoginChallenge = async () => { - const nostrChallenge = await this.nostrService.createNostrChallenge(); +const errors = require('../errors'); - return await this.models.LoginChallengeCreated.create({ - uuid: uuid.v7(), - nostr_challenge_uuid: nostrChallenge.uuid, - created_at: new Date().toISOString(), - }); - }; +async function createLoginChallenge() { + const nostrChallenge = await nostrService.createNostrChallenge(); - const verifyLoginChallenge = async (signedEvent) => { - const challengeTag = signedEvent.tags.find( - (tag) => tag[0] === 'challenge' - ); - const challenge = challengeTag[1]; - - if (await this.nostrService.hasNostrChallengeBeenCompleted(challenge)) { - throw new this.errors.AlreadyUsedError( - 'This challenge has already been used.' - ); - } - - const completedNostrChallenge = - await this.nostrService.verifyNostrChallenge(signedEvent); - - if ( - !(await this.invitesService.isPublicKeySignedUp( - completedNostrChallenge.public_key - )) - ) { - throw new this.errors.ForbiddenError( - `Public key ${completedNostrChallenge.public_key} is not authorized.` - ); - } - - const completedLoginChallenge = - await this.models.LoginChallengeCompleted.create({ - uuid: uuid.v7(), - nostr_challenge_completed_uuid: completedNostrChallenge.uuid, - public_key: completedNostrChallenge.public_key, - created_at: new Date().toISOString(), - }); - - return completedLoginChallenge; - }; - - return { - createLoginChallenge, - verifyLoginChallenge, - }; - } + return await LoginChallengeCreated.create({ + uuid: uuid.v7(), + nostr_challenge_uuid: nostrChallenge.uuid, + created_at: new Date().toISOString(), + }); } -module.exports = LoginServiceProvider; + +async function verifyLoginChallenge(signedEvent) { + const challengeTag = signedEvent.tags.find((tag) => tag[0] === 'challenge'); + const challenge = challengeTag[1]; + + if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) { + throw new errors.AlreadyUsedError('This challenge has already been used.'); + } + + const completedNostrChallenge = + await nostrService.verifyNostrChallenge(signedEvent); + + if ( + !(await invitesService.isPublicKeySignedUp( + completedNostrChallenge.public_key + )) + ) { + console.log('helo4'); + throw new errors.ForbiddenError( + `Public key ${completedNostrChallenge.public_key} is not authorized.` + ); + } + + const completedLoginChallenge = await LoginChallengeCompleted.create({ + uuid: uuid.v7(), + nostr_challenge_completed_uuid: completedNostrChallenge.uuid, + public_key: completedNostrChallenge.public_key, + created_at: new Date().toISOString(), + }); + + console.log('helo3'); + console.log(completedLoginChallenge); + + return completedLoginChallenge; +} + +module.exports = { + createLoginChallenge, + verifyLoginChallenge, +}; diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 7d87ea6..542ba1c 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -1,126 +1,113 @@ const uuid = require('uuid'); const crypto = require('crypto'); -const { Op } = require('sequelize'); +const { Op, TimeoutError } = require('sequelize'); const { verifyEvent } = require('nostr-tools'); -class NostrServiceProvider { - constructor({ models, constants, errors }) { - this.models = models; - this.constants = constants; - this.errors = errors; - } +const NostrChallengeCreated = require('../models/NostrChallengeCreated'); +const NostrChallengeCompleted = require('../models/NostrChallengeCompleted'); - provide() { - const createNostrChallenge = async () => { - const currentTimestamp = new Date(); - const expiryTimestamp = new Date(currentTimestamp.getTime()); - expiryTimestamp.setSeconds( - expiryTimestamp.getSeconds() + - this.constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS - ); +const constants = require('../constants'); +const errors = require('../errors'); - const nostrChallenge = await this.models.NostrChallengeCreated.create({ - uuid: uuid.v7(), - challenge: crypto.randomBytes(32).toString('hex'), - expires_at: expiryTimestamp.toISOString(), - created_at: currentTimestamp.toISOString(), - }); +async function createNostrChallenge() { + const currentTimestamp = new Date(); + const expiryTimestamp = new Date(currentTimestamp.getTime()); + expiryTimestamp.setSeconds( + expiryTimestamp.getSeconds() + + constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS + ); - return nostrChallenge; - }; + const nostrChallenge = await NostrChallengeCreated.create({ + uuid: uuid.v7(), + challenge: crypto.randomBytes(32).toString('hex'), + expires_at: expiryTimestamp.toISOString(), + created_at: currentTimestamp.toISOString(), + }); - const getNostrChallenge = async ( - nostrChallengeUuid = null, - challenge = null - ) => { - if (nostrChallengeUuid) { - return await this.models.NostrChallengeCreated.findOne({ - where: { - uuid: nostrChallengeUuid, - }, - }); - } - - if (challenge) { - return await this.models.NostrChallengeCreated.findOne({ - where: { - challenge, - }, - }); - } - - throw Error('You need to pass a uuid or a challenge.'); - }; - - const verifyNostrChallenge = async (signedEvent) => { - const challengeTag = signedEvent.tags.find( - (tag) => tag[0] === 'challenge' - ); - const challenge = challengeTag[1]; - - if (!(await isNostrChallengeFresh(challenge))) { - throw new this.errors.ExpiredError( - 'Challenge expired, request new one.' - ); - } - - if (await hasNostrChallengeBeenCompleted(challenge)) { - throw new this.errors.AlreadyUsedError( - 'Challenge already used, request new one.' - ); - } - - const isSignatureValid = verifyEvent(signedEvent); - if (!isSignatureValid) { - throw new this.errors.InvalidSignatureError('Signature is not valid.'); - } - - return await this.models.NostrChallengeCompleted.create({ - uuid: uuid.v7(), - challenge: challenge, - signed_event: signedEvent, - public_key: signedEvent.pubkey, - created_at: new Date().toISOString(), - }); - }; - - const isNostrChallengeFresh = async (challengeString) => { - const nostrChallenge = await this.models.NostrChallengeCreated.findOne({ - where: { - challenge: challengeString, - expires_at: { - [Op.gt]: new Date(), - }, - }, - }); - - if (nostrChallenge) { - return true; - } - return false; - }; - - const hasNostrChallengeBeenCompleted = async (challengeString) => { - const completedNostrChallenge = - await this.models.NostrChallengeCompleted.findOne({ - where: { - challenge: challengeString, - }, - }); - - if (completedNostrChallenge) { - return true; - } - return false; - }; - - return { - createNostrChallenge, - getNostrChallenge, - verifyNostrChallenge, - isNostrChallengeFresh, - hasNostrChallengeBeenCompleted, - }; - } + return nostrChallenge; } -module.exports = NostrServiceProvider; + +async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) { + if (nostrChallengeUuid) { + return await NostrChallengeCreated.findOne({ + where: { + uuid: nostrChallengeUuid, + }, + }); + } + + if (challenge) { + return await NostrChallengeCreated.findOne({ + where: { + challenge, + }, + }); + } + + throw Error('You need to pass a uuid or a challenge.'); +} + +async function verifyNostrChallenge(signedEvent) { + const challengeTag = signedEvent.tags.find((tag) => tag[0] === 'challenge'); + const challenge = challengeTag[1]; + + if (!(await isNostrChallengeFresh(challenge))) { + throw TimeoutError('Challenge expired, request new one.'); + } + + if (await hasNostrChallengeBeenCompleted(challenge)) { + throw new errors.AlreadyUsedError( + 'Challenge already used, request new one.' + ); + } + + const isSignatureValid = verifyEvent(signedEvent); + if (!isSignatureValid) { + throw new errors.InvalidSignatureError('Signature is not valid.'); + } + + return await NostrChallengeCompleted.create({ + uuid: uuid.v7(), + challenge: challenge, + signed_event: signedEvent, + public_key: signedEvent.pubkey, + created_at: new Date().toISOString(), + }); +} + +async function isNostrChallengeFresh(challengeString) { + const nostrChallenge = await NostrChallengeCreated.findOne({ + where: { + challenge: challengeString, + expires_at: { + [Op.gt]: new Date(), + }, + }, + }); + + if (nostrChallenge) { + return true; + } + return false; +} + +async function hasNostrChallengeBeenCompleted(challengeString) { + const completedNostrChallenge = await NostrChallengeCompleted.findOne({ + where: { + challenge: challengeString, + }, + }); + + if (completedNostrChallenge) { + return true; + } + return false; +} + +module.exports = { + createNostrChallenge, + getNostrChallenge, + verifyNostrChallenge, + isNostrChallengeFresh, + hasNostrChallengeBeenCompleted, +}; diff --git a/src/services/offerService.js b/src/services/offerService.js index bd65fd8..cca3b39 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -1,126 +1,30 @@ const uuid = require('uuid'); -class OfferServiceProvider { - constructor({ models, errors, sequelize }) { - this.models = models; - this.errors = errors; - this.sequelize = sequelize; - } - provide() { - const createOffer = async (publicKey, offerDetails) => { - const createOfferTransaction = await this.sequelize.transaction(); - try { - const offerCreated = await this.models.OfferCreated.create( - { - uuid: uuid.v7(), - public_key: publicKey, - created_at: new Date().toISOString(), - }, - { transaction: createOfferTransaction } - ); +const OfferCreated = require('../models/OfferCreated'); +const OfferDetailsSet = require('../models/OfferDetailsSet'); - await this.models.OfferDetailsSet.create( - { - uuid: uuid.v7(), - offer_uuid: offerCreated.uuid, - wants: offerDetails.wants, - premium: offerDetails.premium, - trade_amount_eur: offerDetails.trade_amount_eur, - location_details: offerDetails.location_details, - time_availability_details: offerDetails.time_availability_details, - show_offer_to_trusted: offerDetails.show_offer_to_trusted, - show_offer_to_trusted_trusted: - offerDetails.show_offer_to_trusted_trusted, - show_offer_to_all_members: offerDetails.show_offer_to_all_members, - is_onchain_accepted: offerDetails.is_onchain_accepted, - is_lightning_accepted: offerDetails.is_lightning_accepted, - are_big_notes_accepted: offerDetails.are_big_notes_accepted, - created_at: new Date().toISOString(), - }, - { transaction: createOfferTransaction } - ); - await createOfferTransaction.commit(); - } catch (error) { - await createOfferTransaction.rollback(); - } - }; +async function createOffer(publicKey, offerDetails) { + const offerCreated = await OfferCreated.create({ + uuid: uuid.v7(), + public_key: publicKey, + created_at: new Date().toISOString(), + }); - const deleteOffer = async (offerUuid) => { - const offerExists = Boolean( - await this.models.OfferCreated.findOne({ where: { uuid: offerUuid } }) - ); - const offerHasBeenDeleted = Boolean( - await this.models.OfferDeleted.findOne({ - where: { offer_uuid: offerUuid }, - }) - ); - - if (!offerExists || offerHasBeenDeleted) { - throw new this.errors.NotFoundError(`Could not find the offer.`); - } - - return this.models.OfferDeleted.create({ - uuid: uuid.v7(), - offer_uuid: offerUuid, - created_at: new Date().toISOString(), - }); - }; - - const getActiveOffersByPublicKey = async (publicKey) => { - const activeOffers = await this.models.OfferCreated.findAll({ - where: { - public_key: publicKey, - '$OfferDeleted.uuid$': null, - }, - include: { model: this.models.OfferDeleted, required: false }, - }); - - console.log(activeOffers); - - if (!activeOffers) { - return []; - } - - const offersToReturn = []; - if (activeOffers) { - for (const someOffer of activeOffers) { - const offerDetails = await this.models.OfferDetailsSet.findOne({ - where: { - offer_uuid: someOffer.uuid, - }, - order: [['created_at', 'DESC']], - }); - - offersToReturn.push({ - uuid: someOffer.uuid, - public_key: someOffer.public_key, - wants: offerDetails.wants, - premium: offerDetails.premium, - trade_amount_eur: offerDetails.trade_amount_eur, - location_details: offerDetails.location_details, - time_availability_details: offerDetails.time_availability_details, - show_offer_to_trusted: offerDetails.show_offer_to_trusted, - show_offer_to_trusted_trusted: - offerDetails.show_offer_to_trusted_trusted, - show_offer_to_all_members: offerDetails.show_offer_to_all_members, - is_onchain_accepted: offerDetails.is_onchain_accepted, - is_lightning_accepted: offerDetails.is_lightning_accepted, - are_big_notes_accepted: offerDetails.are_big_notes_accepted, - created_at: someOffer.created_at, - last_updated_at: offerDetails.created_at, - }); - } - } - - return offersToReturn; - }; - - return { - createOffer, - getActiveOffersByPublicKey, - deleteOffer, - }; - } + const offerDetailsSet = await OfferDetailsSet.create({ + uuid: uuid.v7(), + offer_uuid: offerCreated.uuid, + wants: offerDetails.wants, + premium: offerDetails.premium, + trade_amount_eur: offerDetails.trade_amount_eur, + location_details: offerDetails.location_details, + time_availability_details: offerDetails.time_availability_details, + show_offer_to_trusted: offerDetails.show_offer_to_trusted, + show_offer_to_trusted_trusted: offerDetails.show_offer_to_trusted_trusted, + show_offer_to_all_members: offerDetails.show_offer_to_all_members, + is_onchain_accepted: offerDetails.is_onchain_accepted, + is_lightning_accepted: offerDetails.is_lightning_accepted, + are_big_notes_accepted: offerDetails.are_big_notes_accepted, + created_at: new Date().toISOString(), + }); } - -module.exports = OfferServiceProvider; +module.exports = { createOffer }; diff --git a/src/services/profileService.js b/src/services/profileService.js index aca4247..5db0c24 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -1,44 +1,38 @@ const uuid = require('uuid'); +const ContactDetailsSet = require('../models/ContactDetailsSet'); +const NymSet = require('../models/NymSet'); -class ProfileServiceProvider { - constructor({ models }) { - this.models = models; - } - provide() { - const setContactDetails = async (publicKey, encryptedContactDetails) => { - return await this.models.ContactDetailsSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - encrypted_contact_details: encryptedContactDetails, - created_at: new Date().toISOString(), - }); - }; - - const setNym = async (publicKey, nym) => { - return await this.models.NymSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - nym: nym, - created_at: new Date().toISOString(), - }); - }; - - const areProfileDetailsComplete = async (publicKey) => { - console.log(this.models); - const isNymSet = await this.models.NymSet.findOne({ - where: { public_key: publicKey }, - }); - const areContactDetailsSet = await this.models.ContactDetailsSet.findOne({ - where: { - public_key: publicKey, - }, - }); - - return isNymSet && areContactDetailsSet; - }; - - return { setContactDetails, setNym, areProfileDetailsComplete }; - } +async function setContactDetails(publicKey, encryptedContactDetails) { + return await ContactDetailsSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + encrypted_contact_details: encryptedContactDetails, + created_at: new Date().toISOString(), + }); } -module.exports = ProfileServiceProvider; +async function setNym(publicKey, nym) { + return await NymSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + nym: nym, + created_at: new Date().toISOString(), + }); +} + +async function areProfileDetailsComplete(publicKey) { + const isNymSet = await NymSet.findOne({ where: { public_key: publicKey } }); + const areContactDetailsSet = await ContactDetailsSet.findOne({ + where: { + public_key: publicKey, + }, + }); + + return isNymSet && areContactDetailsSet; +} + +module.exports = { + setContactDetails, + setNym, + areProfileDetailsComplete, +}; diff --git a/src/services/sessionService.js b/src/services/sessionService.js index e731846..452f390 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -1,101 +1,92 @@ const uuid = require('uuid'); -class SessionServiceProvider { - constructor({ models, constants, invitesService }) { - this.models = models; - this.constants = constants; - this.invitesService = invitesService; - } +const SessionCreated = require('../models/SessionCreated'); +const SessionRelatedToPublickey = require('../models/SessionRelatedToPublickey'); - provide() { - const createSession = async (sessionUuid) => { - const currentTimestamp = new Date(); - const expiryTimestamp = new Date(currentTimestamp.getTime()); - expiryTimestamp.setSeconds( - expiryTimestamp.getSeconds() + - this.constants.DEFAULT_SESSION_DURATION_SECONDS - ); +const invitesService = require('./invitesService'); +const constants = require('../constants'); - return await this.models.SessionCreated.create({ - uuid: sessionUuid, - created_at: currentTimestamp.toISOString(), - expires_at: expiryTimestamp.toISOString(), - }); - }; +async function createSession(sessionUuid) { + const currentTimestamp = new Date(); + const expiryTimestamp = new Date(currentTimestamp.getTime()); + expiryTimestamp.setSeconds( + expiryTimestamp.getSeconds() + constants.DEFAULT_SESSION_DURATION_SECONDS + ); - const isSessionValid = async (sessionUuid) => { - const currentSession = await this.models.SessionCreated.findOne({ - where: { - uuid: sessionUuid, - }, - }); - - if (!currentSession) { - return false; - } - - if (currentSession.expires_at <= new Date()) { - return false; - } - - return true; - }; - - const relateSessionToPublicKey = async (sessionUuid, publicKey) => { - if (!(await isSessionValid(sessionUuid))) { - throw Error('Session is not valid anymore.'); - } - - if (!(await this.invitesService.isPublicKeySignedUp(publicKey))) { - throw Error('Public key is not signed up.'); - } - - return this.models.SessionRelatedToPublickey.create({ - uuid: uuid.v7(), - session_uuid: sessionUuid, - public_key: publicKey, - created_at: new Date().toISOString(), - }); - }; - - const isSessionAuthorized = async (sessionUuid) => { - const isSessionRelatedToPublicKey = - await this.models.SessionRelatedToPublickey.findOne({ - where: { - session_uuid: sessionUuid, - }, - }); - - if (isSessionRelatedToPublicKey) { - return true; - } - - return false; - }; - - const getPublicKeyRelatedToSession = async (sessionUuid) => { - const sessionRelatedToPublickey = - await this.models.SessionRelatedToPublickey.findOne({ - where: { - session_uuid: sessionUuid, - }, - }); - - if (sessionRelatedToPublickey) { - return sessionRelatedToPublickey.public_key; - } - - return null; - }; - - return { - createSession, - isSessionValid, - relateSessionToPublicKey, - isSessionAuthorized, - getPublicKeyRelatedToSession, - }; - } + return await SessionCreated.create({ + uuid: sessionUuid, + created_at: currentTimestamp.toISOString(), + expires_at: expiryTimestamp.toISOString(), + }); } -module.exports = SessionServiceProvider; +async function isSessionValid(sessionUuid) { + const currentSession = await SessionCreated.findOne({ + where: { + uuid: sessionUuid, + }, + }); + + if (!currentSession) { + return false; + } + + if (currentSession.expires_at <= new Date()) { + return false; + } + + return true; +} + +async function relateSessionToPublicKey(sessionUuid, publicKey) { + if (!(await isSessionValid(sessionUuid))) { + throw Error('Session is not valid anymore.'); + } + + if (!(await invitesService.isPublicKeySignedUp(publicKey))) { + throw Error('Public key is not signed up.'); + } + + return SessionRelatedToPublickey.create({ + uuid: uuid.v7(), + session_uuid: sessionUuid, + public_key: publicKey, + created_at: new Date().toISOString(), + }); +} + +async function isSessionAuthorized(sessionUuid) { + const isSessionRelatedToPublicKey = await SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); + + if (isSessionRelatedToPublicKey) { + return true; + } + + return false; +} + +async function getPublicKeyRelatedToSession(sessionUuid) { + const sessionRelatedToPublickey = await SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); + + if (sessionRelatedToPublickey) { + return sessionRelatedToPublickey.public_key; + } + + return null; +} + +module.exports = { + createSession, + isSessionValid, + relateSessionToPublicKey, + isSessionAuthorized, + getPublicKeyRelatedToSession, +}; diff --git a/src/views/createOffer.ejs b/src/views/createOffer.ejs new file mode 100644 index 0000000..3ea5150 --- /dev/null +++ b/src/views/createOffer.ejs @@ -0,0 +1,25 @@ + + + + Seca home + + + + + + +
+

la seca

+ + + + +
+
+ +
+ + + diff --git a/src/views/createProfile.ejs b/src/views/createProfile.ejs index d4be5ec..87b57ef 100644 --- a/src/views/createProfile.ejs +++ b/src/views/createProfile.ejs @@ -4,131 +4,131 @@ Crear perfil - <%- include("partials/commonStyles") %> - +
-
-
-

Crea tu perfil

-

Tu clave de Nostr ya es parte de la seca.

-

Añade detalles a tu perfil para poder empezar a comerciar.

-
-
-
-
- - -
-
-

Métodos de contacto

-
-
-

- Tus métodos de contacto son necesarios para hablar con otros - miembros y poder comerciar. -

-

- Tus métodos de contacto se guardan encriptados con tu clave de - nostr. laseca nunca puede verlos. Solo los miembros con los - que tú decidas podrán verlos. -

-

- Debes añadir al menos un método de contacto para completar tu - perfil. -

-
- -
-
- -

Teléfono

- -
-
- -

Whatsapp

- -
-
- -

Telegram

- -
-
- -

Email

- -
-
- -

Nostr

- -
-
- -

Signal

- -
-
-
- -
+
+

Crea tu perfil

+

Tu clave de Nostr ya es parte de la seca.

+

Añade detalles a tu perfil para poder empezar a comerciar.

-
- -

- ¡Bien! Tu perfil está completo. Te estamos llevando a la aplicación... -

+
+
+

Perfil

+ + +
+
+
+
+

Datos de contacto

+
+

+ Añade métodos de contacto para poder hablar con otros miembros. + Debes añadir al menos uno. Siempre puedes editarlos más adelante. +

+ + + +
+

+ Tus métodos de contacto son necesarios para hablar con otros + miembros y poder comerciar. +

+

+ Tus métodos de contacto se guardan encriptados con tu clave de + nostr. La seca no puede verlos. +

+

Solo los miembros con los que tú decidas podrán verlos.

+
+ +
+
+ +

Teléfono

+ +
+
+ +

Whatsapp

+ +
+
+ +

Telegram

+ +
+
+ +

Email

+ +
+
+ +

Nostr

+ +
+
+ +

Signal

+ +
+
+
+ +
- + diff --git a/src/views/home.ejs b/src/views/home.ejs index a764e25..9584e63 100644 --- a/src/views/home.ejs +++ b/src/views/home.ejs @@ -11,6 +11,6 @@ <%- include("partials/appCommonHeader") %> <%- include("partials/appCommonScripts") %> - + diff --git a/src/views/invite.ejs b/src/views/invite.ejs index b7815ab..0cd5df1 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -4,26 +4,97 @@ Invite Details - <%- include("partials/commonStyles") %> - + +
-
+

¡Has sido invitado a la seca!

- +

Usa tu extensión de Nostr para darte de alta:

-
-
- +
+ +
+ + +

¿No tienes cuenta de Nostr?

- diff --git a/src/views/login.ejs b/src/views/login.ejs index 080f8e9..a4467ec 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -1,23 +1,91 @@ - + Hello World -
- - + diff --git a/src/views/offers.ejs b/src/views/offers.ejs index aba756c..7bed996 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -4,7 +4,7 @@ Seca home - <%- include("partials/commonStyles") %> + @@ -12,165 +12,174 @@ <%- include("partials/appCommonHeader") %>
- -
-
-

Mis ofertas

-
-

Vaya, no hay nada por aquí...

-
-
-
-
-
-

Añade los detalles de tu oferta

-
-
-

Premium

-
-
-
-

¿Cuánto?

-
-
- -
- -
-
-
- -
- SAT -
+ +