From ebd5f390e63ee6555659f44fe577039cb93f5435 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 13:56:26 +0100 Subject: [PATCH 001/176] ignore test results --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 43ddd2f..01f0e83 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,4 @@ dist .pnp.* -tests-results/ \ No newline at end of file +tests-results/* \ No newline at end of file From 62b3d73a819dc47876a33ebe01995aec8cc4b809 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 13:56:40 +0100 Subject: [PATCH 002/176] typo --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 01f0e83..ec88182 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,4 @@ dist .pnp.* -tests-results/* \ No newline at end of file +test-results/* \ No newline at end of file From 926dc3f3dd4e02a3c5a196bdcdccebfd1e0478ce Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 13:57:09 +0100 Subject: [PATCH 003/176] make sequelize STFU --- src/database.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/database.js b/src/database.js index b90f77f..133328f 100644 --- a/src/database.js +++ b/src/database.js @@ -10,6 +10,11 @@ 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, From f00a3aa103c0273163ea82ebc2616aaca6c6e572 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 13:57:24 +0100 Subject: [PATCH 004/176] remove old dirt --- tests/app.spec.js | 8 -------- tests/createOffer.spec.js | 18 ++++++++++++++++ tests/test-setup.js | 43 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) delete mode 100644 tests/app.spec.js create mode 100644 tests/createOffer.spec.js create mode 100644 tests/test-setup.js diff --git a/tests/app.spec.js b/tests/app.spec.js deleted file mode 100644 index 9fdadf6..0000000 --- a/tests/app.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -const { test, expect } = require('@playwright/test'); - -test('Home page has correct title', async ({ page }) => { - await page.goto('http://localhost/'); - const title = await page.title(); - - expect(title).toBe('Hello World'); -}); diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js new file mode 100644 index 0000000..06d68e7 --- /dev/null +++ b/tests/createOffer.spec.js @@ -0,0 +1,18 @@ +const { test, expect } = require('./test-setup'); +const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); +const NymSet = require('../src/models/NymSet'); +const ContactDetailsSet = require('../src/models/ContactDetailsSet'); + +test('Mock ecords are present', async ({ page }) => { + for (const someModel of [ + SessionRelatedToPublickey, + NymSet, + ContactDetailsSet, + ]) { + expect(await someModel.findOne()).toBeTruthy(); + } +}); + +// Check that UI controls are manageable with a few test cases. + +// Check that the offer is created after submitting diff --git a/tests/test-setup.js b/tests/test-setup.js new file mode 100644 index 0000000..186d166 --- /dev/null +++ b/tests/test-setup.js @@ -0,0 +1,43 @@ +import { test } from '@playwright/test'; +const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); +const NymSet = require('../src/models/NymSet'); +const ContactDetailsSet = require('../src/models/ContactDetailsSet'); + +test.beforeEach(async () => { + // Truncate all affected tables + + for (const someModel of [ + SessionRelatedToPublickey, + NymSet, + ContactDetailsSet, + ]) { + someModel.truncate(); + } + + await SessionRelatedToPublickey.create({ + uuid: '0195423b-f9ae-737e-98f3-880f6563ed8a', + session_uuid: '0195423c-33d7-75f8-921b-a06e6d3cb8c5', + public_key: + 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + created_at: new Date().toISOString(), + }); + + await NymSet.create({ + uuid: '01954240-ddbb-7d01-9017-efb3e500d333', + public_key: + 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + nym: 'test_nym', + created_at: new Date().toISOString(), + }); + + await ContactDetailsSet.create({ + uuid: '01954240-ddbb-7d01-9017-efb3e500d333', + public_key: + 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + encrypted_contact_details: + '+OD0/Y2IkJ99/E0KAJL/mp3kxQo4DFp1deSPnqiejlyGoeWzBiipemPVSTT/Jg/fCQbN9Pd/GJ6shxuwWECOVyB5PnMZOVJ1MPQ7I8A+63XZ0gKnSnJgry6F69f3MhEjH49JbeVJ37TbruFu/Woevo24VWz2gPXGBuyHLzeg1tyT9+7ZSygkcCrh+bchvymCoF1nNOm/UQKnwecH1wWzo8a+rNokazD1/3iey6iKmKewi+yGCgmljrB866akqBAl?iv=PAKhqTeBfYVX/muhM8xaEA==', + created_at: new Date().toISOString(), + }); +}); + +export { test, expect } from '@playwright/test'; From c7505f50093144e347cf022f494cffe389ccd500 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 14:20:53 +0100 Subject: [PATCH 005/176] add cookie to fixture --- tests/createOffer.spec.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index 06d68e7..609cf71 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -1,9 +1,10 @@ const { test, expect } = require('./test-setup'); + const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); -test('Mock ecords are present', async ({ page }) => { +test('Mock records are present', async ({ page }) => { for (const someModel of [ SessionRelatedToPublickey, NymSet, @@ -13,6 +14,19 @@ test('Mock ecords are present', async ({ page }) => { } }); +test('Session cookie is there', async ({ page }) => { + await page.goto('http://localhost'); + + const cookiesInPage = await page.context().cookies(); + expect(cookiesInPage).toHaveLength(1); + expect(cookiesInPage[0].name).toBe('sessionUuid'); +}); + +test('Offers is reachable', async ({ page }) => { + await page.goto('http://localhost/offers'); + console.log(await page.innerHTML('html')); +}); + // Check that UI controls are manageable with a few test cases. // Check that the offer is created after submitting From a1e07724ce8881018407dd41fddfb44ef74beb62 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 14:21:40 +0100 Subject: [PATCH 006/176] add cookie to fixture --- tests/test-setup.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/test-setup.js b/tests/test-setup.js index 186d166..873667b 100644 --- a/tests/test-setup.js +++ b/tests/test-setup.js @@ -3,9 +3,7 @@ const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublick const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); -test.beforeEach(async () => { - // Truncate all affected tables - +test.beforeEach(async ({ context }) => { for (const someModel of [ SessionRelatedToPublickey, NymSet, @@ -14,7 +12,7 @@ test.beforeEach(async () => { someModel.truncate(); } - await SessionRelatedToPublickey.create({ + const session = await SessionRelatedToPublickey.create({ uuid: '0195423b-f9ae-737e-98f3-880f6563ed8a', session_uuid: '0195423c-33d7-75f8-921b-a06e6d3cb8c5', public_key: @@ -38,6 +36,18 @@ test.beforeEach(async () => { '+OD0/Y2IkJ99/E0KAJL/mp3kxQo4DFp1deSPnqiejlyGoeWzBiipemPVSTT/Jg/fCQbN9Pd/GJ6shxuwWECOVyB5PnMZOVJ1MPQ7I8A+63XZ0gKnSnJgry6F69f3MhEjH49JbeVJ37TbruFu/Woevo24VWz2gPXGBuyHLzeg1tyT9+7ZSygkcCrh+bchvymCoF1nNOm/UQKnwecH1wWzo8a+rNokazD1/3iey6iKmKewi+yGCgmljrB866akqBAl?iv=PAKhqTeBfYVX/muhM8xaEA==', created_at: new Date().toISOString(), }); + + await context.addCookies([ + { + name: 'sessionUuid', + value: session.uuid, + domain: 'localhost', + path: '/', + httpOnly: true, + secure: false, + sameSite: 'None', + }, + ]); }); export { test, expect } from '@playwright/test'; From 1b5f22731800930cd028296b352e2ca8e2896780 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 17:22:37 +0100 Subject: [PATCH 007/176] finally some bloody test works --- tests/createOffer.spec.js | 27 ++++++++++++++++---------- tests/test-setup.js | 40 +++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index 609cf71..236610f 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -1,11 +1,15 @@ -const { test, expect } = require('./test-setup'); +const { test, expect, hardcodedSessionUuid } = require('./test-setup'); +const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); +const sessionService = require('../src/services/sessionService'); + test('Mock records are present', async ({ page }) => { for (const someModel of [ + SessionCreated, SessionRelatedToPublickey, NymSet, ContactDetailsSet, @@ -14,19 +18,22 @@ test('Mock records are present', async ({ page }) => { } }); -test('Session cookie is there', async ({ page }) => { - await page.goto('http://localhost'); - +test('Hardcoded session cookie is there', async ({ context }) => { + const page = await context.newPage(); const cookiesInPage = await page.context().cookies(); expect(cookiesInPage).toHaveLength(1); expect(cookiesInPage[0].name).toBe('sessionUuid'); + expect(cookiesInPage[0].value).toBe(hardcodedSessionUuid); }); -test('Offers is reachable', async ({ page }) => { +test('Offers is reachable', async ({ context }) => { + const page = await context.newPage(); + const cookiez = await page.context().cookies(); + + await page.pause(); await page.goto('http://localhost/offers'); - console.log(await page.innerHTML('html')); + + const createOfferButton = page.locator('#button-start-create-offer'); + await expect(createOfferButton).toBeVisible(); + await expect(createOfferButton).toContainText('Crear nueva oferta'); }); - -// Check that UI controls are manageable with a few test cases. - -// Check that the offer is created after submitting diff --git a/tests/test-setup.js b/tests/test-setup.js index 873667b..5e9c7be 100644 --- a/tests/test-setup.js +++ b/tests/test-setup.js @@ -1,10 +1,17 @@ import { test } from '@playwright/test'; + +const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); +const hardcodedSessionUuid = '0195423c-33d7-75f8-921b-a06e6d3cb8c5'; +const hardcodedPublicKey = + 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a'; + test.beforeEach(async ({ context }) => { for (const someModel of [ + SessionCreated, SessionRelatedToPublickey, NymSet, ContactDetailsSet, @@ -12,42 +19,55 @@ test.beforeEach(async ({ context }) => { someModel.truncate(); } - const session = await SessionRelatedToPublickey.create({ + const currentTimestamp = new Date(); + const expiryTimestamp = new Date(currentTimestamp.getTime()); + expiryTimestamp.setSeconds(expiryTimestamp.getSeconds() + 60); + await SessionCreated.create({ + uuid: hardcodedSessionUuid, + created_at: currentTimestamp.toISOString(), + expires_at: expiryTimestamp.toISOString(), + }); + + await SessionRelatedToPublickey.create({ uuid: '0195423b-f9ae-737e-98f3-880f6563ed8a', - session_uuid: '0195423c-33d7-75f8-921b-a06e6d3cb8c5', - public_key: - 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + session_uuid: hardcodedSessionUuid, + public_key: hardcodedPublicKey, created_at: new Date().toISOString(), }); await NymSet.create({ uuid: '01954240-ddbb-7d01-9017-efb3e500d333', - public_key: - 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + public_key: hardcodedPublicKey, nym: 'test_nym', created_at: new Date().toISOString(), }); await ContactDetailsSet.create({ uuid: '01954240-ddbb-7d01-9017-efb3e500d333', - public_key: - 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a', + public_key: hardcodedPublicKey, encrypted_contact_details: '+OD0/Y2IkJ99/E0KAJL/mp3kxQo4DFp1deSPnqiejlyGoeWzBiipemPVSTT/Jg/fCQbN9Pd/GJ6shxuwWECOVyB5PnMZOVJ1MPQ7I8A+63XZ0gKnSnJgry6F69f3MhEjH49JbeVJ37TbruFu/Woevo24VWz2gPXGBuyHLzeg1tyT9+7ZSygkcCrh+bchvymCoF1nNOm/UQKnwecH1wWzo8a+rNokazD1/3iey6iKmKewi+yGCgmljrB866akqBAl?iv=PAKhqTeBfYVX/muhM8xaEA==', created_at: new Date().toISOString(), }); +}); +test.beforeEach(async ({ context }) => { await context.addCookies([ { name: 'sessionUuid', - value: session.uuid, + value: hardcodedSessionUuid, domain: 'localhost', path: '/', + expires: Math.floor( + new Date(new Date().setMonth(new Date().getMonth() + 1)).getTime() / + 1000 + ), //This monster is this day next month, turned into epoch format httpOnly: true, secure: false, - sameSite: 'None', + sameSite: 'Lax', }, ]); }); export { test, expect } from '@playwright/test'; +export { hardcodedSessionUuid }; From 95c02cf7e2b65d6b84304938d3e4bafb5feea5be Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 23:55:52 +0100 Subject: [PATCH 008/176] project fluff --- .gitignore | 8 +++++++- package-lock.json | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index ec88182..846e04f 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,10 @@ dist .pnp.* -test-results/* \ No newline at end of file +test-results/* + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index 5490e56..1c6f215 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "express-hello-world", - "version": "1.0.0", + "name": "laseca", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "express-hello-world", - "version": "1.0.0", + "name": "laseca", + "version": "0.1.0", "license": "ISC", "dependencies": { "commander": "^13.1.0", From 3ac74258245b17415c1dd5d248bf9c3ecf357799 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 23:56:06 +0100 Subject: [PATCH 009/176] remove unused declaration --- src/services/offerService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/offerService.js b/src/services/offerService.js index cca3b39..56ef780 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -10,7 +10,7 @@ async function createOffer(publicKey, offerDetails) { created_at: new Date().toISOString(), }); - const offerDetailsSet = await OfferDetailsSet.create({ + await OfferDetailsSet.create({ uuid: uuid.v7(), offer_uuid: offerCreated.uuid, wants: offerDetails.wants, From c1161e3a6634a952aa92d9596e61ee03a76c36be Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 26 Feb 2025 23:56:14 +0100 Subject: [PATCH 010/176] cool tests!!! --- tests/createOffer.spec.js | 58 ++++++++++++++++++++++++++++++++---- tests/recorderHelper.spec.js | 27 +++++++++++++++++ tests/test-setup.js | 7 ++--- 3 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 tests/recorderHelper.spec.js diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index 236610f..a64c0de 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -5,9 +5,7 @@ const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublick const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); -const sessionService = require('../src/services/sessionService'); - -test('Mock records are present', async ({ page }) => { +test('Mock records are present', async () => { for (const someModel of [ SessionCreated, SessionRelatedToPublickey, @@ -28,12 +26,60 @@ test('Hardcoded session cookie is there', async ({ context }) => { test('Offers is reachable', async ({ context }) => { const page = await context.newPage(); - const cookiez = await page.context().cookies(); - await page.pause(); await page.goto('http://localhost/offers'); - const createOfferButton = page.locator('#button-start-create-offer'); await expect(createOfferButton).toBeVisible(); await expect(createOfferButton).toContainText('Crear nueva oferta'); }); + +test('Create an offer with a few options creates in DB', async ({ + context, +}) => { + const page = await context.newPage(); + await page.goto('http://localhost/offers'); + + await page.getByRole('button', { name: 'Crear nueva oferta' }).click(); + await page.getByRole('button', { name: 'Quiero vender Bitcoin' }).click(); + await page.getByRole('button', { name: 'Quiero comprar Bitcoin' }).click(); + await page.getByRole('button', { name: '+' }).click(); + await page.getByRole('button', { name: '+' }).click(); + await page.getByRole('button', { name: '+' }).click(); + await page.getByRole('button', { name: '-' }).click(); + await expect(page.locator('#premium-value')).toContainText('2%'); + await page.locator('#input-eur-amount').click(); + await page.locator('#input-eur-amount').press('ControlOrMeta+a'); + await page.locator('#input-eur-amount').fill('50'); + await expect(page.locator('#input-eur-amount')).toHaveValue('50'); + await page + .getByText( + 'Añade los detalles de tu oferta Quiero comprar Bitcoin Quiero vender Bitcoin' + ) + .click(); + await page + .getByRole('textbox', { name: '¿Dónde? Ej."Eixample", "La' }) + .click(); + await page + .getByRole('textbox', { name: '¿Dónde? Ej."Eixample", "La' }) + .fill('En algún lugar'); + await page + .getByRole('textbox', { name: '¿Cuándo? Ej."Cualquier hora' }) + .click(); + await page + .getByRole('textbox', { name: '¿Cuándo? Ej."Cualquier hora' }) + .fill('En algún momento'); + await page.locator('#onchain-checkbox').uncheck(); + await expect(page.locator('#onchain-checkbox')).not.toBeChecked(); + await expect(page.locator('#lightning-checkbox')).toBeChecked(); + await page.locator('#input-eur-amount').click(); + await page.locator('#my-trusted-trusted-checkbox').uncheck(); + await page.locator('#all-members-checkbox').check(); + await page.locator('#my-trusted-trusted-checkbox').check(); + await page.locator('#all-members-checkbox').uncheck(); + await page.locator('#large-bills-checkbox').check(); + await page.getByRole('button', { name: 'Publicar oferta' }).click(); + await page.locator('#close-offer-controls-x').click(); + await expect(page.locator('#offers-root')).toMatchAriaSnapshot( + `- button "Crear nueva oferta"` + ); +}); diff --git a/tests/recorderHelper.spec.js b/tests/recorderHelper.spec.js new file mode 100644 index 0000000..2c87c21 --- /dev/null +++ b/tests/recorderHelper.spec.js @@ -0,0 +1,27 @@ +// You can uncomment this below to open a recorder page + +/* +const { chromium } = require('playwright'); +test('Mock records are present', async () => { + const browser = await chromium.launch({ headless: false }); + const context = await browser.newContext(); + await context.addCookies([ + { + name: 'sessionUuid', + value: hardcodedSessionUuid, + domain: 'localhost', + path: '/', + expires: Math.floor( + new Date(new Date().setMonth(new Date().getMonth() + 1)).getTime() / + 1000 + ), //This monster is this day next month, turned into epoch format + httpOnly: true, + secure: false, + sameSite: 'Lax', + }, + ]); + + const page = await context.newPage(); + await page.goto('http://localhost'); +}); +*/ diff --git a/tests/test-setup.js b/tests/test-setup.js index 5e9c7be..3d4b669 100644 --- a/tests/test-setup.js +++ b/tests/test-setup.js @@ -1,4 +1,4 @@ -import { test } from '@playwright/test'; +const { test, expect } = require('@playwright/test'); const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); @@ -9,7 +9,7 @@ const hardcodedSessionUuid = '0195423c-33d7-75f8-921b-a06e6d3cb8c5'; const hardcodedPublicKey = 'd3d4c49e7bdbbbf3082151add080e92f9a458d5dec993b371fe6d02cd394d57a'; -test.beforeEach(async ({ context }) => { +test.beforeEach(async () => { for (const someModel of [ SessionCreated, SessionRelatedToPublickey, @@ -69,5 +69,4 @@ test.beforeEach(async ({ context }) => { ]); }); -export { test, expect } from '@playwright/test'; -export { hardcodedSessionUuid }; +module.exports = { test, expect, hardcodedSessionUuid }; From 7dbf1fd082e0684723ae1f2bcd3e2bfe403ea22e Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 12:24:17 +0100 Subject: [PATCH 011/176] very complete test yes sir --- tests/createOffer.spec.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index a64c0de..38aa863 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -4,6 +4,7 @@ const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); const NymSet = require('../src/models/NymSet'); const ContactDetailsSet = require('../src/models/ContactDetailsSet'); +const OfferDetailsSet = require('../src/models/OfferDetailsSet'); test('Mock records are present', async () => { for (const someModel of [ @@ -71,15 +72,32 @@ test('Create an offer with a few options creates in DB', async ({ await page.locator('#onchain-checkbox').uncheck(); await expect(page.locator('#onchain-checkbox')).not.toBeChecked(); await expect(page.locator('#lightning-checkbox')).toBeChecked(); - await page.locator('#input-eur-amount').click(); await page.locator('#my-trusted-trusted-checkbox').uncheck(); await page.locator('#all-members-checkbox').check(); await page.locator('#my-trusted-trusted-checkbox').check(); + await expect(page.locator('#my-trusted-trusted-checkbox')).toBeChecked(); await page.locator('#all-members-checkbox').uncheck(); + await expect(page.locator('#all-members-checkbox')).not.toBeChecked(); await page.locator('#large-bills-checkbox').check(); + await expect(page.locator('#large-bills-checkbox')).toBeChecked(); await page.getByRole('button', { name: 'Publicar oferta' }).click(); await page.locator('#close-offer-controls-x').click(); await expect(page.locator('#offers-root')).toMatchAriaSnapshot( `- button "Crear nueva oferta"` ); + + const createdOfferDetailsSetRecord = await OfferDetailsSet.findOne(); + expect(createdOfferDetailsSetRecord.wants).toBe('BTC'); + expect(createdOfferDetailsSetRecord.premium).toBe('0.02'); + expect(createdOfferDetailsSetRecord.trade_amount_eur).toBe(50); + expect(createdOfferDetailsSetRecord.location_details).toBe('En algún lugar'); + expect(createdOfferDetailsSetRecord.time_availability_details).toBe( + 'En algún momento' + ); + expect(createdOfferDetailsSetRecord.show_offer_to_trusted).toBe(true); + expect(createdOfferDetailsSetRecord.show_offer_to_trusted_trusted).toBe(true); + expect(createdOfferDetailsSetRecord.show_offer_to_all_members).toBe(false); + expect(createdOfferDetailsSetRecord.is_onchain_accepted).toBe(false); + expect(createdOfferDetailsSetRecord.is_lightning_accepted).toBe(true); + expect(createdOfferDetailsSetRecord.are_big_notes_accepted).toBe(true); }); From e632bbf0e036dfde171c473e5144d8b48a6fd17a Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 12:25:51 +0100 Subject: [PATCH 012/176] remvoe old file --- src/views/createOffer.ejs | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/views/createOffer.ejs diff --git a/src/views/createOffer.ejs b/src/views/createOffer.ejs deleted file mode 100644 index 3ea5150..0000000 --- a/src/views/createOffer.ejs +++ /dev/null @@ -1,25 +0,0 @@ - - - - Seca home - - - - - - -
-

la seca

- - - - -
-
- -
- - - From 35d41827c6bdff0869ec8acfcad4b93582f45e3d Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 12:36:22 +0100 Subject: [PATCH 013/176] a couple of small refactors --- src/views/offers.ejs | 3 +-- src/views/partials/appCommonScripts.ejs | 1 + src/views/partials/commonStyles.ejs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 src/views/partials/commonStyles.ejs diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 7bed996..0a49278 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -4,7 +4,7 @@ Seca home - + <%- include("partials/commonStyles") %> @@ -179,7 +179,6 @@ <%- include("partials/appCommonScripts") %> - diff --git a/src/views/partials/appCommonScripts.ejs b/src/views/partials/appCommonScripts.ejs index 1df2d83..58c2ce7 100644 --- a/src/views/partials/appCommonScripts.ejs +++ b/src/views/partials/appCommonScripts.ejs @@ -1 +1,2 @@ + \ No newline at end of file diff --git a/src/views/partials/commonStyles.ejs b/src/views/partials/commonStyles.ejs new file mode 100644 index 0000000..30c6282 --- /dev/null +++ b/src/views/partials/commonStyles.ejs @@ -0,0 +1 @@ + \ No newline at end of file From 0c038cb03ffddbfc6e2b0206ec4e1efe68744628 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 12:51:22 +0100 Subject: [PATCH 014/176] format --- src/views/partials/appCommonScripts.ejs | 2 +- src/views/partials/commonStyles.ejs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/partials/appCommonScripts.ejs b/src/views/partials/appCommonScripts.ejs index 58c2ce7..9c2482c 100644 --- a/src/views/partials/appCommonScripts.ejs +++ b/src/views/partials/appCommonScripts.ejs @@ -1,2 +1,2 @@ - \ No newline at end of file + diff --git a/src/views/partials/commonStyles.ejs b/src/views/partials/commonStyles.ejs index 30c6282..f3843f5 100644 --- a/src/views/partials/commonStyles.ejs +++ b/src/views/partials/commonStyles.ejs @@ -1 +1 @@ - \ No newline at end of file + From aebfad80d536fcee0277c6e97eb4606c8e5883b9 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 12:51:32 +0100 Subject: [PATCH 015/176] refactor steps styles --- src/public/css/offers.css | 19 +++++++++---------- src/views/offers.ejs | 34 ++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 16fad27..e78855f 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -19,7 +19,7 @@ width: 50%; } - #create-offer-controls > * { + .create-offer-step { width: 100%; } @@ -51,7 +51,7 @@ width: 33%; } - #create-offer-controls > * { + .create-offer-step { margin-left: auto; margin-right: auto; width: 70%; @@ -67,10 +67,12 @@ } } -#create-offer-controls > * { +#create-offer-controls { + text-align: center; +} +.create-offer-step { text-align: center; } - #close-offer-controls-area { display: flex; justify-content: end; @@ -108,6 +110,9 @@ .premium-button { background: white; border: 2px solid #e1c300; + width: 100%; + height: 50%; + font-size: 1em; } .premium-button:hover { @@ -122,12 +127,6 @@ border-bottom-right-radius: 10px; } -.premium-button { - width: 100%; - height: 50%; - font-size: 1em; -} - #premium-price-display-area { margin-left: 0; margin-right: auto; diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 0a49278..da43ce1 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -29,18 +29,20 @@ />

Añade los detalles de tu oferta

-
- +
+
+ +
-
+

Premium

@@ -63,7 +65,7 @@
-
+

¿Cuánto?

@@ -91,7 +93,7 @@
-
+

¿Dónde y cuándo?

-
+

¿Cómo se mueve el Bitcoin?

-
+

¿Quién puede ver la oferta?

-
+

Extras

Date: Thu, 27 Feb 2025 13:02:54 +0100 Subject: [PATCH 016/176] pretty boxes --- src/public/css/offers.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index e78855f..b9b32eb 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -72,7 +72,17 @@ } .create-offer-step { text-align: center; + border-radius: 20px; + box-shadow: 0 0 13px #ccc; + padding: 20px 0; + margin-top: 20px; + margin-bottom: 20px; } + +.create-offer-step h3 { + margin-top: 0; +} + #close-offer-controls-area { display: flex; justify-content: end; From ab9d502945443ffb98e2cf1a9cd2d2f088914f84 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 16:54:05 +0100 Subject: [PATCH 017/176] confirmation popup --- src/public/css/seca.css | 40 ++++++++++++++++++++++++++- src/public/img/circle-check-green.svg | 40 +++++++++++++++++++++++++++ src/public/img/circle-check-white.svg | 40 +++++++++++++++++++++++++++ src/views/offers.ejs | 6 ++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/public/img/circle-check-green.svg create mode 100644 src/public/img/circle-check-white.svg diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 629ba1a..d928f4b 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -14,9 +14,14 @@ .button-medium { height: 3em; - padding: 1em; + padding: 0.5em 1em; border-radius: 10px; } + + .top-notification-good { + top: 25px; + + } } @media (min-width: 769px) { @@ -33,8 +38,41 @@ padding: 1em; border-radius: 10px; } + + .top-notification-good { + top: 50px; + } } +.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: translateX(-50%); +} +.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; +} + + .badges { display: grid; grid-template-columns: repeat(2, 1fr); diff --git a/src/public/img/circle-check-green.svg b/src/public/img/circle-check-green.svg new file mode 100644 index 0000000..75a0c96 --- /dev/null +++ b/src/public/img/circle-check-green.svg @@ -0,0 +1,40 @@ + + + + + + + + diff --git a/src/public/img/circle-check-white.svg b/src/public/img/circle-check-white.svg new file mode 100644 index 0000000..e5427dc --- /dev/null +++ b/src/public/img/circle-check-white.svg @@ -0,0 +1,40 @@ + + + + + + + + diff --git a/src/views/offers.ejs b/src/views/offers.ejs index da43ce1..241ba7d 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -178,6 +178,12 @@
+
+ +

¡Oferta creada! Puedes verla en tus ofertas.

+
<%- include("partials/appCommonScripts") %> From 0b3fe23cd371548cb76f6665334955e192e2422a Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 17:36:22 +0100 Subject: [PATCH 018/176] pop up moves --- src/public/css/seca.css | 8 +++++++- src/public/javascript/offers.js | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/public/css/seca.css b/src/public/css/seca.css index d928f4b..f847728 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -56,7 +56,13 @@ justify-content: space-evenly; align-items: center; left: 50%; - transform: translateX(-50%); + transform: translate(-50%, -200%); + transition: transform 1s ease-in-out; + +} + +.top-notification-good.revealed { + transform: translate(-50%, 0%); } .top-notification-good > * { margin: 0 20px; diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 3a73d12..3f15f18 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -43,6 +43,8 @@ const bigNotesAcceptedCheckbox = document.getElementById( const publishOfferButton = document.getElementById('button-submit-offer'); +const offerCreatedPopup = document.getElementById('offer-created-confirmation'); + function toggleCreateOfferControls() { createOfferControls.style.display = createOfferControls.style.display === 'block' ? 'none' : 'block'; @@ -154,6 +156,14 @@ async function publishOffer() { }, body: JSON.stringify({ offerDetails }), }); + + offerCreatedPopup.classList.add('revealed'); + setTimeout(() => { + offerCreatedPopup.classList.remove('revealed'); + }, 3000); + setTimeout(() => { + offerCreatedPopup.classList.remove('revealed'); + }, 3000); } buttonStartCreateOffer.addEventListener('click', () => { From aec803d321ba429f4bb6ab9bef6c7d9bea5c0fb5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 18:26:36 +0100 Subject: [PATCH 019/176] fix tests --- src/public/css/seca.css | 12 +++++++++--- src/public/javascript/offers.js | 7 +++++-- src/views/offers.ejs | 2 +- tests/createOffer.spec.js | 12 ++++++++---- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/public/css/seca.css b/src/public/css/seca.css index f847728..27ced1a 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -44,6 +44,12 @@ } } + +.max-size-zero { + max-width: 0; + max-height: 0; +} + .top-notification-good { border-radius: 10px; background-color: rgb(9, 165, 9); @@ -56,9 +62,9 @@ justify-content: space-evenly; align-items: center; left: 50%; - transform: translate(-50%, -200%); - transition: transform 1s ease-in-out; - + transform: translate(-50%, -200px); + transition: + transform 2s ease-in-out; } .top-notification-good.revealed { diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 3f15f18..054d7fa 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -157,13 +157,16 @@ async function publishOffer() { body: JSON.stringify({ offerDetails }), }); + offerCreatedPopup.classList.remove('max-size-zero'); offerCreatedPopup.classList.add('revealed'); setTimeout(() => { offerCreatedPopup.classList.remove('revealed'); }, 3000); setTimeout(() => { - offerCreatedPopup.classList.remove('revealed'); - }, 3000); + offerCreatedPopup.classList.add('max-size-zero'); + }, 4000); + + toggleCreateOfferControls(); } buttonStartCreateOffer.addEventListener('click', () => { diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 241ba7d..6737f47 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -178,7 +178,7 @@
-
+
diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index 38aa863..1de4566 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -1,4 +1,5 @@ const { test, expect, hardcodedSessionUuid } = require('./test-setup'); +const { chromium } = require('playwright'); const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); @@ -41,6 +42,7 @@ test('Create an offer with a few options creates in DB', async ({ await page.goto('http://localhost/offers'); await page.getByRole('button', { name: 'Crear nueva oferta' }).click(); + await expect(page.locator('#close-offer-controls-area')).toBeVisible(); await page.getByRole('button', { name: 'Quiero vender Bitcoin' }).click(); await page.getByRole('button', { name: 'Quiero comprar Bitcoin' }).click(); await page.getByRole('button', { name: '+' }).click(); @@ -80,11 +82,13 @@ test('Create an offer with a few options creates in DB', async ({ await expect(page.locator('#all-members-checkbox')).not.toBeChecked(); await page.locator('#large-bills-checkbox').check(); await expect(page.locator('#large-bills-checkbox')).toBeChecked(); + await page.getByRole('button', { name: 'Publicar oferta' }).click(); - await page.locator('#close-offer-controls-x').click(); - await expect(page.locator('#offers-root')).toMatchAriaSnapshot( - `- button "Crear nueva oferta"` - ); + await expect(page.locator('#offer-created-confirmation')).toBeInViewport(); + await expect( + page.locator('#offer-created-confirmation') + ).not.toBeInViewport(); + await expect(page.locator('#close-offer-controls-area')).not.toBeVisible(); const createdOfferDetailsSetRecord = await OfferDetailsSet.findOne(); expect(createdOfferDetailsSetRecord.wants).toBe('BTC'); From 94c2231a57b70fd816f65fa4fc67cd5b210cc308 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 18:38:30 +0100 Subject: [PATCH 020/176] move div out --- src/views/offers.ejs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 6737f47..053a8f7 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -178,12 +178,12 @@
-
- -

¡Oferta creada! Puedes verla en tus ofertas.

-
+
+
+ +

¡Oferta creada! Puedes verla en tus ofertas.

<%- include("partials/appCommonScripts") %> From 7e8b27b49d771ae5e9ae88176e50581379cb6b98 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 18:38:42 +0100 Subject: [PATCH 021/176] show confirmation popup --- src/public/css/seca.css | 4 ++-- src/public/javascript/invite.js | 14 ++++++++++---- src/views/invite.ejs | 11 ++++++----- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 27ced1a..255b284 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -62,7 +62,7 @@ justify-content: space-evenly; align-items: center; left: 50%; - transform: translate(-50%, -200px); + transform: translate(-50%, -500px); transition: transform 2s ease-in-out; } @@ -218,7 +218,7 @@ h1 { padding: 0 10%; } -.invite-card-content * { +.invite-card-content > * { margin: 1vh auto; text-align: center; } diff --git a/src/public/javascript/invite.js b/src/public/javascript/invite.js index 04ef5c8..70bb66d 100644 --- a/src/public/javascript/invite.js +++ b/src/public/javascript/invite.js @@ -8,6 +8,15 @@ window.onload = function () { } }; +const signUpConfirmation = document.querySelector('#sign-up-success'); + +function showConfirmationAndRedirect() { + signUpConfirmation.classList.add('revealed'); + setTimeout(() => { + window.location.href = '/createProfile'; + }, 5000); +} + async function acceptInvite() { let challengeResponse; try { @@ -60,9 +69,6 @@ async function acceptInvite() { } if (verifyResponse.ok) { - document.querySelector('#sign-up-success').style.display = 'block'; - setTimeout(() => { - window.location.href = '/createProfile'; - }, 1000); + showConfirmationAndRedirect(); } } diff --git a/src/views/invite.ejs b/src/views/invite.ejs index 0cd5df1..cce211c 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -4,7 +4,6 @@ Invite Details - @@ -89,11 +88,12 @@

¿No tienes cuenta de Nostr?

@@ -107,5 +107,6 @@

+ From 7f84889968938b42900c910922c9cd361e19ce7d Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 18:40:15 +0100 Subject: [PATCH 022/176] formatting --- src/public/css/invite.css | 0 src/public/css/seca.css | 6 +----- src/views/invite.ejs | 17 ++++++++--------- src/views/offers.ejs | 9 +++++---- tests/createOffer.spec.js | 1 - 5 files changed, 14 insertions(+), 19 deletions(-) create mode 100644 src/public/css/invite.css diff --git a/src/public/css/invite.css b/src/public/css/invite.css new file mode 100644 index 0000000..e69de29 diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 255b284..13cd9ba 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -20,7 +20,6 @@ .top-notification-good { top: 25px; - } } @@ -44,7 +43,6 @@ } } - .max-size-zero { max-width: 0; max-height: 0; @@ -63,8 +61,7 @@ align-items: center; left: 50%; transform: translate(-50%, -500px); - transition: - transform 2s ease-in-out; + transition: transform 2s ease-in-out; } .top-notification-good.revealed { @@ -84,7 +81,6 @@ height: 50px; } - .badges { display: grid; grid-template-columns: repeat(2, 1fr); diff --git a/src/views/invite.ejs b/src/views/invite.ejs index cce211c..2750db7 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -4,7 +4,8 @@ Invite Details - + <%- include("partials/commonStyles") %> + @@ -86,14 +87,12 @@ >

-
- -

¡Bien! Hemos dado de alta tu clave de Nostr. Te vamos a redirigir a la seca, espera un momento.

+
+ +

+ ¡Bien! Hemos dado de alta tu clave de Nostr. Te vamos a redirigir + a la seca, espera un momento. +

¿No tienes cuenta de Nostr?

diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 053a8f7..223a408 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -179,10 +179,11 @@

-
- +
+

¡Oferta creada! Puedes verla en tus ofertas.

diff --git a/tests/createOffer.spec.js b/tests/createOffer.spec.js index 1de4566..2820b3a 100644 --- a/tests/createOffer.spec.js +++ b/tests/createOffer.spec.js @@ -1,5 +1,4 @@ const { test, expect, hardcodedSessionUuid } = require('./test-setup'); -const { chromium } = require('playwright'); const SessionCreated = require('../src/models/SessionCreated'); const SessionRelatedToPublickey = require('../src/models/SessionRelatedToPublickey'); From c6e48af792d39086137c82d25db691b6e44c12bf Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 27 Feb 2025 19:00:36 +0100 Subject: [PATCH 023/176] cool card --- src/public/css/invite.css | 39 +++++++++++++++++++++++++++++++++++++++ src/public/css/seca.css | 29 +++++------------------------ src/views/invite.ejs | 8 ++++++-- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/public/css/invite.css b/src/public/css/invite.css index e69de29..6e3d2f3 100644 --- a/src/public/css/invite.css +++ b/src/public/css/invite.css @@ -0,0 +1,39 @@ +@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/src/public/css/seca.css b/src/public/css/seca.css index 13cd9ba..46ce85a 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -157,6 +157,10 @@ h1 { margin: 1%; } +.shadowed-round-area { + border-radius: 20px; + box-shadow: 0 0 13px #ccc; +} #login-card > * { text-align: center; margin-right: auto; @@ -204,21 +208,6 @@ h1 { 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; @@ -240,6 +229,7 @@ h1 { color: white; padding: 1%; cursor: pointer; + border: 0; } .button-nostr:hover { @@ -251,12 +241,3 @@ h1 { cursor: default; border: 0; } - -#nostr-signup-button { - width: 20vw; - border: 0; -} - -#nostr-signup-button p { - font-size: 1.5rem; -} diff --git a/src/views/invite.ejs b/src/views/invite.ejs index 2750db7..3957b59 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -11,10 +11,14 @@
-
+

¡Has sido invitado a la seca!

- +

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

From dff090a9d4265fe1cc51bd9a673c7bb38fdf1cd1 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 12:36:04 +0100 Subject: [PATCH 024/176] refactored and restyled --- src/public/css/createProfile.css | 62 ++++++++++++++++++++++++++++ src/public/css/seca.css | 71 +++++++++++--------------------- src/views/createProfile.ejs | 31 ++++---------- 3 files changed, 96 insertions(+), 68 deletions(-) create mode 100644 src/public/css/createProfile.css diff --git a/src/public/css/createProfile.css b/src/public/css/createProfile.css new file mode 100644 index 0000000..f37d2cf --- /dev/null +++ b/src/public/css/createProfile.css @@ -0,0 +1,62 @@ + +@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; +} \ No newline at end of file diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 46ce85a..ddc68ba 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -43,6 +43,30 @@ } } +body { + background-color: #bdd5ea; +} + +h1 { + color: #e1c300; +} + +.clickable { + cursor: pointer; +} + +.money-input { + text-align: end; +} + +.input-is-valid { + border: 1px solid green; +} + +.input-is-invalid { + border: 2px solid red; +} + .max-size-zero { max-width: 0; max-height: 0; @@ -81,42 +105,6 @@ height: 50px; } -.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 { - background-color: #bdd5ea; -} - -h1 { - color: #e1c300; -} - -.clickable { - cursor: pointer; -} - -.money-input { - text-align: end; -} - -.input-is-valid { - border: 1px solid green; -} - -.input-is-invalid { - border: 2px solid red; -} .button-group button { border: 0; @@ -160,6 +148,7 @@ h1 { .shadowed-round-area { border-radius: 20px; box-shadow: 0 0 13px #ccc; + padding: 20px; } #login-card > * { text-align: center; @@ -169,17 +158,7 @@ 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; diff --git a/src/views/createProfile.ejs b/src/views/createProfile.ejs index 87b57ef..a9cd8b6 100644 --- a/src/views/createProfile.ejs +++ b/src/views/createProfile.ejs @@ -4,21 +4,22 @@ 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.

-
+
-

Perfil

-
-
-

Datos de contacto

+

Métodos 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 @@ -61,9 +46,10 @@

Tus métodos de contacto se guardan encriptados con tu clave de - nostr. La seca no puede verlos. + nostr. laseca nunca puede verlos. Solo los miembros con los que + tú decidas podrán 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.

@@ -128,6 +114,7 @@
+
From 12bc0d2b849ec1f6d34f8d3145d201b3e5465a97 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 12:42:13 +0100 Subject: [PATCH 025/176] pop up confirmation for create profile --- src/public/css/seca.css | 2 +- src/public/javascript/createProfile.js | 7 ++++++- src/views/createProfile.ejs | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/public/css/seca.css b/src/public/css/seca.css index ddc68ba..e23a1bd 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -85,7 +85,7 @@ h1 { align-items: center; left: 50%; transform: translate(-50%, -500px); - transition: transform 2s ease-in-out; + transition: transform 1s ease-in-out; } .top-notification-good.revealed { diff --git a/src/public/javascript/createProfile.js b/src/public/javascript/createProfile.js index f7af1f1..4be8920 100644 --- a/src/public/javascript/createProfile.js +++ b/src/public/javascript/createProfile.js @@ -1,3 +1,7 @@ +const createProfileConfirmation = document.querySelector( + '#create-profile-success' +); + function debounce(func, wait) { let timeout; return function (...args) { @@ -59,9 +63,10 @@ async function createProfile(allInputs) { body: JSON.stringify({ nym }), }); + createProfileConfirmation.classList.add('revealed'); setTimeout(() => { window.location.href = '/home'; - }, 1000); + }, 5000); } function onLoadErrands(allInputs, submitProfileButton) { diff --git a/src/views/createProfile.ejs b/src/views/createProfile.ejs index a9cd8b6..74381f8 100644 --- a/src/views/createProfile.ejs +++ b/src/views/createProfile.ejs @@ -114,6 +114,13 @@
+ +
+
+ +

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

From b8d694cc48bba0999b7dac658538a08159944a87 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 13:05:44 +0100 Subject: [PATCH 026/176] format stuff --- src/public/css/createProfile.css | 50 +++---- src/public/css/invite.css | 2 +- src/public/css/seca.css | 3 - src/views/createProfile.ejs | 222 ++++++++++++++++--------------- 4 files changed, 141 insertions(+), 136 deletions(-) diff --git a/src/public/css/createProfile.css b/src/public/css/createProfile.css index f37d2cf..e9d02c1 100644 --- a/src/public/css/createProfile.css +++ b/src/public/css/createProfile.css @@ -1,31 +1,33 @@ +@media (max-width: 768px) { + .over-background > *:first-child { + margin-bottom: 5px; + } -@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; + } } -.over-background > *:last-child { - 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; + } } -.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); @@ -59,4 +61,4 @@ h2 { margin-top: 0; margin-bottom: 5px; -} \ No newline at end of file +} diff --git a/src/public/css/invite.css b/src/public/css/invite.css index 6e3d2f3..bdb4779 100644 --- a/src/public/css/invite.css +++ b/src/public/css/invite.css @@ -4,7 +4,7 @@ } #invite-card-content { - width:100%; + width: 100%; } } diff --git a/src/public/css/seca.css b/src/public/css/seca.css index e23a1bd..6d89de4 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -105,7 +105,6 @@ h1 { height: 50px; } - .button-group button { border: 0; padding: 1em; @@ -158,8 +157,6 @@ h1 { margin-bottom: 1vh; } - - .text-warning { color: red; font-weight: 800; diff --git a/src/views/createProfile.ejs b/src/views/createProfile.ejs index 74381f8..3613440 100644 --- a/src/views/createProfile.ejs +++ b/src/views/createProfile.ejs @@ -11,117 +11,123 @@
-
-
-

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.

+
+
+
+
+ + +
+
+

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

+ +
+
+
+
-
- -
-
- -

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

-
+
+ +

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

+
From 1996299d8d8b0125514f56ef48ba9426a5920322 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 13:21:49 +0100 Subject: [PATCH 027/176] pretty navbar --- src/public/css/seca.css | 18 ++++++++++- .../img/laseca-logo-transparent-textonly.svg | 30 +++++++++++++++++++ src/views/partials/appCommonHeader.ejs | 4 +-- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/public/img/laseca-logo-transparent-textonly.svg diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 6d89de4..02ef0e5 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -21,6 +21,12 @@ .top-notification-good { top: 25px; } + + + #app-nav-bar > #laseca-logo { + height: 30px; + } + } @media (min-width: 769px) { @@ -41,8 +47,18 @@ .top-notification-good { top: 50px; } + + #app-nav-bar > #laseca-logo { + height: 30px; + } } +#app-nav-bar > #laseca-logo { + margin-top: 0; + transform: translateY(20%); +} + + body { background-color: #bdd5ea; } @@ -140,7 +156,7 @@ h1 { .over-background { background-color: white; border-radius: 1vw; - padding: 2vw; + padding: 10px; margin: 1%; } diff --git a/src/public/img/laseca-logo-transparent-textonly.svg b/src/public/img/laseca-logo-transparent-textonly.svg new file mode 100644 index 0000000..9884298 --- /dev/null +++ b/src/public/img/laseca-logo-transparent-textonly.svg @@ -0,0 +1,30 @@ + + + +LASECA diff --git a/src/views/partials/appCommonHeader.ejs b/src/views/partials/appCommonHeader.ejs index c41181b..ae7f61b 100644 --- a/src/views/partials/appCommonHeader.ejs +++ b/src/views/partials/appCommonHeader.ejs @@ -1,5 +1,5 @@ -
-

la seca

+
+ From 0dd14eae8e3e60cf3112be960300898f0905b44d Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 13:37:10 +0100 Subject: [PATCH 028/176] split card --- src/public/css/offers.css | 4 ++++ src/public/javascript/offers.js | 10 +++------- src/views/offers.ejs | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index b9b32eb..f257a49 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -67,6 +67,10 @@ } } +#create-offer-root { + display: none; +} + #create-offer-controls { text-align: center; } diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 054d7fa..22e88bc 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -2,8 +2,7 @@ const buttonStartCreateOffer = document.getElementById( 'button-start-create-offer' ); const closeOfferControls = document.getElementById('close-offer-controls-x'); -const createOfferControls = document.getElementById('create-offer-controls'); - +const createOfferRoot = document.getElementById('create-offer-root'); const buyOrSellButtonGroup = document.getElementById( 'button-group-buy-or-sell' ); @@ -46,11 +45,8 @@ const publishOfferButton = document.getElementById('button-submit-offer'); const offerCreatedPopup = document.getElementById('offer-created-confirmation'); function toggleCreateOfferControls() { - createOfferControls.style.display = - createOfferControls.style.display === 'block' ? 'none' : 'block'; - - buttonStartCreateOffer.style.display = - buttonStartCreateOffer.style.display === 'block' ? 'none' : 'block'; + createOfferRoot.style.display = + createOfferRoot.style.display === 'block' ? 'none' : 'block'; } function modifyPremiumValue(delta) { diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 223a408..748992c 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -19,8 +19,9 @@ > Crear nueva oferta - - +
+
Date: Fri, 28 Feb 2025 15:53:40 +0100 Subject: [PATCH 029/176] show offers panel --- src/public/css/offers.css | 4 ++++ src/public/javascript/offers.js | 11 +++++++++++ src/views/offers.ejs | 11 ++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index f257a49..2e0a0fb 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -71,6 +71,10 @@ display: none; } +#view-my-offers-root { + display: none; +} + #create-offer-controls { text-align: center; } diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 22e88bc..74b23a6 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -1,8 +1,10 @@ const buttonStartCreateOffer = document.getElementById( 'button-start-create-offer' ); +const buttonViewMyOffers = document.getElementById('button-view-my-offers'); const closeOfferControls = document.getElementById('close-offer-controls-x'); const createOfferRoot = document.getElementById('create-offer-root'); +const viewMyOffersRoot = document.getElementById('view-my-offers-root'); const buyOrSellButtonGroup = document.getElementById( 'button-group-buy-or-sell' ); @@ -49,6 +51,11 @@ function toggleCreateOfferControls() { createOfferRoot.style.display === 'block' ? 'none' : 'block'; } +function toggleViewMyOffersPanel() { + viewMyOffersRoot.style.display = + viewMyOffersRoot.style.display === 'block' ? 'none' : 'block'; +} + function modifyPremiumValue(delta) { const regexExpression = /-*\d+/; const numValue = parseInt(premiumValue.innerText.match(regexExpression)[0]); @@ -169,6 +176,10 @@ buttonStartCreateOffer.addEventListener('click', () => { toggleCreateOfferControls(); }); +buttonViewMyOffers.addEventListener('click', () => { + toggleViewMyOffersPanel(); +}); + closeOfferControls.addEventListener('click', () => { toggleCreateOfferControls(); }); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 748992c..69ee865 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -12,13 +12,19 @@ <%- include("partials/appCommonHeader") %>
+ +
@@ -179,6 +185,9 @@
+
+
+
Date: Fri, 28 Feb 2025 16:14:39 +0100 Subject: [PATCH 030/176] content --- src/public/css/offers.css | 10 ++++++++++ src/views/offers.ejs | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 2e0a0fb..2d85788 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -75,6 +75,16 @@ display: none; } +#view-my-offers-root { + display: none; + text-align: center; +} + +#view-my-offers-root > * { + margin-left:auto; + margin-right: auto; +} + #create-offer-controls { text-align: center; } diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 69ee865..06e5254 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -187,7 +187,12 @@
- +

Mis ofertas

+

Vaya, no hay nada por aquí...

+
+ Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu oferta la pueden ver tus confiados y los confidados de tus confiados. Se pueden usar billetes grandes. + +
Date: Fri, 28 Feb 2025 16:15:18 +0100 Subject: [PATCH 031/176] format --- src/public/css/offers.css | 4 ++-- src/public/css/seca.css | 3 --- src/views/offers.ejs | 8 +++++--- src/views/partials/appCommonHeader.ejs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 2d85788..5468e9f 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -81,9 +81,9 @@ } #view-my-offers-root > * { - margin-left:auto; + margin-left: auto; margin-right: auto; -} +} #create-offer-controls { text-align: center; diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 02ef0e5..28962f0 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -22,11 +22,9 @@ top: 25px; } - #app-nav-bar > #laseca-logo { height: 30px; } - } @media (min-width: 769px) { @@ -58,7 +56,6 @@ transform: translateY(20%); } - body { background-color: #bdd5ea; } diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 06e5254..b638288 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -24,7 +24,6 @@ > Crear nueva oferta -
@@ -190,8 +189,11 @@

Mis ofertas

Vaya, no hay nada por aquí...

- Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu oferta la pueden ver tus confiados y los confidados de tus confiados. Se pueden usar billetes grandes. - + Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres + comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va + mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu + oferta la pueden ver tus confiados y los confidados de tus confiados. Se + pueden usar billetes grandes.
- + From 9e2af37158257b249d680bdaccbecf440a105e1a Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 28 Feb 2025 23:58:51 +0100 Subject: [PATCH 032/176] endpoint workzzz --- src/routes/apiRoutes.js | 27 ++++++++++++++++++++ src/services/offerService.js | 49 +++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 547cdb3..7ff2616 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -234,4 +234,31 @@ router.post( } ); +router.get( + '/publickey-offers', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + console.log('elo'); + const publicKey = req.cookies.publicKey; + + const offers = await offerService.getOffersByPublicKey(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, + }); + } + } +); + module.exports = router; diff --git a/src/services/offerService.js b/src/services/offerService.js index 56ef780..4defc25 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -27,4 +27,51 @@ async function createOffer(publicKey, offerDetails) { created_at: new Date().toISOString(), }); } -module.exports = { createOffer }; + +async function getOffersByPublicKey(publicKey) { + const offers = await OfferCreated.findAll({ + where: { + public_key: publicKey, + }, + }); + + console.log(offers); + + if (!offers) { + return []; + } + + const offersToReturn = []; + if (offers) { + for (const someOffer of offers) { + const offerDetails = await OfferDetailsSet.findOne({ + where: { + offer_uuid: someOffer.uuid, + }, + order: [['created_at', 'DESC']], + }); + + console.log(offerDetails); + + 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, + }); + } + } + + return offersToReturn; +} +module.exports = { createOffer, getOffersByPublicKey }; From 0e0a094dc87c0ca409f5afe29ebea2f06827460b Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 1 Mar 2025 23:18:18 +0100 Subject: [PATCH 033/176] fe can read from back --- src/app.js | 2 ++ src/public/javascript/offers.js | 44 +++++++++++++++++++++++++++++++++ src/services/offerService.js | 4 +-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index 47430d0..adf0dbe 100644 --- a/src/app.js +++ b/src/app.js @@ -24,6 +24,8 @@ app.use('/api', apiRoutes); app.use(express.static(path.join(__dirname, 'public'))); +app.disable('etag'); //avoids 304 responses + app.listen(port, () => { console.log(`Server started on port ${port}`); }); diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 74b23a6..e3222c6 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -172,6 +172,50 @@ async function publishOffer() { toggleCreateOfferControls(); } +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; + } +} + +class MyOffers { + constructor() { + this.offers = []; + } + + async getOffersFromApi() { + const offersResponse = await fetch('/api/publickey-offers'); + + if (!offersResponse.ok) { + this.offers = []; + } + + const offersData = (await offersResponse.json()).data; + if (offersResponse.ok) { + for (const record of offersData) { + this.offers.push(new Offer(record)); + } + } + } + + async render() {} +} + buttonStartCreateOffer.addEventListener('click', () => { toggleCreateOfferControls(); }); diff --git a/src/services/offerService.js b/src/services/offerService.js index 4defc25..b2d557c 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -51,8 +51,6 @@ async function getOffersByPublicKey(publicKey) { order: [['created_at', 'DESC']], }); - console.log(offerDetails); - offersToReturn.push({ uuid: someOffer.uuid, public_key: someOffer.public_key, @@ -68,6 +66,8 @@ async function getOffersByPublicKey(publicKey) { 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, }); } } From dab201f0699d36baa0e61d392930c00f5a2d9957 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 3 Mar 2025 10:32:57 +0100 Subject: [PATCH 034/176] improving description --- src/public/css/offers.css | 6 +++ src/public/javascript/offers.js | 85 +++++++++++++++++++++++++++++++-- src/views/offers.ejs | 4 +- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 5468e9f..0aa1945 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -85,6 +85,12 @@ margin-right: auto; } +.myoffer-card { + text-align: start; + margin-top: 5px; + margin-bottom: 5px; +} + #create-offer-controls { text-align: center; } diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index e3222c6..4c5c552 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -46,6 +46,8 @@ const publishOfferButton = document.getElementById('button-submit-offer'); const offerCreatedPopup = document.getElementById('offer-created-confirmation'); +const ownOffersContainer = document.getElementById('own-offers-container'); + function toggleCreateOfferControls() { createOfferRoot.style.display = createOfferRoot.style.display === 'block' ? 'none' : 'block'; @@ -194,7 +196,8 @@ class Offer { } class MyOffers { - constructor() { + constructor(ownOffersContainerElement) { + this.ownOffersContainerElement = ownOffersContainerElement; this.offers = []; } @@ -213,14 +216,88 @@ class MyOffers { } } - async render() {} + async render() { + if (!this.offers) { + this.ownOffersContainerElement.innerHTML = + '

Vaya, no hay nada por aquí...

'; + return; + } + + let offersHTML = ''; + + for (const someOffer of this.offers) { + let tradeDescription; + if (someOffer.wants === 'BTC') { + tradeDescription = `Vendes ${someOffer.trade_amount_eur} €. Compras 100 000 sats. `; + } else { + tradeDescription = `Vendes 100 000 sats. Compras ${someOffer.trade_amount_eur} €. `; + } + tradeDescription += `El premium es de ${someOffer.premium}%.`; + + let paymentMethodsDescription = ''; + if (someOffer.is_onchain_accepted) { + paymentMethodsDescription += 'Se acepta Bitcoin onchain. '; + } + if (someOffer.is_lightning_accepted) { + paymentMethodsDescription += 'Se acepta Bitcoin Lightning.'; + } + + let visibilityDescription = 'La oferta es visible para: '; + if (someOffer.show_offer_to_trusted) { + visibilityDescription += 'Tus confiados'; + } + if (someOffer.show_offer_to_trusted_trusted) { + visibilityDescription += ', los confiados de tus confiados'; + } + if (someOffer.show_offer_to_all_members) { + visibilityDescription += ', todos los miembros'; + } + + offersHTML += ` +
+
+ ${tradeDescription} +
+
+ ${paymentMethodsDescription} +
+
+ ${visibilityDescription} +
+
+
+
    +
  • uuid: ${someOffer.uuid}
  • +
  • public_key: ${someOffer.public_key}
  • +
  • wants: ${someOffer.wants}
  • +
  • premium: ${someOffer.premium}
  • +
  • trade_amount_eur: ${someOffer.trade_amount_eur}
  • +
  • location_details: ${someOffer.location_details}
  • +
  • time_availability_details: ${someOffer.time_availability_details}
  • +
  • show_offer_to_trusted: ${someOffer.show_offer_to_trusted}
  • +
  • show_offer_to_trusted_trusted: ${someOffer.show_offer_to_trusted_trusted}
  • +
  • show_offer_to_all_members: ${someOffer.show_offer_to_all_members}
  • +
  • is_onchain_accepted: ${someOffer.is_onchain_accepted}
  • +
  • is_lightning_accepted: ${someOffer.is_lightning_accepted}
  • +
  • are_big_notes_accepted: ${someOffer.are_big_notes_accepted}
  • +
  • created_at: ${someOffer.created_at}
  • +
  • last_updated_at: ${someOffer.last_updated_at}
  • +
+
+ `; + } + + this.ownOffersContainerElement.innerHTML = offersHTML; + } } buttonStartCreateOffer.addEventListener('click', () => { toggleCreateOfferControls(); }); -buttonViewMyOffers.addEventListener('click', () => { +buttonViewMyOffers.addEventListener('click', async () => { + await myOffers.getOffersFromApi(); + await myOffers.render(); toggleViewMyOffersPanel(); }); @@ -263,3 +340,5 @@ publishOfferButton.addEventListener('click', () => { }); updateBtcInput(); + +const myOffers = new MyOffers(ownOffersContainer); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index b638288..4b16445 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -187,8 +187,8 @@

Mis ofertas

-

Vaya, no hay nada por aquí...

-
+
+

Vaya, no hay nada por aquí...

Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu From 0c6ad8bd8552e71c03a6658f31ae08831a47ab5e Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 3 Mar 2025 10:53:18 +0100 Subject: [PATCH 035/176] descriptions --- src/public/css/offers.css | 6 ++++++ src/public/css/seca.css | 6 ++++++ src/public/javascript/offers.js | 18 +++++++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 0aa1945..65fe5e2 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -91,6 +91,12 @@ margin-bottom: 5px; } +.myoffer-card > div { + max-width: 200px; + display: inline-block; + padding: 5px; +} + #create-offer-controls { text-align: center; } diff --git a/src/public/css/seca.css b/src/public/css/seca.css index 28962f0..07f3895 100644 --- a/src/public/css/seca.css +++ b/src/public/css/seca.css @@ -162,6 +162,12 @@ h1 { 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; diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 4c5c552..18ac641 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -253,17 +253,29 @@ class MyOffers { visibilityDescription += ', todos los miembros'; } + const whereAndWhenDescription = `Te va bien quedar: ${someOffer.location_details} | ${someOffer.time_availability_details}.`; + + let otherDescription = ''; + if (someOffer.are_big_notes_accepted) { + otherDescription += 'Se aceptan billetes grandes (100, 200, 500).'; + } offersHTML += `
-
+
${tradeDescription}
-
+
${paymentMethodsDescription}
-
+
${visibilityDescription}
+
+ ${whereAndWhenDescription} +
+
+ ${otherDescription} +
-
-
-
- -
-

Añade los detalles de tu oferta

-
-
+
+
+
+

Añade los detalles de tu oferta

+
+
+ +
+
+
+

Premium

+
+
+
0%
+
+ +
+
+
+

+ Tu precio: 90 000€/BTC +

+

+ (Precio mercado: 83 000€/BTC) +

+
+
+
+
+

¿Cuánto?

+
+
+ +
+ +
+
+
+ +
+ SAT +
+
+
+
+
+

¿Dónde y cuándo?

+
+ + +
+
+
+

¿Cómo se mueve el Bitcoin?

+
+ +
+
+ +
+
+
+

¿Quién puede ver la oferta?

+
+ +
+
+ +
+
+ +
+
+
+

Extras

+
+ +
+
+
-
-
-

Premium

-
-
-
0%
-
- -
-
-
-

- Tu precio: 90 000€/BTC -

-

- (Precio mercado: 83 000€/BTC) -

-
+
+
-
-

¿Cuánto?

-
-
- -
- -
-
-
- -
- SAT -
-
-
-
-
-

¿Dónde y cuándo?

-
- - -
-
-
-

¿Cómo se mueve el Bitcoin?

-
- -
-
- -
-
-
-

¿Quién puede ver la oferta?

-
- -
-
- -
-
- -
-
-
-

Extras

-
- -
-
-
- -
From aea850c642b879bfd78a4484fcb3d5f79876ab98 Mon Sep 17 00:00:00 2001 From: counterweight Date: Tue, 4 Mar 2025 17:55:30 +0100 Subject: [PATCH 046/176] move button --- src/public/css/offers.css | 4 ++-- src/public/javascript/offers.js | 6 ++++-- src/views/offers.ejs | 33 +++++++++++++++------------------ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index 7c8590d..e89f523 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -387,5 +387,5 @@ #close-offer { margin-left: auto; - margin-right:auto; -} \ No newline at end of file + margin-right: auto; +} diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 3f352ac..cfb55f6 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -617,8 +617,10 @@ for (const btcMethodCheckbox of btcMethodCheckboxes) { }); } -publishOfferButton.addEventListener('click', () => { - publishOffer(); +publishOfferButton.addEventListener('click', async () => { + await publishOffer(); + await myOffers.getOffersFromApi(); + await myOffers.render(); }); updateBtcInput(); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index e96425f..5822afb 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -18,12 +18,18 @@ > Ver mis ofertas - +
+
+

Mis ofertas

+ +
+

Vaya, no hay nada por aquí...

+ Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres + comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va + mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu + oferta la pueden ver tus confiados y los confidados de tus confiados. Se + pueden usar billetes grandes. +
@@ -181,22 +187,13 @@
- +
-
-

Mis ofertas

-
-

Vaya, no hay nada por aquí...

- Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres - comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va - mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu - oferta la pueden ver tus confiados y los confidados de tus confiados. Se - pueden usar billetes grandes. -
-
Date: Wed, 5 Mar 2025 00:26:30 +0100 Subject: [PATCH 047/176] checkboxes now work properly --- src/public/javascript/offers.js | 24 ++++++++++++++++++++++++ src/views/offers.ejs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index cfb55f6..f3fd9b6 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -119,6 +119,22 @@ function validateBitcoinMethodCheckboxes(clickedCheckbox) { } } +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() { let wants; if (buyButton.classList.contains('selected')) { @@ -617,6 +633,14 @@ for (const btcMethodCheckbox of btcMethodCheckboxes) { }); } +myTrustedTrustedCheckbox.addEventListener('click', () => { + applyTrustCheckboxConstraints(myTrustedTrustedCheckbox); +}); + +allMembersCheckbox.addEventListener('click', () => { + applyTrustCheckboxConstraints(allMembersCheckbox); +}); + publishOfferButton.addEventListener('click', async () => { await publishOffer(); await myOffers.getOffersFromApi(); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 5822afb..ea86e5a 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -21,7 +21,7 @@

Mis ofertas

- +

Vaya, no hay nada por aquí...

Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres From 6930e0708c3e12b9822ba2035023c5cc3df3ab8b Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 00:28:56 +0100 Subject: [PATCH 048/176] remove old stuff, format --- src/views/offers.ejs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/offers.ejs b/src/views/offers.ejs index ea86e5a..17c03b7 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -21,14 +21,14 @@

Mis ofertas

- +

Vaya, no hay nada por aquí...

- Quieres comprar BTC a un premium de 3% (precio: 93.000€/BTC). Quieres - comprar 100€/102 100SAT. Puedes quedar en: "Cualquier parte", y te va - mejor: "en cualquier momento". Se puede usar Onchain y Lightning. Tu - oferta la pueden ver tus confiados y los confidados de tus confiados. Se - pueden usar billetes grandes.
From 9f807a783dbf483293213ff5553095b98456fb0f Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 00:40:34 +0100 Subject: [PATCH 049/176] new model --- src/models/OfferDeleted.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/models/OfferDeleted.js diff --git a/src/models/OfferDeleted.js b/src/models/OfferDeleted.js new file mode 100644 index 0000000..036a31a --- /dev/null +++ b/src/models/OfferDeleted.js @@ -0,0 +1,27 @@ +const { DataTypes } = require('sequelize'); +const sequelize = require('../database'); + +const OfferDeleted = sequelize.define( + 'OfferCreated', + { + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + offer_uuid: { + type: DataTypes.STRING, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'offer_deleted', + } +); + +module.exports = OfferDeleted; From a5e519f63415c764eecbbe0a20fa37f2c17a3988 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 00:48:33 +0100 Subject: [PATCH 050/176] service entry --- src/services/offerService.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/services/offerService.js b/src/services/offerService.js index b2d557c..0b746d5 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -2,6 +2,9 @@ const uuid = require('uuid'); const OfferCreated = require('../models/OfferCreated'); const OfferDetailsSet = require('../models/OfferDetailsSet'); +const OfferDeleted = require('../models/OfferDeleted'); + +const errors = require('../errors'); async function createOffer(publicKey, offerDetails) { const offerCreated = await OfferCreated.create({ @@ -28,6 +31,25 @@ async function createOffer(publicKey, offerDetails) { }); } +async function deleteOffer(offerUuid) { + const offerExists = Boolean( + await OfferCreated.findOne({ where: { uuid: offerUuid } }) + ); + const offerHasBeenDeleted = Boolean( + await OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) + ); + + if (!offerExists || offerHasBeenDeleted) { + throw new errors.NotFoundError(`Could not find the offer.`); + } + + return OfferDeleted.create({ + uuid: uuid.v7(), + offer_uuid: offerUuid, + created_at: new Date().toISOString(), + }); +} + async function getOffersByPublicKey(publicKey) { const offers = await OfferCreated.findAll({ where: { From 5134982870f414af6b234b1b6eeea61fd6b36118 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 11:10:06 +0100 Subject: [PATCH 051/176] fix bug --- src/models/OfferDeleted.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/OfferDeleted.js b/src/models/OfferDeleted.js index 036a31a..aa1b71a 100644 --- a/src/models/OfferDeleted.js +++ b/src/models/OfferDeleted.js @@ -2,7 +2,7 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../database'); const OfferDeleted = sequelize.define( - 'OfferCreated', + 'OfferDeleted', { uuid: { type: DataTypes.UUID, From ab41f50142610835533ec42010053427ea62a113 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 11:13:01 +0100 Subject: [PATCH 052/176] export service function --- src/services/offerService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/offerService.js b/src/services/offerService.js index 0b746d5..fe6273d 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -96,4 +96,4 @@ async function getOffersByPublicKey(publicKey) { return offersToReturn; } -module.exports = { createOffer, getOffersByPublicKey }; +module.exports = { createOffer, getOffersByPublicKey, deleteOffer }; From 2b48c7f5e23aa7f81239f613a777b149e48118a9 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 11:13:10 +0100 Subject: [PATCH 053/176] endpont --- src/routes/apiRoutes.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 7ff2616..02bcc56 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -234,6 +234,35 @@ router.post( } ); +router.delete( + '/offer/:offerUuid', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + const offerUuid = req.params.offerUuid; + + try { + await offerService.deleteOffer(offerUuid); + } catch (error) { + if (error instanceof 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', + }); + } +); + router.get( '/publickey-offers', rejectIfNotAuthorizedMiddleware, From 26f549c9285433ae1b3aa7fda4a2952af73943e5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 11:29:20 +0100 Subject: [PATCH 054/176] button deletes --- src/public/css/offers.css | 1 + src/public/javascript/offers.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/public/css/offers.css b/src/public/css/offers.css index e89f523..17ac4b5 100644 --- a/src/public/css/offers.css +++ b/src/public/css/offers.css @@ -214,6 +214,7 @@ .offer-action-area { cursor: pointer; margin-right: 20px; + padding: 3px; } .offer-long-text { diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index f3fd9b6..5bcd2cc 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -525,6 +525,7 @@ class Offer { 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'); @@ -533,11 +534,15 @@ class Offer { 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 () => { + deleteOfferByUuid(this.uuid); + }); actionButtonsArea.append(editActionArea, deleteActionArea); @@ -589,6 +594,18 @@ class MyOffers { } } +async function deleteOfferByUuid(offerUuid) { + await fetch(`/api/offer/${offerUuid}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }); + + myOffers.getOffersFromApi(); + myOffers.render(); +} + buttonStartCreateOffer.addEventListener('click', () => { toggleCreateOfferModal(); }); From 76e6bee4116eac4a62800583ba9406aa603047e9 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 14:08:12 +0100 Subject: [PATCH 055/176] put factor creation in function --- src/app.js | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/app.js b/src/app.js index adf0dbe..9691247 100644 --- a/src/app.js +++ b/src/app.js @@ -1,31 +1,38 @@ -const express = require('express'); -const cookieParser = require('cookie-parser'); -const path = require('path'); +function createApp() { + const express = require('express'); + const cookieParser = require('cookie-parser'); + const path = require('path'); -const app = express(); -const port = 3000; + const app = express(); + const port = 3000; -app.use(cookieParser()); + app.set('port', port); -app.use(express.json()); + app.use(cookieParser()); -app.set('view engine', 'ejs'); -app.set('views', path.join(__dirname, 'views')); + app.use(express.json()); -const createSessionMiddleware = require('./middlewares/sessionMiddleware'); + app.set('view engine', 'ejs'); + app.set('views', path.join(__dirname, 'views')); -app.use(createSessionMiddleware); + const createSessionMiddleware = require('./middlewares/sessionMiddleware'); -const webRoutes = require('./routes/webRoutes'); -const apiRoutes = require('./routes/apiRoutes'); + app.use(createSessionMiddleware); -app.use('/', webRoutes); -app.use('/api', 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'))); -app.listen(port, () => { - console.log(`Server started on port ${port}`); + app.disable('etag'); //avoids 304 responses + + return app; +} + +const app = createApp(); +app.listen(app.get('port'), () => { + console.log(`Server started on port ${app.get('port')}`); }); From ba9fa8440742b259a08f15325e4ae29f57b6ea8d Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:04:27 +0100 Subject: [PATCH 056/176] provider-ize profile service --- src/app.js | 18 ++++-- ...directIfMissingProfileDetailsMiddleware.js | 5 +- src/routes/apiRoutes.js | 6 +- src/services/profileService.js | 61 +++++++++---------- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/app.js b/src/app.js index 9691247..49abb0f 100644 --- a/src/app.js +++ b/src/app.js @@ -1,8 +1,15 @@ -function createApp() { - const express = require('express'); - const cookieParser = require('cookie-parser'); - const path = require('path'); +const express = require('express'); +const cookieParser = require('cookie-parser'); +const path = require('path'); +function buildDependencies() { + const dependencies = {}; + + dependencies.sequelize = require('./database'); + return dependencies; +} + +function createApp(dependencies) { const app = express(); const port = 3000; @@ -32,7 +39,8 @@ function createApp() { return app; } -const app = createApp(); +const dependencies = buildDependencies(); +const app = createApp(dependencies); app.listen(app.get('port'), () => { console.log(`Server started on port ${app.get('port')}`); }); diff --git a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js index 934048d..090bc31 100644 --- a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js +++ b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js @@ -1,4 +1,7 @@ -const profileService = require('../services/profileService'); +const profileServiceProvider = require('../services/profileService'); +const ContactDetailsSet = require('../models/ContactDetailsSet'); +const NymSet = require('../models/NymSet'); +const profileService = profileServiceProvider(ContactDetailsSet, NymSet); async function redirectIfMissingProfileDetailsMiddleware(req, res, next) { const publicKey = req.cookies.publicKey; diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 02bcc56..9d5516c 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -4,12 +4,16 @@ 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 profileServiceProvider = require('../services/profileService'); const offerService = require('../services/offerService'); const errors = require('../errors'); const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddleware'); const rejectIfNotAuthorizedMiddleware = require('../middlewares/rejectIfNotAuthorizedMiddleware'); +const ContactDetailsSet = require('../models/ContactDetailsSet'); +const NymSet = require('../models/NymSet'); +const profileService = profileServiceProvider(ContactDetailsSet, NymSet); + const router = express.Router(); router.get('/signup/nostr-challenge', async (req, res) => { diff --git a/src/services/profileService.js b/src/services/profileService.js index 5db0c24..2ea84c8 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -1,38 +1,35 @@ const uuid = require('uuid'); -const ContactDetailsSet = require('../models/ContactDetailsSet'); -const NymSet = require('../models/NymSet'); -async function setContactDetails(publicKey, encryptedContactDetails) { - return await ContactDetailsSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - encrypted_contact_details: encryptedContactDetails, - created_at: new Date().toISOString(), - }); -} - -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: { +const profileServicesProvider = (ContactDetailsSet, NymSet) => { + async function setContactDetails(publicKey, encryptedContactDetails) { + return await ContactDetailsSet.create({ + uuid: uuid.v7(), public_key: publicKey, - }, - }); + encrypted_contact_details: encryptedContactDetails, + created_at: new Date().toISOString(), + }); + } - return isNymSet && areContactDetailsSet; -} + async function setNym(publicKey, nym) { + return await NymSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + nym: nym, + created_at: new Date().toISOString(), + }); + } -module.exports = { - setContactDetails, - setNym, - areProfileDetailsComplete, + 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; + } + + return { setContactDetails, setNym, areProfileDetailsComplete }; }; +module.exports = profileServicesProvider; From 7db9e7f78c5c085a31e5eb44318b834e5c3d7e12 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:21:57 +0100 Subject: [PATCH 057/176] going somewhere, kinda --- src/app.js | 5 +- src/routes/webRoutes.js | 139 ++++++++++++++++++++++------------------ 2 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/app.js b/src/app.js index 49abb0f..9c89db5 100644 --- a/src/app.js +++ b/src/app.js @@ -5,7 +5,8 @@ const path = require('path'); function buildDependencies() { const dependencies = {}; - dependencies.sequelize = require('./database'); + const webRoutesProvider = new (require('./routes/webRoutes'))(); + dependencies.webRoutes = webRoutesProvider.provide(); return dependencies; } @@ -26,7 +27,7 @@ function createApp(dependencies) { app.use(createSessionMiddleware); - const webRoutes = require('./routes/webRoutes'); + const webRoutes = dependencies.webRoutes; const apiRoutes = require('./routes/apiRoutes'); app.use('/', webRoutes); diff --git a/src/routes/webRoutes.js b/src/routes/webRoutes.js index fd6585e..49ad7cd 100644 --- a/src/routes/webRoutes.js +++ b/src/routes/webRoutes.js @@ -7,68 +7,81 @@ const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddlew const redirectIfMissingProfileDetailsMiddleware = require('../middlewares/redirectIfMissingProfileDetailsMiddleware'); const redirectHomeIfAuthorized = require('../middlewares/redirectHomeIfAuthorized'); -router.get( - '/', - redirectHomeIfAuthorized, - redirectIfNotAuthorizedMiddleware, - (req, res) => { - res.redirect('/login'); +class WebRoutesProvider { + provide() { + 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' }); + } + + return res.render('invite', { invite }); + }); + + router.get( + '/createProfile', + redirectIfNotAuthorizedMiddleware, + async (req, res) => { + return res.status(200).render('createProfile'); + } + ); + + router.get( + '/home', + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + (req, res) => { + res.render('home', {}); + } + ); + + router.get( + '/offers', + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + (req, res) => { + res.render('offers', {}); + } + ); + + return router; } -); +} -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' }); - } - - return res.render('invite', { invite }); -}); - -router.get( - '/createProfile', - redirectIfNotAuthorizedMiddleware, - async (req, res) => { - return res.status(200).render('createProfile'); - } -); - -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; +module.exports = WebRoutesProvider; From efa855c60ac95b04ea377eb22fdf6ce9fc4dc570 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:39:31 +0100 Subject: [PATCH 058/176] split --- src/app.js | 9 +-------- src/dependencies.js | 12 ++++++++++++ src/routes/webRoutes.js | 22 ++++++++++++---------- 3 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 src/dependencies.js diff --git a/src/app.js b/src/app.js index 9c89db5..7ec68ee 100644 --- a/src/app.js +++ b/src/app.js @@ -1,14 +1,7 @@ const express = require('express'); const cookieParser = require('cookie-parser'); const path = require('path'); - -function buildDependencies() { - const dependencies = {}; - - const webRoutesProvider = new (require('./routes/webRoutes'))(); - dependencies.webRoutes = webRoutesProvider.provide(); - return dependencies; -} +const { buildDependencies } = require('./dependencies'); function createApp(dependencies) { const app = express(); diff --git a/src/dependencies.js b/src/dependencies.js new file mode 100644 index 0000000..bef3e82 --- /dev/null +++ b/src/dependencies.js @@ -0,0 +1,12 @@ +const express = require('express'); + +function buildDependencies() { + const dependencies = {}; + + const WebRoutesProvider = require('./routes/webRoutes'); + const webRoutesProvider = new WebRoutesProvider(express); + dependencies.webRoutes = webRoutesProvider.provide(); + return dependencies; +} + +module.exports = { buildDependencies }; diff --git a/src/routes/webRoutes.js b/src/routes/webRoutes.js index 49ad7cd..8a9784f 100644 --- a/src/routes/webRoutes.js +++ b/src/routes/webRoutes.js @@ -1,6 +1,3 @@ -const express = require('express'); -const router = express.Router(); - const redirectIfNotAuthorizedMiddleware = require('../middlewares/redirectIfNotAuthorizedMiddleware'); const invitesService = require('../services/invitesService'); const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddleware'); @@ -8,8 +5,13 @@ const redirectIfMissingProfileDetailsMiddleware = require('../middlewares/redire const redirectHomeIfAuthorized = require('../middlewares/redirectHomeIfAuthorized'); class WebRoutesProvider { + constructor(express) { + this.express; + this.router = express.Router(); + } + provide() { - router.get( + this.router.get( '/', redirectHomeIfAuthorized, redirectIfNotAuthorizedMiddleware, @@ -18,11 +20,11 @@ class WebRoutesProvider { } ); - router.get('/login', redirectHomeIfAuthorized, (req, res) => { + this.router.get('/login', redirectHomeIfAuthorized, (req, res) => { res.render('login', { uuid: req.cookies.sessionUuid }); }); - router.get('/invite/:inviteUuid', async (req, res) => { + this.router.get('/invite/:inviteUuid', async (req, res) => { const { inviteUuid } = req.params; res.cookie('inviteUuid', inviteUuid, { @@ -52,7 +54,7 @@ class WebRoutesProvider { return res.render('invite', { invite }); }); - router.get( + this.router.get( '/createProfile', redirectIfNotAuthorizedMiddleware, async (req, res) => { @@ -60,7 +62,7 @@ class WebRoutesProvider { } ); - router.get( + this.router.get( '/home', redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, @@ -70,7 +72,7 @@ class WebRoutesProvider { } ); - router.get( + this.router.get( '/offers', redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, @@ -80,7 +82,7 @@ class WebRoutesProvider { } ); - return router; + return this.router; } } From 76efca914a5ce7cd68fc7fca96106a3242794e31 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:46:51 +0100 Subject: [PATCH 059/176] refactor middlewares --- src/dependencies.js | 14 +++++++++++++- src/routes/webRoutes.js | 36 ++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index bef3e82..1eeaac2 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -3,8 +3,20 @@ const express = require('express'); function buildDependencies() { const dependencies = {}; + const redirectIfNotAuthorizedMiddleware = require('./middlewares/redirectIfNotAuthorizedMiddleware'); + const attachPublicKeyMiddleware = require('./middlewares/attachPublicKeyMiddleware'); + const redirectIfMissingProfileDetailsMiddleware = require('./middlewares/redirectIfMissingProfileDetailsMiddleware'); + const redirectHomeIfAuthorized = require('./middlewares/redirectHomeIfAuthorized'); + + const middlewares = { + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + redirectHomeIfAuthorized, + }; + const WebRoutesProvider = require('./routes/webRoutes'); - const webRoutesProvider = new WebRoutesProvider(express); + const webRoutesProvider = new WebRoutesProvider({ express, middlewares }); dependencies.webRoutes = webRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/webRoutes.js b/src/routes/webRoutes.js index 8a9784f..4849c91 100644 --- a/src/routes/webRoutes.js +++ b/src/routes/webRoutes.js @@ -1,28 +1,28 @@ -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'); class WebRoutesProvider { - constructor(express) { - this.express; + constructor({ express, middlewares }) { this.router = express.Router(); + this.middlewares = middlewares; } provide() { this.router.get( '/', - redirectHomeIfAuthorized, - redirectIfNotAuthorizedMiddleware, + this.middlewares.redirectHomeIfAuthorized, + this.middlewares.redirectIfNotAuthorizedMiddleware, (req, res) => { res.redirect('/login'); } ); - this.router.get('/login', redirectHomeIfAuthorized, (req, res) => { - res.render('login', { uuid: req.cookies.sessionUuid }); - }); + 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; @@ -56,7 +56,7 @@ class WebRoutesProvider { this.router.get( '/createProfile', - redirectIfNotAuthorizedMiddleware, + this.middlewares.redirectIfNotAuthorizedMiddleware, async (req, res) => { return res.status(200).render('createProfile'); } @@ -64,9 +64,9 @@ class WebRoutesProvider { this.router.get( '/home', - redirectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - redirectIfMissingProfileDetailsMiddleware, + this.middlewares.redirectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, + this.middlewares.redirectIfMissingProfileDetailsMiddleware, (req, res) => { res.render('home', {}); } @@ -74,9 +74,9 @@ class WebRoutesProvider { this.router.get( '/offers', - redirectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - redirectIfMissingProfileDetailsMiddleware, + this.middlewares.redirectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, + this.middlewares.redirectIfMissingProfileDetailsMiddleware, (req, res) => { res.render('offers', {}); } From b36f44ae35d63b0e91330fba4dad75bff1831b4d Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:50:51 +0100 Subject: [PATCH 060/176] refactor middlwares --- src/dependencies.js | 12 +----------- src/middlewares/index.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 src/middlewares/index.js diff --git a/src/dependencies.js b/src/dependencies.js index 1eeaac2..a28c54c 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -3,17 +3,7 @@ const express = require('express'); function buildDependencies() { const dependencies = {}; - const redirectIfNotAuthorizedMiddleware = require('./middlewares/redirectIfNotAuthorizedMiddleware'); - const attachPublicKeyMiddleware = require('./middlewares/attachPublicKeyMiddleware'); - const redirectIfMissingProfileDetailsMiddleware = require('./middlewares/redirectIfMissingProfileDetailsMiddleware'); - const redirectHomeIfAuthorized = require('./middlewares/redirectHomeIfAuthorized'); - - const middlewares = { - redirectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - redirectIfMissingProfileDetailsMiddleware, - redirectHomeIfAuthorized, - }; + const middlewares = require('./middlewares'); const WebRoutesProvider = require('./routes/webRoutes'); const webRoutesProvider = new WebRoutesProvider({ express, middlewares }); diff --git a/src/middlewares/index.js b/src/middlewares/index.js new file mode 100644 index 0000000..a1506b3 --- /dev/null +++ b/src/middlewares/index.js @@ -0,0 +1,15 @@ +const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); +const attachPublicKeyMiddleware = require('./attachPublicKeyMiddleware'); +const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); +const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); +const rejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); +const sessionMiddleware = require('./sessionMiddleware'); + +module.exports = { + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + redirectHomeIfAuthorized, + rejectIfNotAuthorizedMiddleware, + sessionMiddleware, +}; From 7cb914428175fb8a8fb9db1bef7bc4d1d0d22b82 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:53:13 +0100 Subject: [PATCH 061/176] more refactor --- src/app.js | 4 +--- src/dependencies.js | 1 + .../{sessionMiddleware.js => createSessionMiddleware.js} | 0 src/middlewares/index.js | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) rename src/middlewares/{sessionMiddleware.js => createSessionMiddleware.js} (100%) diff --git a/src/app.js b/src/app.js index 7ec68ee..0745caa 100644 --- a/src/app.js +++ b/src/app.js @@ -16,9 +16,7 @@ function createApp(dependencies) { app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); - const createSessionMiddleware = require('./middlewares/sessionMiddleware'); - - app.use(createSessionMiddleware); + app.use(dependencies.middlewares.createSessionMiddleware); const webRoutes = dependencies.webRoutes; const apiRoutes = require('./routes/apiRoutes'); diff --git a/src/dependencies.js b/src/dependencies.js index a28c54c..8bf8346 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -4,6 +4,7 @@ function buildDependencies() { const dependencies = {}; const middlewares = require('./middlewares'); + dependencies.middlewares = middlewares; const WebRoutesProvider = require('./routes/webRoutes'); const webRoutesProvider = new WebRoutesProvider({ express, middlewares }); diff --git a/src/middlewares/sessionMiddleware.js b/src/middlewares/createSessionMiddleware.js similarity index 100% rename from src/middlewares/sessionMiddleware.js rename to src/middlewares/createSessionMiddleware.js diff --git a/src/middlewares/index.js b/src/middlewares/index.js index a1506b3..6a56fa8 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -3,7 +3,7 @@ const attachPublicKeyMiddleware = require('./attachPublicKeyMiddleware'); const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); const rejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); -const sessionMiddleware = require('./sessionMiddleware'); +const createSessionMiddleware = require('./createSessionMiddleware'); module.exports = { redirectIfNotAuthorizedMiddleware, @@ -11,5 +11,5 @@ module.exports = { redirectIfMissingProfileDetailsMiddleware, redirectHomeIfAuthorized, rejectIfNotAuthorizedMiddleware, - sessionMiddleware, + createSessionMiddleware, }; From db97f9fd80dd502a313d10d29cf026571be2db2d Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 15:55:13 +0100 Subject: [PATCH 062/176] web routes complete di-ized --- src/dependencies.js | 8 +++++++- src/routes/webRoutes.js | 9 ++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 8bf8346..13a1af7 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -6,8 +6,14 @@ function buildDependencies() { const middlewares = require('./middlewares'); dependencies.middlewares = middlewares; + const invitesService = require('./services/invitesService'); + const WebRoutesProvider = require('./routes/webRoutes'); - const webRoutesProvider = new WebRoutesProvider({ express, middlewares }); + const webRoutesProvider = new WebRoutesProvider({ + express, + middlewares, + invitesService, + }); dependencies.webRoutes = webRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/webRoutes.js b/src/routes/webRoutes.js index 4849c91..b215aa5 100644 --- a/src/routes/webRoutes.js +++ b/src/routes/webRoutes.js @@ -1,9 +1,8 @@ -const invitesService = require('../services/invitesService'); - class WebRoutesProvider { - constructor({ express, middlewares }) { + constructor({ express, middlewares, invitesService }) { this.router = express.Router(); this.middlewares = middlewares; + this.invitesService = invitesService; } provide() { @@ -34,14 +33,14 @@ class WebRoutesProvider { let invite; try { - invite = await invitesService.getAppInvite(inviteUuid); + invite = await this.invitesService.getAppInvite(inviteUuid); if (!invite) { return res .status(404) .render('error', { message: 'Invite not found.' }); } - if (await invitesService.isAppInviteSpent(inviteUuid)) { + if (await this.invitesService.isAppInviteSpent(inviteUuid)) { return res.status(410).render('invite_spent', { invite }); } } catch (error) { From c923493108d7dbb7e2cb3703f0c50814803eb634 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:04:44 +0100 Subject: [PATCH 063/176] start api routes --- src/app.js | 7 +- src/dependencies.js | 4 + src/routes/apiRoutes.js | 552 +++++++++++++++++++++------------------- 3 files changed, 290 insertions(+), 273 deletions(-) diff --git a/src/app.js b/src/app.js index 0745caa..dc3cf83 100644 --- a/src/app.js +++ b/src/app.js @@ -18,11 +18,8 @@ function createApp(dependencies) { app.use(dependencies.middlewares.createSessionMiddleware); - const webRoutes = dependencies.webRoutes; - const apiRoutes = require('./routes/apiRoutes'); - - app.use('/', webRoutes); - app.use('/api', apiRoutes); + app.use('/', dependencies.webRoutes); + app.use('/api', dependencies.apiRoutes); app.use(express.static(path.join(__dirname, 'public'))); diff --git a/src/dependencies.js b/src/dependencies.js index 13a1af7..4e44210 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -15,6 +15,10 @@ function buildDependencies() { invitesService, }); dependencies.webRoutes = webRoutesProvider.provide(); + + const ApiRoutesProvider = require('./routes/apiRoutes'); + const apiRoutesProvider = new ApiRoutesProvider(); + dependencies.apiRoutes = apiRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 9d5516c..f3a0bdc 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -16,282 +16,298 @@ const profileService = profileServiceProvider(ContactDetailsSet, NymSet); const router = express.Router(); -router.get('/signup/nostr-challenge', async (req, res) => { - const inviteUuid = req.cookies.inviteUuid; +class ApiRoutesProvider { + constructor() {} - 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.', - }); - } + provide() { + router.get('/signup/nostr-challenge', async (req, res) => { + const inviteUuid = req.cookies.inviteUuid; - if (error instanceof errors.AlreadyUsedError) { - return res.status(410).json({ - success: false, - message: 'That invite has already been used.', - }); - } + 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.', + }); + } - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } + if (error instanceof errors.AlreadyUsedError) { + return res.status(410).json({ + success: false, + message: 'That invite has already been used.', + }); + } - let relatedNostrChallenge; - try { - relatedNostrChallenge = await 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 }); -}); - -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', - }); - } -); - -router.delete( - '/offer/:offerUuid', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - async (req, res) => { - const offerUuid = req.params.offerUuid; - - try { - await offerService.deleteOffer(offerUuid); - } catch (error) { - if (error instanceof errors.NotFoundError) { - return res.status(404).json({ + return res.status(500).json({ success: false, - message: 'Offer not found for the given public key.', + message: 'Unexpected error.', }); } - return res.status(500).json({ - success: false, - message: 'Unexpected error.', - }); - } - return res.status(204).json({ - success: true, - message: 'Offer deleted successfully', + let relatedNostrChallenge; + try { + relatedNostrChallenge = await 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 }); }); + + 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', + }); + } + ); + + router.delete( + '/offer/:offerUuid', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + const offerUuid = req.params.offerUuid; + + try { + await offerService.deleteOffer(offerUuid); + } catch (error) { + if (error instanceof 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', + }); + } + ); + + router.get( + '/publickey-offers', + rejectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + async (req, res) => { + console.log('elo'); + const publicKey = req.cookies.publicKey; + + const offers = await offerService.getOffersByPublicKey(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 router; } -); +} -router.get( - '/publickey-offers', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - async (req, res) => { - console.log('elo'); - const publicKey = req.cookies.publicKey; - - const offers = await offerService.getOffersByPublicKey(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, - }); - } - } -); - -module.exports = router; +module.exports = ApiRoutesProvider; From 966d95149002881b9eb7a4e806a513cd47fb74c5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:07:57 +0100 Subject: [PATCH 064/176] pass in middlwares --- src/dependencies.js | 2 +- src/routes/apiRoutes.js | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 4e44210..febcfc7 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -17,7 +17,7 @@ function buildDependencies() { dependencies.webRoutes = webRoutesProvider.provide(); const ApiRoutesProvider = require('./routes/apiRoutes'); - const apiRoutesProvider = new ApiRoutesProvider(); + const apiRoutesProvider = new ApiRoutesProvider(middlewares); dependencies.apiRoutes = apiRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index f3a0bdc..7bfebb0 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -7,8 +7,6 @@ const sessionService = require('../services/sessionService'); const profileServiceProvider = require('../services/profileService'); const offerService = require('../services/offerService'); const errors = require('../errors'); -const attachPublicKeyMiddleware = require('../middlewares/attachPublicKeyMiddleware'); -const rejectIfNotAuthorizedMiddleware = require('../middlewares/rejectIfNotAuthorizedMiddleware'); const ContactDetailsSet = require('../models/ContactDetailsSet'); const NymSet = require('../models/NymSet'); @@ -17,7 +15,9 @@ const profileService = profileServiceProvider(ContactDetailsSet, NymSet); const router = express.Router(); class ApiRoutesProvider { - constructor() {} + constructor(middlewares) { + this.middlewares = middlewares; + } provide() { router.get('/signup/nostr-challenge', async (req, res) => { @@ -184,8 +184,8 @@ class ApiRoutesProvider { router.post( '/set-contact-details', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, + this.middlewares.rejectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, async (req, res) => { const encryptedContactDetails = req.body.encryptedContactDetails; const publicKey = req.cookies.publicKey; @@ -211,8 +211,8 @@ class ApiRoutesProvider { router.post( '/set-nym', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, + this.middlewares.rejectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, async (req, res) => { const nym = req.body.nym; const publicKey = req.cookies.publicKey; @@ -235,8 +235,8 @@ class ApiRoutesProvider { router.post( '/offer', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, + this.middlewares.rejectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, async (req, res) => { const publicKey = req.cookies.publicKey; const offerDetails = req.body.offerDetails; @@ -252,8 +252,8 @@ class ApiRoutesProvider { router.delete( '/offer/:offerUuid', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, + this.middlewares.rejectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, async (req, res) => { const offerUuid = req.params.offerUuid; @@ -281,8 +281,8 @@ class ApiRoutesProvider { router.get( '/publickey-offers', - rejectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, + this.middlewares.rejectIfNotAuthorizedMiddleware, + this.middlewares.attachPublicKeyMiddleware, async (req, res) => { console.log('elo'); const publicKey = req.cookies.publicKey; From 54cb13d1e553c758d6515c919243f15498363776 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:13:39 +0100 Subject: [PATCH 065/176] revert the shit i did when i started the other way around --- ...directIfMissingProfileDetailsMiddleware.js | 5 +- src/routes/apiRoutes.js | 6 +- src/services/profileService.js | 59 +++++++++---------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js index 090bc31..934048d 100644 --- a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js +++ b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js @@ -1,7 +1,4 @@ -const profileServiceProvider = require('../services/profileService'); -const ContactDetailsSet = require('../models/ContactDetailsSet'); -const NymSet = require('../models/NymSet'); -const profileService = profileServiceProvider(ContactDetailsSet, NymSet); +const profileService = require('../services/profileService'); async function redirectIfMissingProfileDetailsMiddleware(req, res, next) { const publicKey = req.cookies.publicKey; diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 7bfebb0..fc2ad86 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -4,14 +4,10 @@ const invitesService = require('../services/invitesService'); const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); const sessionService = require('../services/sessionService'); -const profileServiceProvider = require('../services/profileService'); +const profileService = require('../services/profileService'); const offerService = require('../services/offerService'); const errors = require('../errors'); -const ContactDetailsSet = require('../models/ContactDetailsSet'); -const NymSet = require('../models/NymSet'); -const profileService = profileServiceProvider(ContactDetailsSet, NymSet); - const router = express.Router(); class ApiRoutesProvider { diff --git a/src/services/profileService.js b/src/services/profileService.js index 2ea84c8..358499c 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -1,35 +1,34 @@ const uuid = require('uuid'); +const ContactDetailsSet = require('../models/ContactDetailsSet'); +const NymSet = require('../models/NymSet'); -const profileServicesProvider = (ContactDetailsSet, NymSet) => { - async function setContactDetails(publicKey, encryptedContactDetails) { - return await ContactDetailsSet.create({ - uuid: uuid.v7(), +async function setContactDetails(publicKey, encryptedContactDetails) { + return await ContactDetailsSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + encrypted_contact_details: encryptedContactDetails, + created_at: new Date().toISOString(), + }); +} + +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, - encrypted_contact_details: encryptedContactDetails, - created_at: new Date().toISOString(), - }); - } + }, + }); - async function setNym(publicKey, nym) { - return await NymSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - nym: nym, - created_at: new Date().toISOString(), - }); - } + return isNymSet && areContactDetailsSet; +} - 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; - } - - return { setContactDetails, setNym, areProfileDetailsComplete }; -}; -module.exports = profileServicesProvider; +module.exports = { setContactDetails, setNym, areProfileDetailsComplete }; From 1cf303c31d49764187dd4e57ef6aa868f6a9814d Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:18:09 +0100 Subject: [PATCH 066/176] require all services in one go --- src/routes/apiRoutes.js | 30 +++++++++++++++++------------- src/services/index.js | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 src/services/index.js diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index fc2ad86..a02ae23 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -6,6 +6,8 @@ const loginService = require('../services/loginService'); const sessionService = require('../services/sessionService'); const profileService = require('../services/profileService'); const offerService = require('../services/offerService'); + +const services = require('../services'); const errors = require('../errors'); const router = express.Router(); @@ -13,6 +15,7 @@ const router = express.Router(); class ApiRoutesProvider { constructor(middlewares) { this.middlewares = middlewares; + this.services = services; } provide() { @@ -22,7 +25,7 @@ class ApiRoutesProvider { let signUpChallenge; try { signUpChallenge = - await invitesService.createSignUpChallenge(inviteUuid); + await services.invitesService.createSignUpChallenge(inviteUuid); } catch (error) { if (error instanceof errors.NotFoundError) { return res.status(404).json({ @@ -46,7 +49,7 @@ class ApiRoutesProvider { let relatedNostrChallenge; try { - relatedNostrChallenge = await nostrService.getNostrChallenge( + relatedNostrChallenge = await services.nostrService.getNostrChallenge( signUpChallenge.nostr_challenge_uuid ); } catch (error) { @@ -68,7 +71,7 @@ class ApiRoutesProvider { let completedSignUpChallenge; try { completedSignUpChallenge = - await invitesService.verifySignUpChallenge(signedEvent); + await services.invitesService.verifySignUpChallenge(signedEvent); } catch (error) { if (error instanceof errors.ExpiredError) { return res.status(410).json({ @@ -90,7 +93,7 @@ class ApiRoutesProvider { } } - await sessionService.relateSessionToPublicKey( + await services.sessionService.relateSessionToPublicKey( sessionUuid, completedSignUpChallenge.public_key ); @@ -101,7 +104,7 @@ class ApiRoutesProvider { router.get('/login/nostr-challenge', async (req, res) => { let loginChallenge; try { - loginChallenge = await loginService.createLoginChallenge(); + loginChallenge = await services.loginService.createLoginChallenge(); } catch (error) { return res.status(500).json({ success: false, @@ -111,7 +114,7 @@ class ApiRoutesProvider { let relatedNostrChallenge; try { - relatedNostrChallenge = await nostrService.getNostrChallenge( + relatedNostrChallenge = await services.nostrService.getNostrChallenge( loginChallenge.nostr_challenge_uuid ); } catch (error) { @@ -133,7 +136,7 @@ class ApiRoutesProvider { let completedLoginChallenge; try { completedLoginChallenge = - await loginService.verifyLoginChallenge(signedEvent); + await services.loginService.verifyLoginChallenge(signedEvent); } catch (error) { console.log('helo5'); console.log(error); @@ -170,7 +173,7 @@ class ApiRoutesProvider { } console.log('helo?2'); console.log(completedLoginChallenge); - await sessionService.relateSessionToPublicKey( + await services.sessionService.relateSessionToPublicKey( sessionUuid, completedLoginChallenge.public_key ); @@ -193,7 +196,7 @@ class ApiRoutesProvider { }); } - await profileService.setContactDetails( + await services.profileService.setContactDetails( publicKey, encryptedContactDetails ); @@ -220,7 +223,7 @@ class ApiRoutesProvider { }); } - await profileService.setNym(publicKey, nym); + await services.profileService.setNym(publicKey, nym); return res.status(200).json({ success: true, @@ -237,7 +240,7 @@ class ApiRoutesProvider { const publicKey = req.cookies.publicKey; const offerDetails = req.body.offerDetails; - await offerService.createOffer(publicKey, offerDetails); + await services.offerService.createOffer(publicKey, offerDetails); return res.status(200).json({ success: true, @@ -254,7 +257,7 @@ class ApiRoutesProvider { const offerUuid = req.params.offerUuid; try { - await offerService.deleteOffer(offerUuid); + await services.offerService.deleteOffer(offerUuid); } catch (error) { if (error instanceof errors.NotFoundError) { return res.status(404).json({ @@ -283,7 +286,8 @@ class ApiRoutesProvider { console.log('elo'); const publicKey = req.cookies.publicKey; - const offers = await offerService.getOffersByPublicKey(publicKey); + const offers = + await services.offerService.getOffersByPublicKey(publicKey); if (!offers) { return res.status(404).json({ diff --git a/src/services/index.js b/src/services/index.js new file mode 100644 index 0000000..3e35e60 --- /dev/null +++ b/src/services/index.js @@ -0,0 +1,15 @@ +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'); + +module.exports = { + invitesService, + nostrService, + loginService, + sessionService, + profileService, + offerService, +}; From 47b2821f9068ae4f1f05b5175fd6c4030bd00f52 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:21:37 +0100 Subject: [PATCH 067/176] passing services --- src/dependencies.js | 6 +++--- src/routes/apiRoutes.js | 47 ++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index febcfc7..5ba1f36 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -6,18 +6,18 @@ function buildDependencies() { const middlewares = require('./middlewares'); dependencies.middlewares = middlewares; - const invitesService = require('./services/invitesService'); + const services = require('./services'); const WebRoutesProvider = require('./routes/webRoutes'); const webRoutesProvider = new WebRoutesProvider({ express, middlewares, - invitesService, + invitesService: services.invitesService, }); dependencies.webRoutes = webRoutesProvider.provide(); const ApiRoutesProvider = require('./routes/apiRoutes'); - const apiRoutesProvider = new ApiRoutesProvider(middlewares); + const apiRoutesProvider = new ApiRoutesProvider({ middlewares, services }); dependencies.apiRoutes = apiRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index a02ae23..7a86bdb 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -1,19 +1,11 @@ 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 services = require('../services'); const errors = require('../errors'); const router = express.Router(); class ApiRoutesProvider { - constructor(middlewares) { + constructor({ middlewares, services }) { this.middlewares = middlewares; this.services = services; } @@ -25,7 +17,7 @@ class ApiRoutesProvider { let signUpChallenge; try { signUpChallenge = - await services.invitesService.createSignUpChallenge(inviteUuid); + await this.services.invitesService.createSignUpChallenge(inviteUuid); } catch (error) { if (error instanceof errors.NotFoundError) { return res.status(404).json({ @@ -49,9 +41,10 @@ class ApiRoutesProvider { let relatedNostrChallenge; try { - relatedNostrChallenge = await services.nostrService.getNostrChallenge( - signUpChallenge.nostr_challenge_uuid - ); + relatedNostrChallenge = + await this.services.nostrService.getNostrChallenge( + signUpChallenge.nostr_challenge_uuid + ); } catch (error) { return res.status(500).json({ success: false, @@ -71,7 +64,7 @@ class ApiRoutesProvider { let completedSignUpChallenge; try { completedSignUpChallenge = - await services.invitesService.verifySignUpChallenge(signedEvent); + await this.services.invitesService.verifySignUpChallenge(signedEvent); } catch (error) { if (error instanceof errors.ExpiredError) { return res.status(410).json({ @@ -93,7 +86,7 @@ class ApiRoutesProvider { } } - await services.sessionService.relateSessionToPublicKey( + await this.services.sessionService.relateSessionToPublicKey( sessionUuid, completedSignUpChallenge.public_key ); @@ -104,7 +97,8 @@ class ApiRoutesProvider { router.get('/login/nostr-challenge', async (req, res) => { let loginChallenge; try { - loginChallenge = await services.loginService.createLoginChallenge(); + loginChallenge = + await this.services.loginService.createLoginChallenge(); } catch (error) { return res.status(500).json({ success: false, @@ -114,9 +108,10 @@ class ApiRoutesProvider { let relatedNostrChallenge; try { - relatedNostrChallenge = await services.nostrService.getNostrChallenge( - loginChallenge.nostr_challenge_uuid - ); + relatedNostrChallenge = + await this.services.nostrService.getNostrChallenge( + loginChallenge.nostr_challenge_uuid + ); } catch (error) { return res.status(500).json({ success: false, @@ -136,7 +131,7 @@ class ApiRoutesProvider { let completedLoginChallenge; try { completedLoginChallenge = - await services.loginService.verifyLoginChallenge(signedEvent); + await this.services.loginService.verifyLoginChallenge(signedEvent); } catch (error) { console.log('helo5'); console.log(error); @@ -173,7 +168,7 @@ class ApiRoutesProvider { } console.log('helo?2'); console.log(completedLoginChallenge); - await services.sessionService.relateSessionToPublicKey( + await this.services.sessionService.relateSessionToPublicKey( sessionUuid, completedLoginChallenge.public_key ); @@ -196,7 +191,7 @@ class ApiRoutesProvider { }); } - await services.profileService.setContactDetails( + await this.services.profileService.setContactDetails( publicKey, encryptedContactDetails ); @@ -223,7 +218,7 @@ class ApiRoutesProvider { }); } - await services.profileService.setNym(publicKey, nym); + await this.services.profileService.setNym(publicKey, nym); return res.status(200).json({ success: true, @@ -240,7 +235,7 @@ class ApiRoutesProvider { const publicKey = req.cookies.publicKey; const offerDetails = req.body.offerDetails; - await services.offerService.createOffer(publicKey, offerDetails); + await this.services.offerService.createOffer(publicKey, offerDetails); return res.status(200).json({ success: true, @@ -257,7 +252,7 @@ class ApiRoutesProvider { const offerUuid = req.params.offerUuid; try { - await services.offerService.deleteOffer(offerUuid); + await this.services.offerService.deleteOffer(offerUuid); } catch (error) { if (error instanceof errors.NotFoundError) { return res.status(404).json({ @@ -287,7 +282,7 @@ class ApiRoutesProvider { const publicKey = req.cookies.publicKey; const offers = - await services.offerService.getOffersByPublicKey(publicKey); + await this.services.offerService.getOffersByPublicKey(publicKey); if (!offers) { return res.status(404).json({ From 2f93e658625915ce7b6333438588884bd73c6c4c Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:23:36 +0100 Subject: [PATCH 068/176] pass errors --- src/dependencies.js | 7 ++++++- src/routes/apiRoutes.js | 25 ++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 5ba1f36..d4f71b8 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -7,6 +7,7 @@ function buildDependencies() { dependencies.middlewares = middlewares; const services = require('./services'); + const errors = require('./errors'); const WebRoutesProvider = require('./routes/webRoutes'); const webRoutesProvider = new WebRoutesProvider({ @@ -17,7 +18,11 @@ function buildDependencies() { dependencies.webRoutes = webRoutesProvider.provide(); const ApiRoutesProvider = require('./routes/apiRoutes'); - const apiRoutesProvider = new ApiRoutesProvider({ middlewares, services }); + const apiRoutesProvider = new ApiRoutesProvider({ + middlewares, + services, + errors, + }); dependencies.apiRoutes = apiRoutesProvider.provide(); return dependencies; } diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 7a86bdb..d873438 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -1,13 +1,12 @@ const express = require('express'); -const errors = require('../errors'); - const router = express.Router(); class ApiRoutesProvider { - constructor({ middlewares, services }) { + constructor({ middlewares, services, errors }) { this.middlewares = middlewares; this.services = services; + this.errors = errors; } provide() { @@ -19,14 +18,14 @@ class ApiRoutesProvider { signUpChallenge = await this.services.invitesService.createSignUpChallenge(inviteUuid); } catch (error) { - if (error instanceof errors.NotFoundError) { + if (error instanceof this.services.NotFoundError) { return res.status(404).json({ success: false, message: 'Could not find invite with that id.', }); } - if (error instanceof errors.AlreadyUsedError) { + if (error instanceof this.errors.AlreadyUsedError) { return res.status(410).json({ success: false, message: 'That invite has already been used.', @@ -66,19 +65,19 @@ class ApiRoutesProvider { completedSignUpChallenge = await this.services.invitesService.verifySignUpChallenge(signedEvent); } catch (error) { - if (error instanceof errors.ExpiredError) { + 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 errors.AlreadyUsedError) { + 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 errors.InvalidSignatureError) { + if (error instanceof this.errors.InvalidSignatureError) { return res.status(400).json({ success: false, message: 'The challenge signature is not valid.', @@ -135,25 +134,25 @@ class ApiRoutesProvider { } catch (error) { console.log('helo5'); console.log(error); - if (error instanceof errors.ExpiredError) { + 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 errors.AlreadyUsedError) { + 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 errors.InvalidSignatureError) { + if (error instanceof this.errors.InvalidSignatureError) { return res.status(400).json({ success: false, message: 'The challenge signature is not valid.', }); } - if (error instanceof errors.ForbiddenError) { + if (error instanceof this.errors.ForbiddenError) { console.log('helo?1'); return res.status(403).json({ success: false, @@ -254,7 +253,7 @@ class ApiRoutesProvider { try { await this.services.offerService.deleteOffer(offerUuid); } catch (error) { - if (error instanceof errors.NotFoundError) { + if (error instanceof this.errors.NotFoundError) { return res.status(404).json({ success: false, message: 'Offer not found for the given public key.', From 1a82b287454fcc2a08623c7f1e01fbed24f7e126 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:29:51 +0100 Subject: [PATCH 069/176] pass in express --- src/dependencies.js | 1 + src/routes/apiRoutes.js | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index d4f71b8..b182680 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -19,6 +19,7 @@ function buildDependencies() { const ApiRoutesProvider = require('./routes/apiRoutes'); const apiRoutesProvider = new ApiRoutesProvider({ + express, middlewares, services, errors, diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index d873438..6979b53 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -1,16 +1,13 @@ -const express = require('express'); - -const router = express.Router(); - class ApiRoutesProvider { - constructor({ middlewares, services, errors }) { + constructor({ express, middlewares, services, errors }) { + this.router = express.Router(); this.middlewares = middlewares; this.services = services; this.errors = errors; } provide() { - router.get('/signup/nostr-challenge', async (req, res) => { + this.router.get('/signup/nostr-challenge', async (req, res) => { const inviteUuid = req.cookies.inviteUuid; let signUpChallenge; @@ -56,7 +53,7 @@ class ApiRoutesProvider { .json({ challenge: relatedNostrChallenge.challenge }); }); - router.post('/signup/nostr-verify', async (req, res) => { + this.router.post('/signup/nostr-verify', async (req, res) => { const signedEvent = req.body; const sessionUuid = req.cookies.sessionUuid; @@ -93,7 +90,7 @@ class ApiRoutesProvider { return res.status(200).json({ success: true }); }); - router.get('/login/nostr-challenge', async (req, res) => { + this.router.get('/login/nostr-challenge', async (req, res) => { let loginChallenge; try { loginChallenge = @@ -123,7 +120,7 @@ class ApiRoutesProvider { .json({ challenge: relatedNostrChallenge.challenge }); }); - router.post('/login/nostr-verify', async (req, res) => { + this.router.post('/login/nostr-verify', async (req, res) => { const signedEvent = req.body; const sessionUuid = req.cookies.sessionUuid; @@ -175,7 +172,7 @@ class ApiRoutesProvider { return res.status(200).json({ success: true }); }); - router.post( + this.router.post( '/set-contact-details', this.middlewares.rejectIfNotAuthorizedMiddleware, this.middlewares.attachPublicKeyMiddleware, @@ -202,7 +199,7 @@ class ApiRoutesProvider { } ); - router.post( + this.router.post( '/set-nym', this.middlewares.rejectIfNotAuthorizedMiddleware, this.middlewares.attachPublicKeyMiddleware, @@ -226,7 +223,7 @@ class ApiRoutesProvider { } ); - router.post( + this.router.post( '/offer', this.middlewares.rejectIfNotAuthorizedMiddleware, this.middlewares.attachPublicKeyMiddleware, @@ -243,7 +240,7 @@ class ApiRoutesProvider { } ); - router.delete( + this.router.delete( '/offer/:offerUuid', this.middlewares.rejectIfNotAuthorizedMiddleware, this.middlewares.attachPublicKeyMiddleware, @@ -272,7 +269,7 @@ class ApiRoutesProvider { } ); - router.get( + this.router.get( '/publickey-offers', this.middlewares.rejectIfNotAuthorizedMiddleware, this.middlewares.attachPublicKeyMiddleware, @@ -300,8 +297,7 @@ class ApiRoutesProvider { } ); - return router; + return this.router; } } - module.exports = ApiRoutesProvider; From 1702f2998789bbb1785d3e04b11dbaa5b16f03d8 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:40:54 +0100 Subject: [PATCH 070/176] typo --- src/routes/apiRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 6979b53..9ff1513 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -15,7 +15,7 @@ class ApiRoutesProvider { signUpChallenge = await this.services.invitesService.createSignUpChallenge(inviteUuid); } catch (error) { - if (error instanceof this.services.NotFoundError) { + if (error instanceof this.errors.NotFoundError) { return res.status(404).json({ success: false, message: 'Could not find invite with that id.', From 011a255d9edc00025fa914c1bfaf7272e381e3d5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:44:02 +0100 Subject: [PATCH 071/176] root module def for models --- src/models/index.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/models/index.js diff --git a/src/models/index.js b/src/models/index.js new file mode 100644 index 0000000..ed7ed30 --- /dev/null +++ b/src/models/index.js @@ -0,0 +1,31 @@ +const AppInviteCreated = require('./AppInviteCreated'); +const ContactDetailsSet = require('./ContactDetailsSet'); +const LoginChallengeCompleted = require('./LoginChallengeCompleted'); +const LoginChallengeCreated = require('./LoginChallengeCreated'); +const NostrChallengeCompleted = require('./NostrChallengeCompleted'); +const NostrChallengeCreated = require('./NostrChallengeCreated'); +const NymSet = require('./NymSet'); +const OfferCreated = require('./OfferCreated'); +const OfferDeleted = require('./OfferDeleted'); +const OfferDetailsSet = require('./OfferDetailsSet'); +const SessionCreated = require('./SessionCreated'); +const SessionRelatedToPublickey = require('./SessionRelatedToPublickey'); +const SignUpChallengeCompleted = require('./SignUpChallengeCompleted'); +const SignUpChallengeCreated = require('./SignUpChallengeCreated'); + +module.exports = { + SignUpChallengeCreated, + SignUpChallengeCompleted, + SessionRelatedToPublickey, + SessionCreated, + OfferDeleted, + OfferDetailsSet, + OfferCreated, + NymSet, + NostrChallengeCreated, + NostrChallengeCompleted, + LoginChallengeCompleted, + LoginChallengeCreated, + ContactDetailsSet, + AppInviteCreated, +}; From d34e62070a8449f215e718371e254e26ae7b19eb Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 5 Mar 2025 16:49:03 +0100 Subject: [PATCH 072/176] refactor model imports in all services --- src/services/invitesService.js | 53 +++++++++++++++++----------------- src/services/loginService.js | 7 ++--- src/services/nostrService.js | 15 +++++----- src/services/offerService.js | 18 +++++------- src/services/profileService.js | 14 +++++---- src/services/sessionService.js | 31 ++++++++++---------- 6 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/services/invitesService.js b/src/services/invitesService.js index 432633c..16d7805 100644 --- a/src/services/invitesService.js +++ b/src/services/invitesService.js @@ -1,14 +1,11 @@ const uuid = require('uuid'); const nostrService = require('./nostrService'); -const AppInviteCreated = require('../models/AppInviteCreated'); -const SignUpChallengeCreated = require('../models/SignUpChallengeCreated'); -const SignUpChallengeCompleted = require('../models/SignUpChallengeCompleted'); - +const models = require('../models'); const errors = require('../errors'); async function appInviteExists(inviteUuid) { - const invite = await AppInviteCreated.findOne({ + const invite = await models.AppInviteCreated.findOne({ where: { uuid: inviteUuid }, }); if (invite) { @@ -18,18 +15,19 @@ async function appInviteExists(inviteUuid) { } async function getAppInvite(inviteUuid) { - const invite = await AppInviteCreated.findOne({ + const invite = await models.AppInviteCreated.findOne({ where: { uuid: inviteUuid }, }); return invite; } async function isAppInviteSpent(appInviteUuid) { - const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({ - where: { - app_invite_uuid: appInviteUuid, - }, - }); + const signUpChallengeCompleted = + await models.SignUpChallengeCompleted.findOne({ + where: { + app_invite_uuid: appInviteUuid, + }, + }); if (signUpChallengeCompleted) { return true; @@ -38,7 +36,7 @@ async function isAppInviteSpent(appInviteUuid) { } async function createAppInvite(inviterPubKey) { - return await AppInviteCreated.create({ + return await models.AppInviteCreated.create({ uuid: uuid.v7(), inviter_pub_key: inviterPubKey, created_at: new Date().toISOString(), @@ -56,7 +54,7 @@ async function createSignUpChallenge(appInviteUuid) { const nostrChallenge = await nostrService.createNostrChallenge(); - return await SignUpChallengeCreated.create({ + return await models.SignUpChallengeCreated.create({ uuid: uuid.v7(), nostr_challenge_uuid: nostrChallenge.uuid, app_invite_uuid: appInviteUuid, @@ -70,7 +68,7 @@ async function verifySignUpChallenge(signedEvent) { const nostrChallenge = await nostrService.getNostrChallenge(null, challenge); - const signUpChallenge = await SignUpChallengeCreated.findOne({ + const signUpChallenge = await models.SignUpChallengeCreated.findOne({ where: { nostr_challenge_uuid: nostrChallenge.uuid, }, @@ -83,23 +81,26 @@ async function verifySignUpChallenge(signedEvent) { 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(), - }); + const completedSignUpChallenge = await 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; } async function isPublicKeySignedUp(publicKey) { - const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({ - where: { - public_key: publicKey, - }, - }); + const signUpChallengeCompleted = + await models.SignUpChallengeCompleted.findOne({ + where: { + public_key: publicKey, + }, + }); if (signUpChallengeCompleted) { return true; diff --git a/src/services/loginService.js b/src/services/loginService.js index 9d5f973..3de0090 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -2,15 +2,14 @@ const uuid = require('uuid'); const nostrService = require('./nostrService'); const invitesService = require('./invitesService'); -const LoginChallengeCreated = require('../models/LoginChallengeCreated'); -const LoginChallengeCompleted = require('../models/LoginChallengeCompleted'); +const models = require('../models'); const errors = require('../errors'); async function createLoginChallenge() { const nostrChallenge = await nostrService.createNostrChallenge(); - return await LoginChallengeCreated.create({ + return await models.LoginChallengeCreated.create({ uuid: uuid.v7(), nostr_challenge_uuid: nostrChallenge.uuid, created_at: new Date().toISOString(), @@ -39,7 +38,7 @@ async function verifyLoginChallenge(signedEvent) { ); } - const completedLoginChallenge = await LoginChallengeCompleted.create({ + const completedLoginChallenge = await models.LoginChallengeCompleted.create({ uuid: uuid.v7(), nostr_challenge_completed_uuid: completedNostrChallenge.uuid, public_key: completedNostrChallenge.public_key, diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 542ba1c..412d0ba 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -3,8 +3,7 @@ const crypto = require('crypto'); const { Op, TimeoutError } = require('sequelize'); const { verifyEvent } = require('nostr-tools'); -const NostrChallengeCreated = require('../models/NostrChallengeCreated'); -const NostrChallengeCompleted = require('../models/NostrChallengeCompleted'); +const models = require('../models'); const constants = require('../constants'); const errors = require('../errors'); @@ -17,7 +16,7 @@ async function createNostrChallenge() { constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS ); - const nostrChallenge = await NostrChallengeCreated.create({ + const nostrChallenge = await models.NostrChallengeCreated.create({ uuid: uuid.v7(), challenge: crypto.randomBytes(32).toString('hex'), expires_at: expiryTimestamp.toISOString(), @@ -29,7 +28,7 @@ async function createNostrChallenge() { async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) { if (nostrChallengeUuid) { - return await NostrChallengeCreated.findOne({ + return await models.NostrChallengeCreated.findOne({ where: { uuid: nostrChallengeUuid, }, @@ -37,7 +36,7 @@ async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) { } if (challenge) { - return await NostrChallengeCreated.findOne({ + return await models.NostrChallengeCreated.findOne({ where: { challenge, }, @@ -66,7 +65,7 @@ async function verifyNostrChallenge(signedEvent) { throw new errors.InvalidSignatureError('Signature is not valid.'); } - return await NostrChallengeCompleted.create({ + return await models.NostrChallengeCompleted.create({ uuid: uuid.v7(), challenge: challenge, signed_event: signedEvent, @@ -76,7 +75,7 @@ async function verifyNostrChallenge(signedEvent) { } async function isNostrChallengeFresh(challengeString) { - const nostrChallenge = await NostrChallengeCreated.findOne({ + const nostrChallenge = await models.NostrChallengeCreated.findOne({ where: { challenge: challengeString, expires_at: { @@ -92,7 +91,7 @@ async function isNostrChallengeFresh(challengeString) { } async function hasNostrChallengeBeenCompleted(challengeString) { - const completedNostrChallenge = await NostrChallengeCompleted.findOne({ + const completedNostrChallenge = await models.NostrChallengeCompleted.findOne({ where: { challenge: challengeString, }, diff --git a/src/services/offerService.js b/src/services/offerService.js index fe6273d..ab1f19e 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -1,19 +1,17 @@ const uuid = require('uuid'); -const OfferCreated = require('../models/OfferCreated'); -const OfferDetailsSet = require('../models/OfferDetailsSet'); -const OfferDeleted = require('../models/OfferDeleted'); +const models = require('../models'); const errors = require('../errors'); async function createOffer(publicKey, offerDetails) { - const offerCreated = await OfferCreated.create({ + const offerCreated = await models.OfferCreated.create({ uuid: uuid.v7(), public_key: publicKey, created_at: new Date().toISOString(), }); - await OfferDetailsSet.create({ + await models.OfferDetailsSet.create({ uuid: uuid.v7(), offer_uuid: offerCreated.uuid, wants: offerDetails.wants, @@ -33,17 +31,17 @@ async function createOffer(publicKey, offerDetails) { async function deleteOffer(offerUuid) { const offerExists = Boolean( - await OfferCreated.findOne({ where: { uuid: offerUuid } }) + await models.OfferCreated.findOne({ where: { uuid: offerUuid } }) ); const offerHasBeenDeleted = Boolean( - await OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) + await models.OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) ); if (!offerExists || offerHasBeenDeleted) { throw new errors.NotFoundError(`Could not find the offer.`); } - return OfferDeleted.create({ + return models.OfferDeleted.create({ uuid: uuid.v7(), offer_uuid: offerUuid, created_at: new Date().toISOString(), @@ -51,7 +49,7 @@ async function deleteOffer(offerUuid) { } async function getOffersByPublicKey(publicKey) { - const offers = await OfferCreated.findAll({ + const offers = await models.OfferCreated.findAll({ where: { public_key: publicKey, }, @@ -66,7 +64,7 @@ async function getOffersByPublicKey(publicKey) { const offersToReturn = []; if (offers) { for (const someOffer of offers) { - const offerDetails = await OfferDetailsSet.findOne({ + const offerDetails = await models.OfferDetailsSet.findOne({ where: { offer_uuid: someOffer.uuid, }, diff --git a/src/services/profileService.js b/src/services/profileService.js index 358499c..a3013b0 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -1,9 +1,9 @@ const uuid = require('uuid'); -const ContactDetailsSet = require('../models/ContactDetailsSet'); -const NymSet = require('../models/NymSet'); + +const models = require('../models'); async function setContactDetails(publicKey, encryptedContactDetails) { - return await ContactDetailsSet.create({ + return await models.ContactDetailsSet.create({ uuid: uuid.v7(), public_key: publicKey, encrypted_contact_details: encryptedContactDetails, @@ -12,7 +12,7 @@ async function setContactDetails(publicKey, encryptedContactDetails) { } async function setNym(publicKey, nym) { - return await NymSet.create({ + return await models.NymSet.create({ uuid: uuid.v7(), public_key: publicKey, nym: nym, @@ -21,8 +21,10 @@ async function setNym(publicKey, nym) { } async function areProfileDetailsComplete(publicKey) { - const isNymSet = await NymSet.findOne({ where: { public_key: publicKey } }); - const areContactDetailsSet = await ContactDetailsSet.findOne({ + const isNymSet = await models.NymSet.findOne({ + where: { public_key: publicKey }, + }); + const areContactDetailsSet = await models.ContactDetailsSet.findOne({ where: { public_key: publicKey, }, diff --git a/src/services/sessionService.js b/src/services/sessionService.js index 452f390..fdf20fc 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -1,7 +1,6 @@ const uuid = require('uuid'); -const SessionCreated = require('../models/SessionCreated'); -const SessionRelatedToPublickey = require('../models/SessionRelatedToPublickey'); +const models = require('../models'); const invitesService = require('./invitesService'); const constants = require('../constants'); @@ -13,7 +12,7 @@ async function createSession(sessionUuid) { expiryTimestamp.getSeconds() + constants.DEFAULT_SESSION_DURATION_SECONDS ); - return await SessionCreated.create({ + return await models.SessionCreated.create({ uuid: sessionUuid, created_at: currentTimestamp.toISOString(), expires_at: expiryTimestamp.toISOString(), @@ -21,7 +20,7 @@ async function createSession(sessionUuid) { } async function isSessionValid(sessionUuid) { - const currentSession = await SessionCreated.findOne({ + const currentSession = await models.SessionCreated.findOne({ where: { uuid: sessionUuid, }, @@ -47,7 +46,7 @@ async function relateSessionToPublicKey(sessionUuid, publicKey) { throw Error('Public key is not signed up.'); } - return SessionRelatedToPublickey.create({ + return models.SessionRelatedToPublickey.create({ uuid: uuid.v7(), session_uuid: sessionUuid, public_key: publicKey, @@ -56,11 +55,12 @@ async function relateSessionToPublicKey(sessionUuid, publicKey) { } async function isSessionAuthorized(sessionUuid) { - const isSessionRelatedToPublicKey = await SessionRelatedToPublickey.findOne({ - where: { - session_uuid: sessionUuid, - }, - }); + const isSessionRelatedToPublicKey = + await models.SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); if (isSessionRelatedToPublicKey) { return true; @@ -70,11 +70,12 @@ async function isSessionAuthorized(sessionUuid) { } async function getPublicKeyRelatedToSession(sessionUuid) { - const sessionRelatedToPublickey = await SessionRelatedToPublickey.findOne({ - where: { - session_uuid: sessionUuid, - }, - }); + const sessionRelatedToPublickey = + await models.SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); if (sessionRelatedToPublickey) { return sessionRelatedToPublickey.public_key; From 3c5aa812abac573942d214d56e606612d5c48842 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:14:30 +0100 Subject: [PATCH 073/176] refactor first middleware --- src/middlewares/attachPublicKeyMiddleware.js | 28 ++++++++++++-------- src/middlewares/index.js | 7 ++++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/middlewares/attachPublicKeyMiddleware.js b/src/middlewares/attachPublicKeyMiddleware.js index c6ad835..4a60104 100644 --- a/src/middlewares/attachPublicKeyMiddleware.js +++ b/src/middlewares/attachPublicKeyMiddleware.js @@ -1,14 +1,20 @@ -const sessionService = require('../services/sessionService'); - -async function attachPublicKeyMiddleware(req, res, next) { - const publicKey = await sessionService.getPublicKeyRelatedToSession( - req.cookies.sessionUuid - ); - - if (publicKey) { - req.cookies.publicKey = publicKey; +class AttachPublicKeyMiddlewareProvider { + constructor(sessionService) { + this.sessionService = sessionService; + } + + provide() { + return async (req, res, next) => { + const publicKey = await this.sessionService.getPublicKeyRelatedToSession( + req.cookies.sessionUuid + ); + + if (publicKey) { + req.cookies.publicKey = publicKey; + } + next(); + }; } - next(); } -module.exports = attachPublicKeyMiddleware; +module.exports = AttachPublicKeyMiddlewareProvider; diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 6a56fa8..289f72e 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,10 +1,15 @@ const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); -const attachPublicKeyMiddleware = require('./attachPublicKeyMiddleware'); const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); const rejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); const createSessionMiddleware = require('./createSessionMiddleware'); +const sessionService = require('../services/sessionService'); +const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); +const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider( + sessionService +).provide(); + module.exports = { redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, From 72b68e772b1942b7fdc48d54d4ccc3af5f069407 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:30:28 +0100 Subject: [PATCH 074/176] another --- src/middlewares/createSessionMiddleware.js | 56 ++++++++++++---------- src/middlewares/index.js | 8 +++- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/middlewares/createSessionMiddleware.js b/src/middlewares/createSessionMiddleware.js index 8d181d6..53e15dd 100644 --- a/src/middlewares/createSessionMiddleware.js +++ b/src/middlewares/createSessionMiddleware.js @@ -1,33 +1,39 @@ 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; +class CreateSessionMiddlewareProvider { + constructor({ constants, sessionService }) { + this.constants = constants; + this.sessionService = sessionService; } - if (sessionUuid) { - if (!(await sessionService.isSessionValid(sessionUuid))) { - const newSession = await setAndPersistNewSession(res); - req.cookies.sessionUuid = newSession.uuid; - } + 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(); + }; } - 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 = createSessionMiddleware; +module.exports = CreateSessionMiddlewareProvider; diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 289f72e..8ad10ef 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -2,7 +2,6 @@ const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMidd const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); const rejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); -const createSessionMiddleware = require('./createSessionMiddleware'); const sessionService = require('../services/sessionService'); const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); @@ -10,6 +9,13 @@ const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider( sessionService ).provide(); +const constants = require('../constants'); +const CreateSessionMiddlewareProvider = require('./createSessionMiddleware'); +const createSessionMiddleware = new CreateSessionMiddlewareProvider({ + constants, + sessionService, +}).provide(); + module.exports = { redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, From 1ec83c5e5dbbc83544889ca667b68aac984efcae Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:33:15 +0100 Subject: [PATCH 075/176] another --- src/middlewares/index.js | 6 +++- .../rejectIfNotAuthorizedMiddleware.js | 30 ++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 8ad10ef..4a13843 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,7 +1,6 @@ const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); -const rejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); const sessionService = require('../services/sessionService'); const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); @@ -16,6 +15,11 @@ const createSessionMiddleware = new CreateSessionMiddlewareProvider({ sessionService, }).provide(); +const RejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); +const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware( + sessionService +).provide(); + module.exports = { redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, diff --git a/src/middlewares/rejectIfNotAuthorizedMiddleware.js b/src/middlewares/rejectIfNotAuthorizedMiddleware.js index 85e67ad..548830e 100644 --- a/src/middlewares/rejectIfNotAuthorizedMiddleware.js +++ b/src/middlewares/rejectIfNotAuthorizedMiddleware.js @@ -1,13 +1,23 @@ -const sessionService = require('../services/sessionService'); - -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.', - }); +class RejectIfNotAuthorizedMiddleware { + constructor({ sessionService }) { + this.sessionService = 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(); + }; } - next(); } -module.exports = rejectIfNotAuthorizedMiddleware; +module.exports = RejectIfNotAuthorizedMiddleware; From 6ba8eed42736865ee292ca6dc2290017cb4c2756 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:35:48 +0100 Subject: [PATCH 076/176] another --- src/middlewares/index.js | 6 +++++- src/middlewares/redirectHomeIfAuthorized.js | 22 ++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 4a13843..c4fc3c7 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,6 +1,5 @@ const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); -const redirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); const sessionService = require('../services/sessionService'); const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); @@ -20,6 +19,11 @@ const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware( sessionService ).provide(); +const RedirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); +const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized( + sessionService +).provide(); + module.exports = { redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, diff --git a/src/middlewares/redirectHomeIfAuthorized.js b/src/middlewares/redirectHomeIfAuthorized.js index 9619c68..4fcca17 100644 --- a/src/middlewares/redirectHomeIfAuthorized.js +++ b/src/middlewares/redirectHomeIfAuthorized.js @@ -1,10 +1,18 @@ -const sessionService = require('../services/sessionService'); - -async function redirectHomeIfAuthorized(req, res, next) { - if (await sessionService.isSessionAuthorized(req.cookies.sessionUuid)) { - return res.redirect('/home'); +class RedirectHomeIfAuthorized { + constructor(sessionService) { + this.sessionService = sessionService; + } + + provide() { + return async (req, res, next) => { + if ( + await this.sessionService.isSessionAuthorized(req.cookies.sessionUuid) + ) { + return res.redirect('/home'); + } + next(); + }; } - next(); } -module.exports = redirectHomeIfAuthorized; +module.exports = RedirectHomeIfAuthorized; From c4131c82aaaf26833422aa8f9c86686bbe4a730c Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:39:16 +0100 Subject: [PATCH 077/176] another --- src/middlewares/index.js | 16 ++++++++----- src/middlewares/redirectHomeIfAuthorized.js | 2 +- .../redirectIfNotAuthorizedMiddleware.js | 24 +++++++++++++------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/middlewares/index.js b/src/middlewares/index.js index c4fc3c7..c546507 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,4 +1,3 @@ -const redirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); const sessionService = require('../services/sessionService'); @@ -15,13 +14,18 @@ const createSessionMiddleware = new CreateSessionMiddlewareProvider({ }).provide(); const RejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); -const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware( - sessionService -).provide(); +const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware({ + sessionService, +}).provide(); const RedirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); -const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized( - sessionService +const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized({ + sessionService, +}).provide(); + +const RedirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); +const redirectIfNotAuthorizedMiddleware = new RedirectIfNotAuthorizedMiddleware( + { sessionService } ).provide(); module.exports = { diff --git a/src/middlewares/redirectHomeIfAuthorized.js b/src/middlewares/redirectHomeIfAuthorized.js index 4fcca17..f3aedf2 100644 --- a/src/middlewares/redirectHomeIfAuthorized.js +++ b/src/middlewares/redirectHomeIfAuthorized.js @@ -1,5 +1,5 @@ class RedirectHomeIfAuthorized { - constructor(sessionService) { + constructor({ sessionService }) { this.sessionService = sessionService; } diff --git a/src/middlewares/redirectIfNotAuthorizedMiddleware.js b/src/middlewares/redirectIfNotAuthorizedMiddleware.js index f4b9e47..353f21c 100644 --- a/src/middlewares/redirectIfNotAuthorizedMiddleware.js +++ b/src/middlewares/redirectIfNotAuthorizedMiddleware.js @@ -1,10 +1,20 @@ -const sessionService = require('../services/sessionService'); - -async function redirectIfNotAuthorizedMiddleware(req, res, next) { - if (!(await sessionService.isSessionAuthorized(req.cookies.sessionUuid))) { - return res.redirect('/login'); +class RedirectIfNotAuthorizedMiddleware { + constructor({ sessionService }) { + this.sessionService = sessionService; + } + + provide() { + return async (req, res, next) => { + if ( + !(await this.sessionService.isSessionAuthorized( + req.cookies.sessionUuid + )) + ) { + return res.redirect('/login'); + } + next(); + }; } - next(); } -module.exports = redirectIfNotAuthorizedMiddleware; +module.exports = RedirectIfNotAuthorizedMiddleware; From 3623ff11cb851394028665a8a202b45a9b08a0bd Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 00:46:51 +0100 Subject: [PATCH 078/176] last one --- src/middlewares/index.js | 9 ++++++-- ...directIfMissingProfileDetailsMiddleware.js | 23 +++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/middlewares/index.js b/src/middlewares/index.js index c546507..017f16d 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,5 +1,3 @@ -const redirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); - const sessionService = require('../services/sessionService'); const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider( @@ -28,6 +26,13 @@ const redirectIfNotAuthorizedMiddleware = new RedirectIfNotAuthorizedMiddleware( { sessionService } ).provide(); +const profileService = require('../services/profileService'); +const RedirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); +const redirectIfMissingProfileDetailsMiddleware = + new RedirectIfMissingProfileDetailsMiddleware({ + profileService, + }).provide(); + module.exports = { redirectIfNotAuthorizedMiddleware, attachPublicKeyMiddleware, diff --git a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js index 934048d..d7e758f 100644 --- a/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js +++ b/src/middlewares/redirectIfMissingProfileDetailsMiddleware.js @@ -1,12 +1,17 @@ -const profileService = require('../services/profileService'); - -async function redirectIfMissingProfileDetailsMiddleware(req, res, next) { - const publicKey = req.cookies.publicKey; - if (!(await profileService.areProfileDetailsComplete(publicKey))) { - res.redirect('/createProfile'); +class RedirectIfMissingProfileDetailsMiddleware { + constructor({ profileService }) { + this.profileService = profileService; } - next(); -} + provide() { + return async (req, res, next) => { + const publicKey = req.cookies.publicKey; + if (!(await this.profileService.areProfileDetailsComplete(publicKey))) { + res.redirect('/createProfile'); + } -module.exports = redirectIfMissingProfileDetailsMiddleware; + next(); + }; + } +} +module.exports = RedirectIfMissingProfileDetailsMiddleware; From 1dca924b837ae5da412dff8976d92ca7c244bf14 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:01:12 +0100 Subject: [PATCH 079/176] all middlewares provider --- src/dependencies.js | 12 ++- src/middlewares/attachPublicKeyMiddleware.js | 2 +- src/middlewares/index.js | 86 +++++++++++--------- 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index b182680..668261a 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -3,12 +3,18 @@ const express = require('express'); function buildDependencies() { const dependencies = {}; - const middlewares = require('./middlewares'); - dependencies.middlewares = middlewares; - + const constants = require('./constants'); const services = require('./services'); const errors = require('./errors'); + 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, diff --git a/src/middlewares/attachPublicKeyMiddleware.js b/src/middlewares/attachPublicKeyMiddleware.js index 4a60104..05c7758 100644 --- a/src/middlewares/attachPublicKeyMiddleware.js +++ b/src/middlewares/attachPublicKeyMiddleware.js @@ -1,5 +1,5 @@ class AttachPublicKeyMiddlewareProvider { - constructor(sessionService) { + constructor({ sessionService }) { this.sessionService = sessionService; } diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 017f16d..1a316a1 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -1,43 +1,55 @@ -const sessionService = require('../services/sessionService'); -const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); -const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider( - sessionService -).provide(); +class MiddlewaresProvider { + constructor({ constants, sessionService, profileService }) { + this.constants = constants; + this.sessionService = sessionService; + this.profileService = profileService; + } -const constants = require('../constants'); -const CreateSessionMiddlewareProvider = require('./createSessionMiddleware'); -const createSessionMiddleware = new CreateSessionMiddlewareProvider({ - constants, - sessionService, -}).provide(); + provide() { + const AttachPublicKeyMiddlewareProvider = require('./attachPublicKeyMiddleware'); + const attachPublicKeyMiddleware = new AttachPublicKeyMiddlewareProvider({ + sessionService: this.sessionService, + }).provide(); -const RejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); -const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware({ - sessionService, -}).provide(); + const CreateSessionMiddlewareProvider = require('./createSessionMiddleware'); + const createSessionMiddleware = new CreateSessionMiddlewareProvider({ + constants: this.constants, + sessionService: this.sessionService, + }).provide(); -const RedirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); -const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized({ - sessionService, -}).provide(); + const RejectIfNotAuthorizedMiddleware = require('./rejectIfNotAuthorizedMiddleware'); + const rejectIfNotAuthorizedMiddleware = new RejectIfNotAuthorizedMiddleware( + { + sessionService: this.sessionService, + } + ).provide(); -const RedirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); -const redirectIfNotAuthorizedMiddleware = new RedirectIfNotAuthorizedMiddleware( - { sessionService } -).provide(); + const RedirectHomeIfAuthorized = require('./redirectHomeIfAuthorized'); + const redirectHomeIfAuthorized = new RedirectHomeIfAuthorized({ + sessionService: this.sessionService, + }).provide(); -const profileService = require('../services/profileService'); -const RedirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); -const redirectIfMissingProfileDetailsMiddleware = - new RedirectIfMissingProfileDetailsMiddleware({ - profileService, - }).provide(); + const RedirectIfNotAuthorizedMiddleware = require('./redirectIfNotAuthorizedMiddleware'); + const redirectIfNotAuthorizedMiddleware = + new RedirectIfNotAuthorizedMiddleware({ + sessionService: this.sessionService, + }).provide(); -module.exports = { - redirectIfNotAuthorizedMiddleware, - attachPublicKeyMiddleware, - redirectIfMissingProfileDetailsMiddleware, - redirectHomeIfAuthorized, - rejectIfNotAuthorizedMiddleware, - createSessionMiddleware, -}; + const RedirectIfMissingProfileDetailsMiddleware = require('./redirectIfMissingProfileDetailsMiddleware'); + const redirectIfMissingProfileDetailsMiddleware = + new RedirectIfMissingProfileDetailsMiddleware({ + profileService: this.profileService, + }).provide(); + + return { + redirectIfNotAuthorizedMiddleware, + attachPublicKeyMiddleware, + redirectIfMissingProfileDetailsMiddleware, + redirectHomeIfAuthorized, + rejectIfNotAuthorizedMiddleware, + createSessionMiddleware, + }; + } +} + +module.exports = MiddlewaresProvider; From cdc344c528831feb7bea528a8029313c5d7415b3 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:09:45 +0100 Subject: [PATCH 080/176] services thingy --- src/dependencies.js | 7 ++++--- src/services/index.js | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 668261a..5b3e4c7 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -2,10 +2,11 @@ const express = require('express'); function buildDependencies() { const dependencies = {}; - - const constants = require('./constants'); - const services = require('./services'); const errors = require('./errors'); + const constants = require('./constants'); + + const ServicesProvider = require('./services'); + const services = new ServicesProvider().provide(); const MiddlewaresProvider = require('./middlewares'); const middlewares = new MiddlewaresProvider({ diff --git a/src/services/index.js b/src/services/index.js index 3e35e60..06fe6e3 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -1,3 +1,18 @@ +class ServicesProvider { + constructor() {} + + provide() { + return { + invitesService, + nostrService, + loginService, + sessionService, + profileService, + offerService, + }; + } +} + const invitesService = require('../services/invitesService'); const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); @@ -5,11 +20,4 @@ const sessionService = require('../services/sessionService'); const profileService = require('../services/profileService'); const offerService = require('../services/offerService'); -module.exports = { - invitesService, - nostrService, - loginService, - sessionService, - profileService, - offerService, -}; +module.exports = ServicesProvider; From 56aa41675105af2ccbd29fdf6b2150f71d5be62b Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:26:36 +0100 Subject: [PATCH 081/176] first service --- src/services/index.js | 15 ++-- src/services/offerService.js | 154 ++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 80 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index 06fe6e3..027cb45 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,6 +2,14 @@ class ServicesProvider { constructor() {} provide() { + 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 OfferServiceProvider = require('../services/offerService'); + const offerService = new OfferServiceProvider().provide(); + return { invitesService, nostrService, @@ -13,11 +21,4 @@ class ServicesProvider { } } -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'); - module.exports = ServicesProvider; diff --git a/src/services/offerService.js b/src/services/offerService.js index ab1f19e..25f7b6d 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -4,76 +4,18 @@ const models = require('../models'); const errors = require('../errors'); -async function createOffer(publicKey, offerDetails) { - const offerCreated = await models.OfferCreated.create({ - uuid: uuid.v7(), - public_key: publicKey, - created_at: new Date().toISOString(), - }); - - await 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(), - }); -} - -async function deleteOffer(offerUuid) { - const offerExists = Boolean( - await models.OfferCreated.findOne({ where: { uuid: offerUuid } }) - ); - const offerHasBeenDeleted = Boolean( - await models.OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) - ); - - if (!offerExists || offerHasBeenDeleted) { - throw new errors.NotFoundError(`Could not find the offer.`); - } - - return models.OfferDeleted.create({ - uuid: uuid.v7(), - offer_uuid: offerUuid, - created_at: new Date().toISOString(), - }); -} - -async function getOffersByPublicKey(publicKey) { - const offers = await models.OfferCreated.findAll({ - where: { - public_key: publicKey, - }, - }); - - console.log(offers); - - if (!offers) { - return []; - } - - const offersToReturn = []; - if (offers) { - for (const someOffer of offers) { - const offerDetails = await models.OfferDetailsSet.findOne({ - where: { - offer_uuid: someOffer.uuid, - }, - order: [['created_at', 'DESC']], +class OfferServiceProvider { + provide() { + async function createOffer(publicKey, offerDetails) { + const offerCreated = await models.OfferCreated.create({ + uuid: uuid.v7(), + public_key: publicKey, + created_at: new Date().toISOString(), }); - offersToReturn.push({ - uuid: someOffer.uuid, - public_key: someOffer.public_key, + await models.OfferDetailsSet.create({ + uuid: uuid.v7(), + offer_uuid: offerCreated.uuid, wants: offerDetails.wants, premium: offerDetails.premium, trade_amount_eur: offerDetails.trade_amount_eur, @@ -86,12 +28,78 @@ async function getOffersByPublicKey(publicKey) { 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, + created_at: new Date().toISOString(), }); } - } - return offersToReturn; + async function deleteOffer(offerUuid) { + const offerExists = Boolean( + await models.OfferCreated.findOne({ where: { uuid: offerUuid } }) + ); + const offerHasBeenDeleted = Boolean( + await models.OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) + ); + + if (!offerExists || offerHasBeenDeleted) { + throw new errors.NotFoundError(`Could not find the offer.`); + } + + return models.OfferDeleted.create({ + uuid: uuid.v7(), + offer_uuid: offerUuid, + created_at: new Date().toISOString(), + }); + } + + async function getOffersByPublicKey(publicKey) { + const offers = await models.OfferCreated.findAll({ + where: { + public_key: publicKey, + }, + }); + + console.log(offers); + + if (!offers) { + return []; + } + + const offersToReturn = []; + if (offers) { + for (const someOffer of offers) { + const offerDetails = await 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, getOffersByPublicKey, deleteOffer }; + } } -module.exports = { createOffer, getOffersByPublicKey, deleteOffer }; + +module.exports = OfferServiceProvider; From a2939a7f2ece8870c265aa82b81b48656708f0d7 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:28:47 +0100 Subject: [PATCH 082/176] profile service --- src/services/index.js | 3 +- src/services/profileService.js | 66 ++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index 027cb45..e38dc48 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -6,7 +6,8 @@ class ServicesProvider { const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); const sessionService = require('../services/sessionService'); - const profileService = require('../services/profileService'); + const ProfileServiceProvider = require('../services/profileService'); + const profileService = new ProfileServiceProvider().provide(); const OfferServiceProvider = require('../services/offerService'); const offerService = new OfferServiceProvider().provide(); diff --git a/src/services/profileService.js b/src/services/profileService.js index a3013b0..34bcd6f 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -2,35 +2,41 @@ const uuid = require('uuid'); const models = require('../models'); -async function setContactDetails(publicKey, encryptedContactDetails) { - return await models.ContactDetailsSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - encrypted_contact_details: encryptedContactDetails, - created_at: new Date().toISOString(), - }); +class ProfileServiceProvider { + provide() { + async function setContactDetails(publicKey, encryptedContactDetails) { + return await models.ContactDetailsSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + encrypted_contact_details: encryptedContactDetails, + created_at: new Date().toISOString(), + }); + } + + async function setNym(publicKey, nym) { + return await models.NymSet.create({ + uuid: uuid.v7(), + public_key: publicKey, + nym: nym, + created_at: new Date().toISOString(), + }); + } + + async function areProfileDetailsComplete(publicKey) { + const isNymSet = await models.NymSet.findOne({ + where: { public_key: publicKey }, + }); + const areContactDetailsSet = await models.ContactDetailsSet.findOne({ + where: { + public_key: publicKey, + }, + }); + + return isNymSet && areContactDetailsSet; + } + + return { setContactDetails, setNym, areProfileDetailsComplete }; + } } -async function setNym(publicKey, nym) { - return await models.NymSet.create({ - uuid: uuid.v7(), - public_key: publicKey, - nym: nym, - created_at: new Date().toISOString(), - }); -} - -async function areProfileDetailsComplete(publicKey) { - const isNymSet = await models.NymSet.findOne({ - where: { public_key: publicKey }, - }); - const areContactDetailsSet = await models.ContactDetailsSet.findOne({ - where: { - public_key: publicKey, - }, - }); - - return isNymSet && areContactDetailsSet; -} - -module.exports = { setContactDetails, setNym, areProfileDetailsComplete }; +module.exports = ProfileServiceProvider; From 69290f4c7a7dc39d0af5f0d884143c59713b116e Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:46:17 +0100 Subject: [PATCH 083/176] session service --- src/services/index.js | 7 +- src/services/sessionService.js | 160 +++++++++++++++++---------------- 2 files changed, 91 insertions(+), 76 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index e38dc48..f4c61c3 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -5,7 +5,12 @@ class ServicesProvider { const invitesService = require('../services/invitesService'); const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); - const sessionService = require('../services/sessionService'); + + const SessionServiceProvider = require('../services/sessionService'); + const sessionService = new SessionServiceProvider({ + invitesService, + }).provide(); + const ProfileServiceProvider = require('../services/profileService'); const profileService = new ProfileServiceProvider().provide(); const OfferServiceProvider = require('../services/offerService'); diff --git a/src/services/sessionService.js b/src/services/sessionService.js index fdf20fc..d489d60 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -2,92 +2,102 @@ const uuid = require('uuid'); const models = require('../models'); -const invitesService = require('./invitesService'); const constants = require('../constants'); -async function createSession(sessionUuid) { - const currentTimestamp = new Date(); - const expiryTimestamp = new Date(currentTimestamp.getTime()); - expiryTimestamp.setSeconds( - expiryTimestamp.getSeconds() + constants.DEFAULT_SESSION_DURATION_SECONDS - ); - - return await models.SessionCreated.create({ - uuid: sessionUuid, - created_at: currentTimestamp.toISOString(), - expires_at: expiryTimestamp.toISOString(), - }); -} - -async function isSessionValid(sessionUuid) { - const currentSession = await models.SessionCreated.findOne({ - where: { - uuid: sessionUuid, - }, - }); - - if (!currentSession) { - return false; +class SessionServiceProvider { + constructor({ invitesService }) { + this.invitesService = invitesService; } - if (currentSession.expires_at <= new Date()) { - return false; - } + provide() { + async function createSession(sessionUuid) { + const currentTimestamp = new Date(); + const expiryTimestamp = new Date(currentTimestamp.getTime()); + expiryTimestamp.setSeconds( + expiryTimestamp.getSeconds() + + constants.DEFAULT_SESSION_DURATION_SECONDS + ); - return true; -} + return await models.SessionCreated.create({ + uuid: sessionUuid, + created_at: currentTimestamp.toISOString(), + expires_at: expiryTimestamp.toISOString(), + }); + } -async function relateSessionToPublicKey(sessionUuid, publicKey) { - if (!(await isSessionValid(sessionUuid))) { - throw Error('Session is not valid anymore.'); - } + async function isSessionValid(sessionUuid) { + const currentSession = await models.SessionCreated.findOne({ + where: { + uuid: sessionUuid, + }, + }); - if (!(await invitesService.isPublicKeySignedUp(publicKey))) { - throw Error('Public key is not signed up.'); - } + if (!currentSession) { + return false; + } - return models.SessionRelatedToPublickey.create({ - uuid: uuid.v7(), - session_uuid: sessionUuid, - public_key: publicKey, - created_at: new Date().toISOString(), - }); -} + if (currentSession.expires_at <= new Date()) { + return false; + } -async function isSessionAuthorized(sessionUuid) { - const isSessionRelatedToPublicKey = - await models.SessionRelatedToPublickey.findOne({ - where: { + 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 models.SessionRelatedToPublickey.create({ + uuid: uuid.v7(), session_uuid: sessionUuid, - }, - }); + public_key: publicKey, + created_at: new Date().toISOString(), + }); + }; - if (isSessionRelatedToPublicKey) { - return true; + async function isSessionAuthorized(sessionUuid) { + const isSessionRelatedToPublicKey = + await models.SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); + + if (isSessionRelatedToPublicKey) { + return true; + } + + return false; + } + + async function getPublicKeyRelatedToSession(sessionUuid) { + const sessionRelatedToPublickey = + await models.SessionRelatedToPublickey.findOne({ + where: { + session_uuid: sessionUuid, + }, + }); + + if (sessionRelatedToPublickey) { + return sessionRelatedToPublickey.public_key; + } + + return null; + } + + return { + createSession, + isSessionValid, + relateSessionToPublicKey, + isSessionAuthorized, + getPublicKeyRelatedToSession, + }; } - - return false; } -async function getPublicKeyRelatedToSession(sessionUuid) { - const sessionRelatedToPublickey = - await models.SessionRelatedToPublickey.findOne({ - where: { - session_uuid: sessionUuid, - }, - }); - - if (sessionRelatedToPublickey) { - return sessionRelatedToPublickey.public_key; - } - - return null; -} - -module.exports = { - createSession, - isSessionValid, - relateSessionToPublicKey, - isSessionAuthorized, - getPublicKeyRelatedToSession, -}; +module.exports = SessionServiceProvider; From 15217dc77a9b367edcc88b6a87f8613eb9d9440b Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 01:59:23 +0100 Subject: [PATCH 084/176] invites --- src/commands/createAppInvite.js | 6 +- src/services/index.js | 6 +- src/services/invitesService.js | 226 +++++++++++++++++--------------- 3 files changed, 131 insertions(+), 107 deletions(-) diff --git a/src/commands/createAppInvite.js b/src/commands/createAppInvite.js index 0767123..f4407bb 100644 --- a/src/commands/createAppInvite.js +++ b/src/commands/createAppInvite.js @@ -1,4 +1,8 @@ -const invitesService = require('../services/invitesService'); +const nostrService = require('../services/nostrService'); +const InvitesServiceProvider = require('../services/invitesService'); +const invitesService = new InvitesServiceProvider({ + nostrService, +}).provide(); module.exports = async function createAppInvite(inviterNpub) { const appInvite = await invitesService.createAppInvite(inviterNpub); diff --git a/src/services/index.js b/src/services/index.js index f4c61c3..efb197d 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,10 +2,14 @@ class ServicesProvider { constructor() {} provide() { - const invitesService = require('../services/invitesService'); const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); + const InvitesServiceProvider = require('../services/invitesService'); + const invitesService = new InvitesServiceProvider({ + nostrService, + }).provide(); + const SessionServiceProvider = require('../services/sessionService'); const sessionService = new SessionServiceProvider({ invitesService, diff --git a/src/services/invitesService.js b/src/services/invitesService.js index 16d7805..75e96f1 100644 --- a/src/services/invitesService.js +++ b/src/services/invitesService.js @@ -4,117 +4,133 @@ const nostrService = require('./nostrService'); const models = require('../models'); const errors = require('../errors'); -async function appInviteExists(inviteUuid) { - const invite = await models.AppInviteCreated.findOne({ - where: { uuid: inviteUuid }, - }); - if (invite) { - return true; - } - return false; -} - -async function getAppInvite(inviteUuid) { - const invite = await models.AppInviteCreated.findOne({ - where: { uuid: inviteUuid }, - }); - return invite; -} - -async function isAppInviteSpent(appInviteUuid) { - const signUpChallengeCompleted = - await models.SignUpChallengeCompleted.findOne({ - where: { - app_invite_uuid: appInviteUuid, - }, - }); - - if (signUpChallengeCompleted) { - return true; - } - return false; -} - -async function createAppInvite(inviterPubKey) { - return await models.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."); +class InvitesServiceProvider { + constructor({ nostrService }) { + this.nostrService = nostrService; } - if (await isAppInviteSpent(appInviteUuid)) { - throw new errors.AlreadyUsedError('Invite has already been used.'); - } - - const nostrChallenge = await nostrService.createNostrChallenge(); - - return await models.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 models.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 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(), + provide() { + async function appInviteExists(inviteUuid) { + const invite = await models.AppInviteCreated.findOne({ + where: { uuid: inviteUuid }, + }); + if (invite) { + return true; + } + return false; } - ); - return completedSignUpChallenge; -} + async function getAppInvite(inviteUuid) { + const invite = await models.AppInviteCreated.findOne({ + where: { uuid: inviteUuid }, + }); + return invite; + } -async function isPublicKeySignedUp(publicKey) { - const signUpChallengeCompleted = - await models.SignUpChallengeCompleted.findOne({ - where: { - public_key: publicKey, - }, - }); + async function isAppInviteSpent(appInviteUuid) { + const signUpChallengeCompleted = + await models.SignUpChallengeCompleted.findOne({ + where: { + app_invite_uuid: appInviteUuid, + }, + }); - if (signUpChallengeCompleted) { - return true; + if (signUpChallengeCompleted) { + return true; + } + return false; + } + + async function createAppInvite(inviterPubKey) { + return await models.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 models.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 models.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 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; + } + + async function isPublicKeySignedUp(publicKey) { + const signUpChallengeCompleted = + await models.SignUpChallengeCompleted.findOne({ + where: { + public_key: publicKey, + }, + }); + + if (signUpChallengeCompleted) { + return true; + } + + return false; + } + + return { + appInviteExists, + getAppInvite, + isAppInviteSpent, + createAppInvite, + createSignUpChallenge, + verifySignUpChallenge, + isPublicKeySignedUp, + }; } - - return false; } -module.exports = { - appInviteExists, - getAppInvite, - isAppInviteSpent, - createAppInvite, - createSignUpChallenge, - verifySignUpChallenge, - isPublicKeySignedUp, -}; +module.exports = InvitesServiceProvider; From f8a185e879b3ef4676a9ba119a9fa7dc4771c827 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 6 Mar 2025 02:19:23 +0100 Subject: [PATCH 085/176] nostr service and fix usage in invites --- src/commands/createAppInvite.js | 3 +- src/services/index.js | 3 +- src/services/invitesService.js | 17 ++- src/services/nostrService.js | 211 +++++++++++++++++--------------- 4 files changed, 124 insertions(+), 110 deletions(-) diff --git a/src/commands/createAppInvite.js b/src/commands/createAppInvite.js index f4407bb..6b36583 100644 --- a/src/commands/createAppInvite.js +++ b/src/commands/createAppInvite.js @@ -1,4 +1,5 @@ -const nostrService = require('../services/nostrService'); +const NostrServiceProvider = require('../services/nostrService'); +const nostrService = new NostrServiceProvider().provide(); const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ nostrService, diff --git a/src/services/index.js b/src/services/index.js index efb197d..deaaa8b 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,9 +2,10 @@ class ServicesProvider { constructor() {} provide() { - const nostrService = require('../services/nostrService'); const loginService = require('../services/loginService'); + const NostrServiceProvider = require('../services/nostrService'); + const nostrService = new NostrServiceProvider().provide(); const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ nostrService, diff --git a/src/services/invitesService.js b/src/services/invitesService.js index 75e96f1..6b3fde0 100644 --- a/src/services/invitesService.js +++ b/src/services/invitesService.js @@ -1,6 +1,5 @@ const uuid = require('uuid'); -const nostrService = require('./nostrService'); const models = require('../models'); const errors = require('../errors'); @@ -49,7 +48,7 @@ class InvitesServiceProvider { }); } - async function createSignUpChallenge(appInviteUuid) { + const createSignUpChallenge = async (appInviteUuid) => { if (!(await appInviteExists(appInviteUuid))) { throw new errors.NotFoundError("Invite doesn't exist."); } @@ -58,7 +57,7 @@ class InvitesServiceProvider { throw new errors.AlreadyUsedError('Invite has already been used.'); } - const nostrChallenge = await nostrService.createNostrChallenge(); + const nostrChallenge = await this.nostrService.createNostrChallenge(); return await models.SignUpChallengeCreated.create({ uuid: uuid.v7(), @@ -66,15 +65,15 @@ class InvitesServiceProvider { app_invite_uuid: appInviteUuid, created_at: new Date().toISOString(), }); - } + }; - async function verifySignUpChallenge(signedEvent) { + const verifySignUpChallenge = async (signedEvent) => { const challengeTag = signedEvent.tags.find( (tag) => tag[0] === 'challenge' ); const challenge = challengeTag[1]; - const nostrChallenge = await nostrService.getNostrChallenge( + const nostrChallenge = await this.nostrService.getNostrChallenge( null, challenge ); @@ -85,14 +84,14 @@ class InvitesServiceProvider { }, }); - if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) { + if (await this.nostrService.hasNostrChallengeBeenCompleted(challenge)) { throw new errors.AlreadyUsedError( 'This challenge has already been used.' ); } const completedNostrChallenge = - await nostrService.verifyNostrChallenge(signedEvent); + await this.nostrService.verifyNostrChallenge(signedEvent); const completedSignUpChallenge = await models.SignUpChallengeCompleted.create({ @@ -104,7 +103,7 @@ class InvitesServiceProvider { }); return completedSignUpChallenge; - } + }; async function isPublicKeySignedUp(publicKey) { const signUpChallengeCompleted = diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 412d0ba..51cb0d3 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -8,105 +8,118 @@ const models = require('../models'); const constants = require('../constants'); const errors = require('../errors'); -async function createNostrChallenge() { - const currentTimestamp = new Date(); - const expiryTimestamp = new Date(currentTimestamp.getTime()); - expiryTimestamp.setSeconds( - expiryTimestamp.getSeconds() + - constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS - ); +class NostrServiceProvider { + constructor() {} - const nostrChallenge = await models.NostrChallengeCreated.create({ - uuid: uuid.v7(), - challenge: crypto.randomBytes(32).toString('hex'), - expires_at: expiryTimestamp.toISOString(), - created_at: currentTimestamp.toISOString(), - }); + provide() { + 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 models.NostrChallengeCreated.create({ + uuid: uuid.v7(), + challenge: crypto.randomBytes(32).toString('hex'), + expires_at: expiryTimestamp.toISOString(), + created_at: currentTimestamp.toISOString(), + }); + + return nostrChallenge; + } + + async function getNostrChallenge( + nostrChallengeUuid = null, + challenge = null + ) { + if (nostrChallengeUuid) { + return await models.NostrChallengeCreated.findOne({ + where: { + uuid: nostrChallengeUuid, + }, + }); + } + + if (challenge) { + return await models.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 models.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 models.NostrChallengeCreated.findOne({ + where: { + challenge: challengeString, + expires_at: { + [Op.gt]: new Date(), + }, + }, + }); + + if (nostrChallenge) { + return true; + } + return false; + } + + async function hasNostrChallengeBeenCompleted(challengeString) { + const completedNostrChallenge = + await models.NostrChallengeCompleted.findOne({ + where: { + challenge: challengeString, + }, + }); + + if (completedNostrChallenge) { + return true; + } + return false; + } + + return { + createNostrChallenge, + getNostrChallenge, + verifyNostrChallenge, + isNostrChallengeFresh, + hasNostrChallengeBeenCompleted, + }; + } } - -async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) { - if (nostrChallengeUuid) { - return await models.NostrChallengeCreated.findOne({ - where: { - uuid: nostrChallengeUuid, - }, - }); - } - - if (challenge) { - return await models.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 models.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 models.NostrChallengeCreated.findOne({ - where: { - challenge: challengeString, - expires_at: { - [Op.gt]: new Date(), - }, - }, - }); - - if (nostrChallenge) { - return true; - } - return false; -} - -async function hasNostrChallengeBeenCompleted(challengeString) { - const completedNostrChallenge = await models.NostrChallengeCompleted.findOne({ - where: { - challenge: challengeString, - }, - }); - - if (completedNostrChallenge) { - return true; - } - return false; -} - -module.exports = { - createNostrChallenge, - getNostrChallenge, - verifyNostrChallenge, - isNostrChallengeFresh, - hasNostrChallengeBeenCompleted, -}; +module.exports = NostrServiceProvider; From b680ede093d4fb6aee7921a181f8a00d8a4adb14 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 12:31:07 +0100 Subject: [PATCH 086/176] finished all services --- src/services/index.js | 12 +++- src/services/loginService.js | 105 ++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index deaaa8b..2c0d0ce 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -2,7 +2,11 @@ class ServicesProvider { constructor() {} provide() { - const loginService = require('../services/loginService'); + /* + Up next: + - start passing all service stuff from outside + - also remember to get CLi fixed and working + */ const NostrServiceProvider = require('../services/nostrService'); const nostrService = new NostrServiceProvider().provide(); @@ -11,6 +15,12 @@ class ServicesProvider { nostrService, }).provide(); + const LoginServiceProvider = require('../services/loginService'); + const loginService = new LoginServiceProvider({ + nostrService, + invitesService, + }).provide(); + const SessionServiceProvider = require('../services/sessionService'); const sessionService = new SessionServiceProvider({ invitesService, diff --git a/src/services/loginService.js b/src/services/loginService.js index 3de0090..db6b2cf 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -1,57 +1,70 @@ const uuid = require('uuid'); -const nostrService = require('./nostrService'); -const invitesService = require('./invitesService'); const models = require('../models'); const errors = require('../errors'); -async function createLoginChallenge() { - const nostrChallenge = await nostrService.createNostrChallenge(); - - return await models.LoginChallengeCreated.create({ - uuid: uuid.v7(), - nostr_challenge_uuid: nostrChallenge.uuid, - created_at: new Date().toISOString(), - }); -} - -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.'); +class LoginServiceProvider { + constructor({ nostrService, invitesService }) { + this.nostrService = nostrService; + this.invitesService = invitesService; } - const completedNostrChallenge = - await nostrService.verifyNostrChallenge(signedEvent); + provide() { + const createLoginChallenge = async () => { + const nostrChallenge = await this.nostrService.createNostrChallenge(); - if ( - !(await invitesService.isPublicKeySignedUp( - completedNostrChallenge.public_key - )) - ) { - console.log('helo4'); - throw new errors.ForbiddenError( - `Public key ${completedNostrChallenge.public_key} is not authorized.` - ); + return await models.LoginChallengeCreated.create({ + uuid: uuid.v7(), + nostr_challenge_uuid: nostrChallenge.uuid, + created_at: new Date().toISOString(), + }); + }; + + 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 errors.AlreadyUsedError( + 'This challenge has already been used.' + ); + } + + const completedNostrChallenge = + await this.nostrService.verifyNostrChallenge(signedEvent); + + if ( + !(await this.invitesService.isPublicKeySignedUp( + completedNostrChallenge.public_key + )) + ) { + console.log('helo4'); + throw new errors.ForbiddenError( + `Public key ${completedNostrChallenge.public_key} is not authorized.` + ); + } + + const completedLoginChallenge = + await models.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; + }; + + return { + createLoginChallenge, + verifyLoginChallenge, + }; } - - const completedLoginChallenge = await models.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, -}; +module.exports = LoginServiceProvider; From 9246da2e84355cad34415e59a77c53a5ae5ed6ea Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 12:52:15 +0100 Subject: [PATCH 087/176] cli now uses DI --- src/cli.js | 35 +++++++++++++++++++++++++-------- src/commands/createAppInvite.js | 23 +++++++++++++++++----- src/dependencies.js | 1 + 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/cli.js b/src/cli.js index 3776ee3..f4e36c8 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,13 +1,32 @@ const { Command } = require('commander'); -const program = new Command(); +const { buildDependencies } = require('./dependencies'); -const createAppInviteCommand = require('./commands/createAppInvite'); +function buildCLIDependencies() { + const appDependencies = buildDependencies(); -program.version('1.0.0').description('CLI for managing web app tasks'); + const CreateAppInviteProvider = require('./commands/createAppInvite'); + const createAppInvite = new CreateAppInviteProvider({ + nostrService: appDependencies.services.nostrService, + invitesService: appDependencies.services.invitesService, + }).provide(); -program - .command('createAppInvite ') - .description('Create an invite') - .action(createAppInviteCommand); + return { createAppInviteCommand: createAppInvite }; +} -program.parse(process.argv); +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); diff --git a/src/commands/createAppInvite.js b/src/commands/createAppInvite.js index 6b36583..1a7c41b 100644 --- a/src/commands/createAppInvite.js +++ b/src/commands/createAppInvite.js @@ -5,8 +5,21 @@ const invitesService = new InvitesServiceProvider({ nostrService, }).provide(); -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}`); -}; +class CreateAppInviteProvider { + constructor({ nostrService, invitesService }) { + this.nostrService = nostrService; + this.invitesService = invitesService; + } + + provide() { + const createAppInvite = async (inviterNpub) => { + const appInvite = await invitesService.createAppInvite(inviterNpub); + console.log('Invite created'); + console.log(`Check at http://localhost/invite/${appInvite.uuid}`); + }; + + return createAppInvite; + } +} + +module.exports = CreateAppInviteProvider; diff --git a/src/dependencies.js b/src/dependencies.js index 5b3e4c7..ce71d18 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -7,6 +7,7 @@ function buildDependencies() { const ServicesProvider = require('./services'); const services = new ServicesProvider().provide(); + dependencies.services = services; const MiddlewaresProvider = require('./middlewares'); const middlewares = new MiddlewaresProvider({ From 5c294b89a16d9a2e950d4d75503248c1ff2abd58 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 12:53:37 +0100 Subject: [PATCH 088/176] just a silly error change --- src/services/nostrService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 51cb0d3..46573be 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -1,6 +1,6 @@ const uuid = require('uuid'); const crypto = require('crypto'); -const { Op, TimeoutError } = require('sequelize'); +const { Op } = require('sequelize'); const { verifyEvent } = require('nostr-tools'); const models = require('../models'); @@ -60,7 +60,7 @@ class NostrServiceProvider { const challenge = challengeTag[1]; if (!(await isNostrChallengeFresh(challenge))) { - throw TimeoutError('Challenge expired, request new one.'); + throw errors.ExpiredError('Challenge expired, request new one.'); } if (await hasNostrChallengeBeenCompleted(challenge)) { From 9f4bc729e24dd32a0377ed3fcf5d21add44b6734 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 13:03:44 +0100 Subject: [PATCH 089/176] finish the cli stuff --- src/cli.js | 1 - src/commands/createAppInvite.js | 12 ++---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cli.js b/src/cli.js index f4e36c8..dae7913 100644 --- a/src/cli.js +++ b/src/cli.js @@ -6,7 +6,6 @@ function buildCLIDependencies() { const CreateAppInviteProvider = require('./commands/createAppInvite'); const createAppInvite = new CreateAppInviteProvider({ - nostrService: appDependencies.services.nostrService, invitesService: appDependencies.services.invitesService, }).provide(); diff --git a/src/commands/createAppInvite.js b/src/commands/createAppInvite.js index 1a7c41b..610c098 100644 --- a/src/commands/createAppInvite.js +++ b/src/commands/createAppInvite.js @@ -1,19 +1,11 @@ -const NostrServiceProvider = require('../services/nostrService'); -const nostrService = new NostrServiceProvider().provide(); -const InvitesServiceProvider = require('../services/invitesService'); -const invitesService = new InvitesServiceProvider({ - nostrService, -}).provide(); - class CreateAppInviteProvider { - constructor({ nostrService, invitesService }) { - this.nostrService = nostrService; + constructor({ invitesService }) { this.invitesService = invitesService; } provide() { const createAppInvite = async (inviterNpub) => { - const appInvite = await invitesService.createAppInvite(inviterNpub); + const appInvite = await this.invitesService.createAppInvite(inviterNpub); console.log('Invite created'); console.log(`Check at http://localhost/invite/${appInvite.uuid}`); }; From 73a71d3ccbfce734a35ac668d0e8c052b290e7dd Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 13:17:55 +0100 Subject: [PATCH 090/176] pass constants to nostr service --- src/services/index.js | 6 +++++- src/services/nostrService.js | 11 ++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index 2c0d0ce..454dc5a 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -1,3 +1,5 @@ +const constants = require('../constants'); + class ServicesProvider { constructor() {} @@ -9,7 +11,9 @@ class ServicesProvider { */ const NostrServiceProvider = require('../services/nostrService'); - const nostrService = new NostrServiceProvider().provide(); + const nostrService = new NostrServiceProvider({ + constants: constants, + }).provide(); const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ nostrService, diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 46573be..5239273 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -5,19 +5,20 @@ const { verifyEvent } = require('nostr-tools'); const models = require('../models'); -const constants = require('../constants'); const errors = require('../errors'); class NostrServiceProvider { - constructor() {} + constructor({ constants }) { + this.constants = constants; + } provide() { - async function createNostrChallenge() { + const createNostrChallenge = async () => { const currentTimestamp = new Date(); const expiryTimestamp = new Date(currentTimestamp.getTime()); expiryTimestamp.setSeconds( expiryTimestamp.getSeconds() + - constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS + this.constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS ); const nostrChallenge = await models.NostrChallengeCreated.create({ @@ -28,7 +29,7 @@ class NostrServiceProvider { }); return nostrChallenge; - } + }; async function getNostrChallenge( nostrChallengeUuid = null, From 00670f051fbfe72fd87e8c9bb152b5a4fdf6c4fc Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 13:27:28 +0100 Subject: [PATCH 091/176] no more models --- src/services/nostrService.js | 44 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/services/nostrService.js b/src/services/nostrService.js index 5239273..f881c5c 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -3,13 +3,11 @@ const crypto = require('crypto'); const { Op } = require('sequelize'); const { verifyEvent } = require('nostr-tools'); -const models = require('../models'); - -const errors = require('../errors'); - class NostrServiceProvider { - constructor({ constants }) { + constructor({ models, constants, errors }) { + this.models = models; this.constants = constants; + this.errors = errors; } provide() { @@ -21,7 +19,7 @@ class NostrServiceProvider { this.constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS ); - const nostrChallenge = await models.NostrChallengeCreated.create({ + const nostrChallenge = await this.models.NostrChallengeCreated.create({ uuid: uuid.v7(), challenge: crypto.randomBytes(32).toString('hex'), expires_at: expiryTimestamp.toISOString(), @@ -31,12 +29,12 @@ class NostrServiceProvider { return nostrChallenge; }; - async function getNostrChallenge( + const getNostrChallenge = async ( nostrChallengeUuid = null, challenge = null - ) { + ) => { if (nostrChallengeUuid) { - return await models.NostrChallengeCreated.findOne({ + return await this.models.NostrChallengeCreated.findOne({ where: { uuid: nostrChallengeUuid, }, @@ -44,7 +42,7 @@ class NostrServiceProvider { } if (challenge) { - return await models.NostrChallengeCreated.findOne({ + return await this.models.NostrChallengeCreated.findOne({ where: { challenge, }, @@ -52,40 +50,40 @@ class NostrServiceProvider { } throw Error('You need to pass a uuid or a challenge.'); - } + }; - async function verifyNostrChallenge(signedEvent) { + const verifyNostrChallenge = async (signedEvent) => { const challengeTag = signedEvent.tags.find( (tag) => tag[0] === 'challenge' ); const challenge = challengeTag[1]; if (!(await isNostrChallengeFresh(challenge))) { - throw errors.ExpiredError('Challenge expired, request new one.'); + throw this.errors.ExpiredError('Challenge expired, request new one.'); } if (await hasNostrChallengeBeenCompleted(challenge)) { - throw new errors.AlreadyUsedError( + throw new this.errors.AlreadyUsedError( 'Challenge already used, request new one.' ); } const isSignatureValid = verifyEvent(signedEvent); if (!isSignatureValid) { - throw new errors.InvalidSignatureError('Signature is not valid.'); + throw new this.errors.InvalidSignatureError('Signature is not valid.'); } - return await models.NostrChallengeCompleted.create({ + return await this.models.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 models.NostrChallengeCreated.findOne({ + const isNostrChallengeFresh = async (challengeString) => { + const nostrChallenge = await this.models.NostrChallengeCreated.findOne({ where: { challenge: challengeString, expires_at: { @@ -98,11 +96,11 @@ class NostrServiceProvider { return true; } return false; - } + }; - async function hasNostrChallengeBeenCompleted(challengeString) { + const hasNostrChallengeBeenCompleted = async (challengeString) => { const completedNostrChallenge = - await models.NostrChallengeCompleted.findOne({ + await this.models.NostrChallengeCompleted.findOne({ where: { challenge: challengeString, }, @@ -112,7 +110,7 @@ class NostrServiceProvider { return true; } return false; - } + }; return { createNostrChallenge, From 0da67c6104255abdd4b534fa5592b69233e77b39 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 13:28:00 +0100 Subject: [PATCH 092/176] missing errors import --- src/services/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/index.js b/src/services/index.js index 454dc5a..321fa54 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -1,4 +1,6 @@ +const models = require('../models'); const constants = require('../constants'); +const errors = require('../errors'); class ServicesProvider { constructor() {} @@ -12,7 +14,9 @@ class ServicesProvider { const NostrServiceProvider = require('../services/nostrService'); const nostrService = new NostrServiceProvider({ + models: models, constants: constants, + errors: errors, }).provide(); const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ From 544a134eb3a6d654c0c4affe9ca9c3c76c099f17 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:05:43 +0100 Subject: [PATCH 093/176] invite service --- src/services/index.js | 6 ++++- src/services/invitesService.js | 49 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index 321fa54..04d47a3 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -12,15 +12,19 @@ class ServicesProvider { - also remember to get CLi fixed and working */ + //done const NostrServiceProvider = require('../services/nostrService'); const nostrService = new NostrServiceProvider({ models: models, constants: constants, errors: errors, }).provide(); + const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ - nostrService, + models: models, + errors: errors, + nostrService: nostrService, }).provide(); const LoginServiceProvider = require('../services/loginService'); diff --git a/src/services/invitesService.js b/src/services/invitesService.js index 6b3fde0..df357e3 100644 --- a/src/services/invitesService.js +++ b/src/services/invitesService.js @@ -1,34 +1,33 @@ const uuid = require('uuid'); -const models = require('../models'); -const errors = require('../errors'); - class InvitesServiceProvider { - constructor({ nostrService }) { + constructor({ models, errors, nostrService }) { + this.models = models; + this.errors = errors; this.nostrService = nostrService; } provide() { - async function appInviteExists(inviteUuid) { - const invite = await models.AppInviteCreated.findOne({ + const appInviteExists = async (inviteUuid) => { + const invite = await this.models.AppInviteCreated.findOne({ where: { uuid: inviteUuid }, }); if (invite) { return true; } return false; - } + }; - async function getAppInvite(inviteUuid) { - const invite = await models.AppInviteCreated.findOne({ + const getAppInvite = async (inviteUuid) => { + const invite = await this.models.AppInviteCreated.findOne({ where: { uuid: inviteUuid }, }); return invite; - } + }; - async function isAppInviteSpent(appInviteUuid) { + const isAppInviteSpent = async (appInviteUuid) => { const signUpChallengeCompleted = - await models.SignUpChallengeCompleted.findOne({ + await this.models.SignUpChallengeCompleted.findOne({ where: { app_invite_uuid: appInviteUuid, }, @@ -38,28 +37,28 @@ class InvitesServiceProvider { return true; } return false; - } + }; - async function createAppInvite(inviterPubKey) { - return await models.AppInviteCreated.create({ + 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 errors.NotFoundError("Invite doesn't exist."); + throw new this.errors.NotFoundError("Invite doesn't exist."); } if (await isAppInviteSpent(appInviteUuid)) { - throw new errors.AlreadyUsedError('Invite has already been used.'); + throw new this.errors.AlreadyUsedError('Invite has already been used.'); } const nostrChallenge = await this.nostrService.createNostrChallenge(); - return await models.SignUpChallengeCreated.create({ + return await this.models.SignUpChallengeCreated.create({ uuid: uuid.v7(), nostr_challenge_uuid: nostrChallenge.uuid, app_invite_uuid: appInviteUuid, @@ -78,14 +77,14 @@ class InvitesServiceProvider { challenge ); - const signUpChallenge = await models.SignUpChallengeCreated.findOne({ + const signUpChallenge = await this.models.SignUpChallengeCreated.findOne({ where: { nostr_challenge_uuid: nostrChallenge.uuid, }, }); if (await this.nostrService.hasNostrChallengeBeenCompleted(challenge)) { - throw new errors.AlreadyUsedError( + throw new this.errors.AlreadyUsedError( 'This challenge has already been used.' ); } @@ -94,7 +93,7 @@ class InvitesServiceProvider { await this.nostrService.verifyNostrChallenge(signedEvent); const completedSignUpChallenge = - await models.SignUpChallengeCompleted.create({ + await this.models.SignUpChallengeCompleted.create({ uuid: uuid.v7(), nostr_challenge_completed_uuid: completedNostrChallenge.uuid, app_invite_uuid: signUpChallenge.app_invite_uuid, @@ -105,9 +104,9 @@ class InvitesServiceProvider { return completedSignUpChallenge; }; - async function isPublicKeySignedUp(publicKey) { + const isPublicKeySignedUp = async (publicKey) => { const signUpChallengeCompleted = - await models.SignUpChallengeCompleted.findOne({ + await this.models.SignUpChallengeCompleted.findOne({ where: { public_key: publicKey, }, @@ -118,7 +117,7 @@ class InvitesServiceProvider { } return false; - } + }; return { appInviteExists, From ba9b4d5ef300fdaa503001564fdf39930e50bc74 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:11:34 +0100 Subject: [PATCH 094/176] login service --- src/routes/apiRoutes.js | 2 -- src/services/index.js | 2 ++ src/services/loginService.js | 16 +++++++--------- src/services/nostrService.js | 4 +++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index 9ff1513..f97ae1a 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -129,8 +129,6 @@ class ApiRoutesProvider { completedLoginChallenge = await this.services.loginService.verifyLoginChallenge(signedEvent); } catch (error) { - console.log('helo5'); - console.log(error); if (error instanceof this.errors.ExpiredError) { return res.status(410).json({ success: false, diff --git a/src/services/index.js b/src/services/index.js index 04d47a3..eea745b 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -29,6 +29,8 @@ class ServicesProvider { const LoginServiceProvider = require('../services/loginService'); const loginService = new LoginServiceProvider({ + models, + errors, nostrService, invitesService, }).provide(); diff --git a/src/services/loginService.js b/src/services/loginService.js index db6b2cf..b4e74cd 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -1,11 +1,9 @@ const uuid = require('uuid'); -const models = require('../models'); - -const errors = require('../errors'); - class LoginServiceProvider { - constructor({ nostrService, invitesService }) { + constructor({ models, errors, nostrService, invitesService }) { + this.models = models; + this.errors = errors; this.nostrService = nostrService; this.invitesService = invitesService; } @@ -14,7 +12,7 @@ class LoginServiceProvider { const createLoginChallenge = async () => { const nostrChallenge = await this.nostrService.createNostrChallenge(); - return await models.LoginChallengeCreated.create({ + return await this.models.LoginChallengeCreated.create({ uuid: uuid.v7(), nostr_challenge_uuid: nostrChallenge.uuid, created_at: new Date().toISOString(), @@ -28,7 +26,7 @@ class LoginServiceProvider { const challenge = challengeTag[1]; if (await this.nostrService.hasNostrChallengeBeenCompleted(challenge)) { - throw new errors.AlreadyUsedError( + throw new this.errors.AlreadyUsedError( 'This challenge has already been used.' ); } @@ -42,13 +40,13 @@ class LoginServiceProvider { )) ) { console.log('helo4'); - throw new errors.ForbiddenError( + throw new this.errors.ForbiddenError( `Public key ${completedNostrChallenge.public_key} is not authorized.` ); } const completedLoginChallenge = - await models.LoginChallengeCompleted.create({ + await this.models.LoginChallengeCompleted.create({ uuid: uuid.v7(), nostr_challenge_completed_uuid: completedNostrChallenge.uuid, public_key: completedNostrChallenge.public_key, diff --git a/src/services/nostrService.js b/src/services/nostrService.js index f881c5c..7d87ea6 100644 --- a/src/services/nostrService.js +++ b/src/services/nostrService.js @@ -59,7 +59,9 @@ class NostrServiceProvider { const challenge = challengeTag[1]; if (!(await isNostrChallengeFresh(challenge))) { - throw this.errors.ExpiredError('Challenge expired, request new one.'); + throw new this.errors.ExpiredError( + 'Challenge expired, request new one.' + ); } if (await hasNostrChallengeBeenCompleted(challenge)) { From 0f4ccf98473180fa062c566cfab5651d5359cbac Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:29:58 +0100 Subject: [PATCH 095/176] wip sessionService --- src/services/sessionService.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index d489d60..e8e563a 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -5,12 +5,14 @@ const models = require('../models'); const constants = require('../constants'); class SessionServiceProvider { - constructor({ invitesService }) { + constructor({ models, constants, invitesService }) { + this.models = models; + this.constants = constants; this.invitesService = invitesService; } provide() { - async function createSession(sessionUuid) { + const createSession = async (sessionUuid) => { const currentTimestamp = new Date(); const expiryTimestamp = new Date(currentTimestamp.getTime()); expiryTimestamp.setSeconds( @@ -18,12 +20,12 @@ class SessionServiceProvider { constants.DEFAULT_SESSION_DURATION_SECONDS ); - return await models.SessionCreated.create({ + return await this.models.SessionCreated.create({ uuid: sessionUuid, created_at: currentTimestamp.toISOString(), expires_at: expiryTimestamp.toISOString(), }); - } + }; async function isSessionValid(sessionUuid) { const currentSession = await models.SessionCreated.findOne({ From 49c3e970cb2215397a1555c482900692a93551dc Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:30:30 +0100 Subject: [PATCH 096/176] wip sessionService --- src/services/sessionService.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index e8e563a..2438d05 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -27,8 +27,8 @@ class SessionServiceProvider { }); }; - async function isSessionValid(sessionUuid) { - const currentSession = await models.SessionCreated.findOne({ + const isSessionValid = async (sessionUuid) => { + const currentSession = await this.models.SessionCreated.findOne({ where: { uuid: sessionUuid, }, @@ -43,7 +43,7 @@ class SessionServiceProvider { } return true; - } + }; const relateSessionToPublicKey = async (sessionUuid, publicKey) => { if (!(await isSessionValid(sessionUuid))) { From e85fae2f996c93004fa975a45a9ec5ffc0fd41d6 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:31:04 +0100 Subject: [PATCH 097/176] wip sessionService --- src/services/sessionService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index 2438d05..96341ec 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -54,7 +54,7 @@ class SessionServiceProvider { throw Error('Public key is not signed up.'); } - return models.SessionRelatedToPublickey.create({ + return this.models.SessionRelatedToPublickey.create({ uuid: uuid.v7(), session_uuid: sessionUuid, public_key: publicKey, From 097bed525e64e92a770254f604604f0a75e61952 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:31:46 +0100 Subject: [PATCH 098/176] wip sessionService --- src/services/sessionService.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index 96341ec..ea0cc18 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -62,9 +62,9 @@ class SessionServiceProvider { }); }; - async function isSessionAuthorized(sessionUuid) { + const isSessionAuthorized = async (sessionUuid) => { const isSessionRelatedToPublicKey = - await models.SessionRelatedToPublickey.findOne({ + await this.models.SessionRelatedToPublickey.findOne({ where: { session_uuid: sessionUuid, }, @@ -75,7 +75,7 @@ class SessionServiceProvider { } return false; - } + }; async function getPublicKeyRelatedToSession(sessionUuid) { const sessionRelatedToPublickey = From 53219924ff35adb110d4e2d3d2984ce050246299 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:35:22 +0100 Subject: [PATCH 099/176] wip sessionService --- src/services/sessionService.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index ea0cc18..a032f1b 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -1,7 +1,5 @@ const uuid = require('uuid'); -const models = require('../models'); - const constants = require('../constants'); class SessionServiceProvider { @@ -77,9 +75,9 @@ class SessionServiceProvider { return false; }; - async function getPublicKeyRelatedToSession(sessionUuid) { + const getPublicKeyRelatedToSession = async (sessionUuid) => { const sessionRelatedToPublickey = - await models.SessionRelatedToPublickey.findOne({ + await this.models.SessionRelatedToPublickey.findOne({ where: { session_uuid: sessionUuid, }, @@ -90,7 +88,7 @@ class SessionServiceProvider { } return null; - } + }; return { createSession, From a2e44553d61fa2b3d7aa54d7f7cfbeebcc159aae Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:35:42 +0100 Subject: [PATCH 100/176] wip sessionService --- src/services/sessionService.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/services/sessionService.js b/src/services/sessionService.js index a032f1b..e731846 100644 --- a/src/services/sessionService.js +++ b/src/services/sessionService.js @@ -1,7 +1,5 @@ const uuid = require('uuid'); -const constants = require('../constants'); - class SessionServiceProvider { constructor({ models, constants, invitesService }) { this.models = models; @@ -15,7 +13,7 @@ class SessionServiceProvider { const expiryTimestamp = new Date(currentTimestamp.getTime()); expiryTimestamp.setSeconds( expiryTimestamp.getSeconds() + - constants.DEFAULT_SESSION_DURATION_SECONDS + this.constants.DEFAULT_SESSION_DURATION_SECONDS ); return await this.models.SessionCreated.create({ From 42fea5523368194f1566a90d1835a33ba85845f3 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 15:35:55 +0100 Subject: [PATCH 101/176] finished session service --- src/services/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/index.js b/src/services/index.js index eea745b..bf41899 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -37,6 +37,8 @@ class ServicesProvider { const SessionServiceProvider = require('../services/sessionService'); const sessionService = new SessionServiceProvider({ + models, + constants, invitesService, }).provide(); From ec725f8e4ea1297279f2d542099c3bde88ae82d1 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 16:03:10 +0100 Subject: [PATCH 102/176] profile service --- src/routes/apiRoutes.js | 4 +--- src/services/index.js | 2 +- src/services/loginService.js | 4 ---- src/services/profileService.js | 24 ++++++++++++++---------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index f97ae1a..b93b1f0 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -148,7 +148,6 @@ class ApiRoutesProvider { }); } if (error instanceof this.errors.ForbiddenError) { - console.log('helo?1'); return res.status(403).json({ success: false, message: 'This public key is not authorized.', @@ -160,8 +159,7 @@ class ApiRoutesProvider { message: 'Unexpected error.', }); } - console.log('helo?2'); - console.log(completedLoginChallenge); + await this.services.sessionService.relateSessionToPublicKey( sessionUuid, completedLoginChallenge.public_key diff --git a/src/services/index.js b/src/services/index.js index bf41899..f71ab58 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -43,7 +43,7 @@ class ServicesProvider { }).provide(); const ProfileServiceProvider = require('../services/profileService'); - const profileService = new ProfileServiceProvider().provide(); + const profileService = new ProfileServiceProvider({ models }).provide(); const OfferServiceProvider = require('../services/offerService'); const offerService = new OfferServiceProvider().provide(); diff --git a/src/services/loginService.js b/src/services/loginService.js index b4e74cd..73a0b58 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -39,7 +39,6 @@ class LoginServiceProvider { completedNostrChallenge.public_key )) ) { - console.log('helo4'); throw new this.errors.ForbiddenError( `Public key ${completedNostrChallenge.public_key} is not authorized.` ); @@ -53,9 +52,6 @@ class LoginServiceProvider { created_at: new Date().toISOString(), }); - console.log('helo3'); - console.log(completedLoginChallenge); - return completedLoginChallenge; }; diff --git a/src/services/profileService.js b/src/services/profileService.js index 34bcd6f..391d953 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -3,37 +3,41 @@ const uuid = require('uuid'); const models = require('../models'); class ProfileServiceProvider { + constructor({ models }) { + this.models = models; + } provide() { - async function setContactDetails(publicKey, encryptedContactDetails) { - return await models.ContactDetailsSet.create({ + 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(), }); - } + }; - async function setNym(publicKey, nym) { - return await models.NymSet.create({ + const setNym = async (publicKey, nym) => { + return await this.models.NymSet.create({ uuid: uuid.v7(), public_key: publicKey, nym: nym, created_at: new Date().toISOString(), }); - } + }; - async function areProfileDetailsComplete(publicKey) { - const isNymSet = await models.NymSet.findOne({ + const areProfileDetailsComplete = async (publicKey) => { + console.log(this.models); + const isNymSet = await this.models.NymSet.findOne({ where: { public_key: publicKey }, }); - const areContactDetailsSet = await models.ContactDetailsSet.findOne({ + const areContactDetailsSet = await this.models.ContactDetailsSet.findOne({ where: { public_key: publicKey, }, }); return isNymSet && areContactDetailsSet; - } + }; return { setContactDetails, setNym, areProfileDetailsComplete }; } From bf0775ba31680d87b81fc61935b589ad36e5d619 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 16:06:44 +0100 Subject: [PATCH 103/176] offer service --- src/services/index.js | 5 ++++- src/services/offerService.js | 38 +++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index f71ab58..65dc649 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -45,7 +45,10 @@ class ServicesProvider { const ProfileServiceProvider = require('../services/profileService'); const profileService = new ProfileServiceProvider({ models }).provide(); const OfferServiceProvider = require('../services/offerService'); - const offerService = new OfferServiceProvider().provide(); + const offerService = new OfferServiceProvider({ + models, + errors, + }).provide(); return { invitesService, diff --git a/src/services/offerService.js b/src/services/offerService.js index 25f7b6d..b2696aa 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -1,19 +1,19 @@ const uuid = require('uuid'); -const models = require('../models'); - -const errors = require('../errors'); - class OfferServiceProvider { + constructor({ models, errors }) { + this.models = models; + this.errors = errors; + } provide() { - async function createOffer(publicKey, offerDetails) { - const offerCreated = await models.OfferCreated.create({ + const createOffer = async (publicKey, offerDetails) => { + const offerCreated = await this.models.OfferCreated.create({ uuid: uuid.v7(), public_key: publicKey, created_at: new Date().toISOString(), }); - await models.OfferDetailsSet.create({ + await this.models.OfferDetailsSet.create({ uuid: uuid.v7(), offer_uuid: offerCreated.uuid, wants: offerDetails.wants, @@ -30,29 +30,31 @@ class OfferServiceProvider { are_big_notes_accepted: offerDetails.are_big_notes_accepted, created_at: new Date().toISOString(), }); - } + }; - async function deleteOffer(offerUuid) { + const deleteOffer = async (offerUuid) => { const offerExists = Boolean( - await models.OfferCreated.findOne({ where: { uuid: offerUuid } }) + await this.models.OfferCreated.findOne({ where: { uuid: offerUuid } }) ); const offerHasBeenDeleted = Boolean( - await models.OfferDeleted.findOne({ where: { offer_uuid: offerUuid } }) + await this.models.OfferDeleted.findOne({ + where: { offer_uuid: offerUuid }, + }) ); if (!offerExists || offerHasBeenDeleted) { - throw new errors.NotFoundError(`Could not find the offer.`); + throw new this.errors.NotFoundError(`Could not find the offer.`); } - return models.OfferDeleted.create({ + return this.models.OfferDeleted.create({ uuid: uuid.v7(), offer_uuid: offerUuid, created_at: new Date().toISOString(), }); - } + }; - async function getOffersByPublicKey(publicKey) { - const offers = await models.OfferCreated.findAll({ + const getOffersByPublicKey = async (publicKey) => { + const offers = await this.models.OfferCreated.findAll({ where: { public_key: publicKey, }, @@ -67,7 +69,7 @@ class OfferServiceProvider { const offersToReturn = []; if (offers) { for (const someOffer of offers) { - const offerDetails = await models.OfferDetailsSet.findOne({ + const offerDetails = await this.models.OfferDetailsSet.findOne({ where: { offer_uuid: someOffer.uuid, }, @@ -96,7 +98,7 @@ class OfferServiceProvider { } return offersToReturn; - } + }; return { createOffer, getOffersByPublicKey, deleteOffer }; } From ee590984b223bbd206fb3cbd65a5c2ea37a69041 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 16:06:55 +0100 Subject: [PATCH 104/176] remove comments --- src/services/index.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/services/index.js b/src/services/index.js index 65dc649..34a00e4 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -6,13 +6,6 @@ class ServicesProvider { constructor() {} provide() { - /* - Up next: - - start passing all service stuff from outside - - also remember to get CLi fixed and working - */ - - //done const NostrServiceProvider = require('../services/nostrService'); const nostrService = new NostrServiceProvider({ models: models, From b1ff3b8d759be47fb3216846edf24b3be610f21e Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 16:14:04 +0100 Subject: [PATCH 105/176] remove unused require --- src/services/profileService.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/profileService.js b/src/services/profileService.js index 391d953..aca4247 100644 --- a/src/services/profileService.js +++ b/src/services/profileService.js @@ -1,7 +1,5 @@ const uuid = require('uuid'); -const models = require('../models'); - class ProfileServiceProvider { constructor({ models }) { this.models = models; From b5c27c9b26b8086b42132630fc217df84c874ff2 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 7 Mar 2025 16:24:51 +0100 Subject: [PATCH 106/176] pass models from dependencies --- src/dependencies.js | 7 ++++++- src/services/index.js | 36 +++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index ce71d18..fa7afeb 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -4,9 +4,14 @@ function buildDependencies() { const dependencies = {}; const errors = require('./errors'); const constants = require('./constants'); + const models = require('./models'); const ServicesProvider = require('./services'); - const services = new ServicesProvider().provide(); + const services = new ServicesProvider({ + models, + constants, + errors, + }).provide(); dependencies.services = services; const MiddlewaresProvider = require('./middlewares'); diff --git a/src/services/index.js b/src/services/index.js index 34a00e4..d4406fb 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -1,46 +1,48 @@ -const models = require('../models'); -const constants = require('../constants'); -const errors = require('../errors'); - class ServicesProvider { - constructor() {} + constructor({ models, constants, errors }) { + this.models = models; + this.constants = constants; + this.errors = errors; + } provide() { const NostrServiceProvider = require('../services/nostrService'); const nostrService = new NostrServiceProvider({ - models: models, - constants: constants, - errors: errors, + models: this.models, + constants: this.constants, + errors: this.errors, }).provide(); const InvitesServiceProvider = require('../services/invitesService'); const invitesService = new InvitesServiceProvider({ - models: models, - errors: errors, + models: this.models, + errors: this.errors, nostrService: nostrService, }).provide(); const LoginServiceProvider = require('../services/loginService'); const loginService = new LoginServiceProvider({ - models, - errors, + models: this.models, + errors: this.errors, nostrService, invitesService, }).provide(); const SessionServiceProvider = require('../services/sessionService'); const sessionService = new SessionServiceProvider({ - models, - constants, + models: this.models, + constants: this.constants, invitesService, }).provide(); const ProfileServiceProvider = require('../services/profileService'); - const profileService = new ProfileServiceProvider({ models }).provide(); + const profileService = new ProfileServiceProvider({ + models: this.models, + }).provide(); const OfferServiceProvider = require('../services/offerService'); const offerService = new OfferServiceProvider({ - models, - errors, + models: this.models, + errors: this.errors, }).provide(); return { From be7ec9b43fc1f74d01fbb465e18b10ef03dc8cc1 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 8 Mar 2025 00:25:56 +0100 Subject: [PATCH 107/176] mass refactor of models --- src/dependencies.js | 4 +- src/models/AppInviteCreated.js | 57 ++++++---- src/models/ContactDetailsSet.js | 65 ++++++----- src/models/LoginChallengeCompleted.js | 65 ++++++----- src/models/LoginChallengeCreated.js | 57 ++++++---- src/models/NostrChallengeCompleted.js | 73 ++++++------ src/models/NostrChallengeCreated.js | 65 ++++++----- src/models/NymSet.js | 65 ++++++----- src/models/OfferCreated.js | 57 ++++++---- src/models/OfferDeleted.js | 57 ++++++---- src/models/OfferDetailsSet.js | 145 +++++++++++++----------- src/models/SessionCreated.js | 57 ++++++---- src/models/SessionRelatedToPublickey.js | 64 ++++++----- src/models/SignUpChallengeCompleted.js | 73 ++++++------ src/models/SignUpChallengeCreated.js | 66 ++++++----- src/models/index.js | 122 +++++++++++++++----- 16 files changed, 627 insertions(+), 465 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index fa7afeb..8b4eef3 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -4,7 +4,9 @@ function buildDependencies() { const dependencies = {}; const errors = require('./errors'); const constants = require('./constants'); - const models = require('./models'); + + const ModelsProvider = require('./models'); + const models = new ModelsProvider().provide(); const ServicesProvider = require('./services'); const services = new ServicesProvider({ diff --git a/src/models/AppInviteCreated.js b/src/models/AppInviteCreated.js index 49967b3..4062fb5 100644 --- a/src/models/AppInviteCreated.js +++ b/src/models/AppInviteCreated.js @@ -1,27 +1,34 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class AppInviteCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = AppInviteCreated; + 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; + } +} + +module.exports = AppInviteCreatedProvider; diff --git a/src/models/ContactDetailsSet.js b/src/models/ContactDetailsSet.js index 8722e89..4a1a6a1 100644 --- a/src/models/ContactDetailsSet.js +++ b/src/models/ContactDetailsSet.js @@ -1,31 +1,38 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class ContactDetailsSetProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = ContactDetailsSet; + 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; + } +} + +module.exports = ContactDetailsSetProvider; diff --git a/src/models/LoginChallengeCompleted.js b/src/models/LoginChallengeCompleted.js index b4774bb..bce56e8 100644 --- a/src/models/LoginChallengeCompleted.js +++ b/src/models/LoginChallengeCompleted.js @@ -1,31 +1,38 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class LoginChallengeCompletedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = LoginChallengeCompleted; + 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; + } +} + +module.exports = LoginChallengeCompletedProvider; diff --git a/src/models/LoginChallengeCreated.js b/src/models/LoginChallengeCreated.js index c1c1792..3d29c16 100644 --- a/src/models/LoginChallengeCreated.js +++ b/src/models/LoginChallengeCreated.js @@ -1,27 +1,34 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class LoginChallengeCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = LoginChallengeCreated; + 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; + } +} + +module.exports = LoginChallengeCreatedProvider; diff --git a/src/models/NostrChallengeCompleted.js b/src/models/NostrChallengeCompleted.js index a656edd..b57196b 100644 --- a/src/models/NostrChallengeCompleted.js +++ b/src/models/NostrChallengeCompleted.js @@ -1,35 +1,42 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class NostrChallengeCompletedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = NostrChallengeCompleted; + 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, + }, + 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; + } +} + +module.exports = NostrChallengeCompletedProvider; diff --git a/src/models/NostrChallengeCreated.js b/src/models/NostrChallengeCreated.js index 97a4c64..f0f0e91 100644 --- a/src/models/NostrChallengeCreated.js +++ b/src/models/NostrChallengeCreated.js @@ -1,31 +1,38 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class NostrChallengeCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = NostrChallengeCreated; + 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, + }, + expires_at: { + type: this.DataTypes.DATE, + allowNull: false, + }, + created_at: { + type: this.DataTypes.DATE, + allowNull: false, + }, + }, + { + tableName: 'nostr_challenge_created', + } + ); + return NostrChallengeCreated; + } +} + +module.exports = NostrChallengeCreatedProvider; diff --git a/src/models/NymSet.js b/src/models/NymSet.js index d59a310..4a9f26e 100644 --- a/src/models/NymSet.js +++ b/src/models/NymSet.js @@ -1,31 +1,38 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class NymSetProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = NymSet; + 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; + } +} + +module.exports = NymSetProvider; diff --git a/src/models/OfferCreated.js b/src/models/OfferCreated.js index fd67249..13eeb54 100644 --- a/src/models/OfferCreated.js +++ b/src/models/OfferCreated.js @@ -1,27 +1,34 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class OfferCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = OfferCreated; + 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; + } +} + +module.exports = OfferCreatedProvider; diff --git a/src/models/OfferDeleted.js b/src/models/OfferDeleted.js index aa1b71a..6324583 100644 --- a/src/models/OfferDeleted.js +++ b/src/models/OfferDeleted.js @@ -1,27 +1,34 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -const OfferDeleted = sequelize.define( - 'OfferDeleted', - { - uuid: { - type: DataTypes.UUID, - allowNull: false, - unique: true, - primaryKey: true, - }, - offer_uuid: { - type: DataTypes.STRING, - allowNull: false, - }, - created_at: { - type: DataTypes.DATE, - allowNull: false, - }, - }, - { - tableName: 'offer_deleted', +class OfferDeletedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = OfferDeleted; + provide() { + const OfferDeleted = this.sequelize.define( + 'OfferDeleted', + { + uuid: { + type: this.DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + offer_uuid: { + type: this.DataTypes.STRING, + 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 20ac456..766f148 100644 --- a/src/models/OfferDetailsSet.js +++ b/src/models/OfferDetailsSet.js @@ -1,71 +1,78 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class OfferDetailsSetProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = OfferDetailsSet; + provide() { + const OfferDetailsSet = this.sequelize.define( + 'OfferDetailsSet', + { + uuid: { + type: this.DataTypes.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + offer_uuid: { + type: this.DataTypes.STRING, + 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; + } +} + +module.exports = OfferDetailsSetProvider; diff --git a/src/models/SessionCreated.js b/src/models/SessionCreated.js index 8d0de50..a97c1fb 100644 --- a/src/models/SessionCreated.js +++ b/src/models/SessionCreated.js @@ -1,27 +1,34 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class SessionCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = SessionCreated; + 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; + } +} + +module.exports = SessionCreatedProvider; diff --git a/src/models/SessionRelatedToPublickey.js b/src/models/SessionRelatedToPublickey.js index 4802267..321da32 100644 --- a/src/models/SessionRelatedToPublickey.js +++ b/src/models/SessionRelatedToPublickey.js @@ -1,31 +1,37 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class SessionRelatedToPublickeyProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = SessionRelatedToPublickey; + 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; + } +} +module.exports = SessionRelatedToPublickeyProvider; diff --git a/src/models/SignUpChallengeCompleted.js b/src/models/SignUpChallengeCompleted.js index 2960421..afd65f2 100644 --- a/src/models/SignUpChallengeCompleted.js +++ b/src/models/SignUpChallengeCompleted.js @@ -1,35 +1,42 @@ -const { DataTypes } = require('sequelize'); -const sequelize = require('../database'); - -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', +class SignUpChallengeCompletedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = SignUpChallengeCompleted; + 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; + } +} + +module.exports = SignUpChallengeCompletedProvider; diff --git a/src/models/SignUpChallengeCreated.js b/src/models/SignUpChallengeCreated.js index 1d3e22e..3da9ec4 100644 --- a/src/models/SignUpChallengeCreated.js +++ b/src/models/SignUpChallengeCreated.js @@ -1,31 +1,39 @@ -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', +class SignUpChallengeCreatedProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; } -); -module.exports = SignUpChallengeCreated; + 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; diff --git a/src/models/index.js b/src/models/index.js index ed7ed30..1d3da51 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,31 +1,93 @@ -const AppInviteCreated = require('./AppInviteCreated'); -const ContactDetailsSet = require('./ContactDetailsSet'); -const LoginChallengeCompleted = require('./LoginChallengeCompleted'); -const LoginChallengeCreated = require('./LoginChallengeCreated'); -const NostrChallengeCompleted = require('./NostrChallengeCompleted'); -const NostrChallengeCreated = require('./NostrChallengeCreated'); -const NymSet = require('./NymSet'); -const OfferCreated = require('./OfferCreated'); -const OfferDeleted = require('./OfferDeleted'); -const OfferDetailsSet = require('./OfferDetailsSet'); -const SessionCreated = require('./SessionCreated'); -const SessionRelatedToPublickey = require('./SessionRelatedToPublickey'); -const SignUpChallengeCompleted = require('./SignUpChallengeCompleted'); -const SignUpChallengeCreated = require('./SignUpChallengeCreated'); +const sequelize = require('../database'); +const { DataTypes } = require('sequelize'); -module.exports = { - SignUpChallengeCreated, - SignUpChallengeCompleted, - SessionRelatedToPublickey, - SessionCreated, - OfferDeleted, - OfferDetailsSet, - OfferCreated, - NymSet, - NostrChallengeCreated, - NostrChallengeCompleted, - LoginChallengeCompleted, - LoginChallengeCreated, - ContactDetailsSet, - AppInviteCreated, -}; +const AppInviteCreatedProvider = require('./AppInviteCreated'); +const AppInviteCreated = new AppInviteCreatedProvider({ + sequelize, + DataTypes, +}).provide(); +const ContactDetailsSetProvider = require('./ContactDetailsSet'); +const ContactDetailsSet = new ContactDetailsSetProvider({ + sequelize, + DataTypes, +}).provide(); +const LoginChallengeCompletedProvider = require('./LoginChallengeCompleted'); +const LoginChallengeCompleted = new LoginChallengeCompletedProvider({ + sequelize, + DataTypes, +}).provide(); +const LoginChallengeCreatedProvider = require('./LoginChallengeCreated'); +const LoginChallengeCreated = new LoginChallengeCreatedProvider({ + sequelize, + DataTypes, +}).provide(); +const NostrChallengeCompletedProvider = require('./NostrChallengeCompleted'); +const NostrChallengeCompleted = new NostrChallengeCompletedProvider({ + sequelize, + DataTypes, +}).provide(); +const NostrChallengeCreatedProvider = require('./NostrChallengeCreated'); +const NostrChallengeCreated = new NostrChallengeCreatedProvider({ + sequelize, + DataTypes, +}).provide(); +const NymSetProvider = require('./NymSet'); +const NymSet = new NymSetProvider({ sequelize, DataTypes }).provide(); +const OfferCreatedProvider = require('./OfferCreated'); +const OfferCreated = new OfferCreatedProvider({ + sequelize, + DataTypes, +}).provide(); +const OfferDeletedProvider = require('./OfferDeleted'); +const OfferDeleted = new OfferDeletedProvider({ + sequelize, + DataTypes, +}).provide(); +const OfferDetailsSetProvider = require('./OfferDetailsSet'); +const OfferDetailsSet = new OfferDetailsSetProvider({ + sequelize, + DataTypes, +}).provide(); +const SessionCreatedProvider = require('./SessionCreated'); +const SessionCreated = new SessionCreatedProvider({ + sequelize, + DataTypes, +}).provide(); +const SessionRelatedToPublickeyProvider = require('./SessionRelatedToPublickey'); +const SessionRelatedToPublickey = new SessionRelatedToPublickeyProvider({ + sequelize, + DataTypes, +}).provide(); +const SignUpChallengeCompletedProvider = require('./SignUpChallengeCompleted'); +const SignUpChallengeCompleted = new SignUpChallengeCompletedProvider({ + sequelize, + DataTypes, +}).provide(); +const SignUpChallengeCreatedProvider = require('./SignUpChallengeCreated'); +const SignUpChallengeCreated = new SignUpChallengeCreatedProvider({ + sequelize, + DataTypes, +}).provide(); + +class ModelsProvider { + provide() { + return { + SignUpChallengeCreated, + SignUpChallengeCompleted, + SessionRelatedToPublickey, + SessionCreated, + OfferDeleted, + OfferDetailsSet, + OfferCreated, + NymSet, + NostrChallengeCreated, + NostrChallengeCompleted, + LoginChallengeCompleted, + LoginChallengeCreated, + ContactDetailsSet, + AppInviteCreated, + }; + } +} + +module.exports = ModelsProvider; From b4d698a989d1f0125b20214adcc8cc79a9e1e22c Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 8 Mar 2025 00:26:14 +0100 Subject: [PATCH 108/176] move declarations inside provider --- src/models/index.js | 136 ++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/models/index.js b/src/models/index.js index 1d3da51..16e3dec 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,76 +1,76 @@ const sequelize = require('../database'); const { DataTypes } = require('sequelize'); -const AppInviteCreatedProvider = require('./AppInviteCreated'); -const AppInviteCreated = new AppInviteCreatedProvider({ - sequelize, - DataTypes, -}).provide(); -const ContactDetailsSetProvider = require('./ContactDetailsSet'); -const ContactDetailsSet = new ContactDetailsSetProvider({ - sequelize, - DataTypes, -}).provide(); -const LoginChallengeCompletedProvider = require('./LoginChallengeCompleted'); -const LoginChallengeCompleted = new LoginChallengeCompletedProvider({ - sequelize, - DataTypes, -}).provide(); -const LoginChallengeCreatedProvider = require('./LoginChallengeCreated'); -const LoginChallengeCreated = new LoginChallengeCreatedProvider({ - sequelize, - DataTypes, -}).provide(); -const NostrChallengeCompletedProvider = require('./NostrChallengeCompleted'); -const NostrChallengeCompleted = new NostrChallengeCompletedProvider({ - sequelize, - DataTypes, -}).provide(); -const NostrChallengeCreatedProvider = require('./NostrChallengeCreated'); -const NostrChallengeCreated = new NostrChallengeCreatedProvider({ - sequelize, - DataTypes, -}).provide(); -const NymSetProvider = require('./NymSet'); -const NymSet = new NymSetProvider({ sequelize, DataTypes }).provide(); -const OfferCreatedProvider = require('./OfferCreated'); -const OfferCreated = new OfferCreatedProvider({ - sequelize, - DataTypes, -}).provide(); -const OfferDeletedProvider = require('./OfferDeleted'); -const OfferDeleted = new OfferDeletedProvider({ - sequelize, - DataTypes, -}).provide(); -const OfferDetailsSetProvider = require('./OfferDetailsSet'); -const OfferDetailsSet = new OfferDetailsSetProvider({ - sequelize, - DataTypes, -}).provide(); -const SessionCreatedProvider = require('./SessionCreated'); -const SessionCreated = new SessionCreatedProvider({ - sequelize, - DataTypes, -}).provide(); -const SessionRelatedToPublickeyProvider = require('./SessionRelatedToPublickey'); -const SessionRelatedToPublickey = new SessionRelatedToPublickeyProvider({ - sequelize, - DataTypes, -}).provide(); -const SignUpChallengeCompletedProvider = require('./SignUpChallengeCompleted'); -const SignUpChallengeCompleted = new SignUpChallengeCompletedProvider({ - sequelize, - DataTypes, -}).provide(); -const SignUpChallengeCreatedProvider = require('./SignUpChallengeCreated'); -const SignUpChallengeCreated = new SignUpChallengeCreatedProvider({ - sequelize, - DataTypes, -}).provide(); - class ModelsProvider { provide() { + const AppInviteCreatedProvider = require('./AppInviteCreated'); + const AppInviteCreated = new AppInviteCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + const ContactDetailsSetProvider = require('./ContactDetailsSet'); + const ContactDetailsSet = new ContactDetailsSetProvider({ + sequelize, + DataTypes, + }).provide(); + const LoginChallengeCompletedProvider = require('./LoginChallengeCompleted'); + const LoginChallengeCompleted = new LoginChallengeCompletedProvider({ + sequelize, + DataTypes, + }).provide(); + const LoginChallengeCreatedProvider = require('./LoginChallengeCreated'); + const LoginChallengeCreated = new LoginChallengeCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + const NostrChallengeCompletedProvider = require('./NostrChallengeCompleted'); + const NostrChallengeCompleted = new NostrChallengeCompletedProvider({ + sequelize, + DataTypes, + }).provide(); + const NostrChallengeCreatedProvider = require('./NostrChallengeCreated'); + const NostrChallengeCreated = new NostrChallengeCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + const NymSetProvider = require('./NymSet'); + const NymSet = new NymSetProvider({ sequelize, DataTypes }).provide(); + const OfferCreatedProvider = require('./OfferCreated'); + const OfferCreated = new OfferCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + const OfferDeletedProvider = require('./OfferDeleted'); + const OfferDeleted = new OfferDeletedProvider({ + sequelize, + DataTypes, + }).provide(); + const OfferDetailsSetProvider = require('./OfferDetailsSet'); + const OfferDetailsSet = new OfferDetailsSetProvider({ + sequelize, + DataTypes, + }).provide(); + const SessionCreatedProvider = require('./SessionCreated'); + const SessionCreated = new SessionCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + const SessionRelatedToPublickeyProvider = require('./SessionRelatedToPublickey'); + const SessionRelatedToPublickey = new SessionRelatedToPublickeyProvider({ + sequelize, + DataTypes, + }).provide(); + const SignUpChallengeCompletedProvider = require('./SignUpChallengeCompleted'); + const SignUpChallengeCompleted = new SignUpChallengeCompletedProvider({ + sequelize, + DataTypes, + }).provide(); + const SignUpChallengeCreatedProvider = require('./SignUpChallengeCreated'); + const SignUpChallengeCreated = new SignUpChallengeCreatedProvider({ + sequelize, + DataTypes, + }).provide(); + return { SignUpChallengeCreated, SignUpChallengeCompleted, From bf478bbbe912fa1c681b91163184a7f6c23a1f4c Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 8 Mar 2025 00:29:52 +0100 Subject: [PATCH 109/176] pull up --- src/dependencies.js | 4 ++- src/models/index.js | 64 ++++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 8b4eef3..6c68534 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -5,8 +5,10 @@ function buildDependencies() { const errors = require('./errors'); const constants = require('./constants'); + const sequelize = require('./database'); + const { DataTypes } = require('sequelize'); const ModelsProvider = require('./models'); - const models = new ModelsProvider().provide(); + const models = new ModelsProvider({ sequelize, DataTypes }).provide(); const ServicesProvider = require('./services'); const services = new ServicesProvider({ diff --git a/src/models/index.js b/src/models/index.js index 16e3dec..02b4028 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,74 +1,78 @@ -const sequelize = require('../database'); -const { DataTypes } = require('sequelize'); - class ModelsProvider { + constructor({ sequelize, DataTypes }) { + this.sequelize = sequelize; + this.DataTypes = DataTypes; + } provide() { const AppInviteCreatedProvider = require('./AppInviteCreated'); const AppInviteCreated = new AppInviteCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const ContactDetailsSetProvider = require('./ContactDetailsSet'); const ContactDetailsSet = new ContactDetailsSetProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const LoginChallengeCompletedProvider = require('./LoginChallengeCompleted'); const LoginChallengeCompleted = new LoginChallengeCompletedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const LoginChallengeCreatedProvider = require('./LoginChallengeCreated'); const LoginChallengeCreated = new LoginChallengeCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const NostrChallengeCompletedProvider = require('./NostrChallengeCompleted'); const NostrChallengeCompleted = new NostrChallengeCompletedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const NostrChallengeCreatedProvider = require('./NostrChallengeCreated'); const NostrChallengeCreated = new NostrChallengeCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const NymSetProvider = require('./NymSet'); - const NymSet = new NymSetProvider({ sequelize, DataTypes }).provide(); + const NymSet = new NymSetProvider({ + sequelize: this.sequelize, + DataTypes: this.DataTypes, + }).provide(); const OfferCreatedProvider = require('./OfferCreated'); const OfferCreated = new OfferCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const OfferDeletedProvider = require('./OfferDeleted'); const OfferDeleted = new OfferDeletedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const OfferDetailsSetProvider = require('./OfferDetailsSet'); const OfferDetailsSet = new OfferDetailsSetProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const SessionCreatedProvider = require('./SessionCreated'); const SessionCreated = new SessionCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const SessionRelatedToPublickeyProvider = require('./SessionRelatedToPublickey'); const SessionRelatedToPublickey = new SessionRelatedToPublickeyProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const SignUpChallengeCompletedProvider = require('./SignUpChallengeCompleted'); const SignUpChallengeCompleted = new SignUpChallengeCompletedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); const SignUpChallengeCreatedProvider = require('./SignUpChallengeCreated'); const SignUpChallengeCreated = new SignUpChallengeCreatedProvider({ - sequelize, - DataTypes, + sequelize: this.sequelize, + DataTypes: this.DataTypes, }).provide(); return { From 2d124a1ef4fd6c549bbae40541037f3c56b423d5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 8 Mar 2025 02:47:37 +0100 Subject: [PATCH 110/176] associations and db stuff --- src/associations.js | 19 +++++++++++++++++++ src/database.js | 13 ++----------- src/dependencies.js | 14 +++++++++++++- 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/associations.js diff --git a/src/associations.js b/src/associations.js new file mode 100644 index 0000000..08bf67a --- /dev/null +++ b/src/associations.js @@ -0,0 +1,19 @@ +class AssociationsDefiner { + constructor({ models, DataTypes }) { + this.models = models; + this.DataTypes = DataTypes; + } + + define() { + this.models.OfferCreated.hasOne(this.models.OfferDeleted); + 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.js b/src/database.js index 133328f..ed4ef6c 100644 --- a/src/database.js +++ b/src/database.js @@ -10,11 +10,11 @@ const sequelize = new Sequelize({ database: process.env.POSTGRES_DB, username: process.env.POSTGRES_USER, password: process.env.POSTGRES_PASSWORD, - logging: (msg) => { + /* logging: (msg) => { if (msg && (msg.includes('ERROR') || msg.includes('error'))) { console.error(msg); } - }, + }, */ define: { timestamps: false, freezeTableName: true, @@ -23,13 +23,4 @@ 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/dependencies.js b/src/dependencies.js index 6c68534..b85ad2e 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -1,6 +1,6 @@ const express = require('express'); -function buildDependencies() { +async function buildDependencies() { const dependencies = {}; const errors = require('./errors'); const constants = require('./constants'); @@ -10,6 +10,18 @@ function buildDependencies() { const ModelsProvider = require('./models'); const models = new ModelsProvider({ sequelize, DataTypes }).provide(); + const AssociationsDefiner = require('./associations'); + new AssociationsDefiner({ models, DataTypes }).define(); + + sequelize + .sync({ alter: true }) + .then(() => { + console.log('Database synced'); + }) + .catch((err) => { + console.error('Error syncing the database:', err); + }); + const ServicesProvider = require('./services'); const services = new ServicesProvider({ models, From 9660e263d1c032fe6d93b517cba4216f289550c2 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 8 Mar 2025 02:51:11 +0100 Subject: [PATCH 111/176] removed unwanted async --- src/dependencies.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dependencies.js b/src/dependencies.js index b85ad2e..606a0fe 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -1,6 +1,6 @@ const express = require('express'); -async function buildDependencies() { +function buildDependencies() { const dependencies = {}; const errors = require('./errors'); const constants = require('./constants'); From e718626aadf5516ffdc51b1a9b5004f8e4b4adbf Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 16:48:37 +0100 Subject: [PATCH 112/176] now it works --- src/associations.js | 4 +++- src/models/OfferDeleted.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/associations.js b/src/associations.js index 08bf67a..b43246a 100644 --- a/src/associations.js +++ b/src/associations.js @@ -5,7 +5,9 @@ class AssociationsDefiner { } define() { - this.models.OfferCreated.hasOne(this.models.OfferDeleted); + this.models.OfferCreated.hasOne(this.models.OfferDeleted, { + foreignKey: 'offer_uuid', + }); this.models.OfferDeleted.belongsTo(this.models.OfferCreated, { foreignKey: { name: 'offer_uuid', diff --git a/src/models/OfferDeleted.js b/src/models/OfferDeleted.js index 6324583..019c61f 100644 --- a/src/models/OfferDeleted.js +++ b/src/models/OfferDeleted.js @@ -15,7 +15,7 @@ class OfferDeletedProvider { primaryKey: true, }, offer_uuid: { - type: this.DataTypes.STRING, + type: this.DataTypes.UUID, allowNull: false, }, created_at: { From bb68c0585ad2fce24a76a55577d6f2591a38f751 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 16:49:20 +0100 Subject: [PATCH 113/176] no more sync --- src/dependencies.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index 606a0fe..f9d9aa8 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -13,7 +13,7 @@ function buildDependencies() { const AssociationsDefiner = require('./associations'); new AssociationsDefiner({ models, DataTypes }).define(); - sequelize + /* sequelize .sync({ alter: true }) .then(() => { console.log('Database synced'); @@ -21,7 +21,7 @@ function buildDependencies() { .catch((err) => { console.error('Error syncing the database:', err); }); - + */ const ServicesProvider = require('./services'); const services = new ServicesProvider({ models, From 12a0c7563e54c585b78297d3363ef788ec78985a Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 16:49:51 +0100 Subject: [PATCH 114/176] add sequelize-cli --- package-lock.json | 832 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 829 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1c6f215..2bea970 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,8 @@ "globals": "^15.15.0", "playwright": "^1.50.1", "prettier": "^3.5.1", - "prettier-plugin-ejs": "^1.0.3" + "prettier-plugin-ejs": "^1.0.3", + "sequelize-cli": "^6.6.2" } }, "node_modules/@eslint-community/eslint-utils": { @@ -193,6 +194,102 @@ "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/@noble/ciphers": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", @@ -290,6 +387,22 @@ "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", @@ -573,6 +686,15 @@ "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", @@ -615,6 +737,12 @@ "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", @@ -774,6 +902,33 @@ "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/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -812,6 +967,16 @@ "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", @@ -883,6 +1048,19 @@ "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", @@ -989,6 +1167,63 @@ "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", @@ -1012,7 +1247,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true + "devOptional": true }, "node_modules/encodeurl": { "version": "2.0.0", @@ -1093,6 +1328,67 @@ "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", @@ -1275,6 +1571,21 @@ "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", @@ -1337,6 +1648,16 @@ "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/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1390,6 +1711,15 @@ "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", @@ -1512,6 +1842,34 @@ "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", @@ -1533,6 +1891,21 @@ "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", @@ -1591,6 +1964,15 @@ "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", @@ -1689,7 +2071,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==", - "optional": true + "devOptional": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -1960,6 +2342,21 @@ "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", @@ -1972,7 +2369,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==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -2002,11 +2399,32 @@ "node": ">=8" } }, + "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/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", @@ -2024,6 +2442,113 @@ "node": ">=10" } }, + "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", @@ -2056,6 +2581,18 @@ "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", @@ -2112,6 +2649,15 @@ "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", @@ -2155,6 +2701,25 @@ "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", @@ -2377,6 +2942,12 @@ "node": ">= 0.6" } }, + "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", @@ -2567,6 +3138,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -2610,6 +3187,43 @@ "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", @@ -2849,6 +3463,12 @@ "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", @@ -2960,6 +3580,35 @@ "node": ">= 6" } }, + "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/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-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3160,6 +3809,28 @@ } } }, + "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", @@ -3488,7 +4159,22 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, + "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, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3509,6 +4195,19 @@ "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", @@ -3528,6 +4227,18 @@ "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", @@ -3604,6 +4315,19 @@ "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", @@ -3634,6 +4358,12 @@ "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", @@ -3668,6 +4398,18 @@ "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", @@ -3691,6 +4433,15 @@ "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", @@ -3787,6 +4538,41 @@ "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", @@ -3800,11 +4586,47 @@ "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 05c2f32..26f47dc 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "globals": "^15.15.0", "playwright": "^1.50.1", "prettier": "^3.5.1", - "prettier-plugin-ejs": "^1.0.3" + "prettier-plugin-ejs": "^1.0.3", + "sequelize-cli": "^6.6.2" } } From 55a57444f8bb264488de78558db71681ebf0f66b Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 16:51:23 +0100 Subject: [PATCH 115/176] move database into folder --- src/{ => database}/database.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ => database}/database.js (100%) diff --git a/src/database.js b/src/database/database.js similarity index 100% rename from src/database.js rename to src/database/database.js From 8e86f7297582a5802dfb06075ecc15f536dfb1fc Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 17:09:40 +0100 Subject: [PATCH 116/176] start --- .sequelizerc | 6 ++++ src/database/config.js | 20 +++++++++++++ .../migrations/20250308000000-create-table.js | 30 +++++++++++++++++++ src/dependencies.js | 2 +- 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 .sequelizerc create mode 100644 src/database/config.js create mode 100644 src/database/migrations/20250308000000-create-table.js diff --git a/.sequelizerc b/.sequelizerc new file mode 100644 index 0000000..c49d00f --- /dev/null +++ b/.sequelizerc @@ -0,0 +1,6 @@ +const path = require('path'); + +module.exports = { + config: path.resolve('src', 'database', 'config.js'), + 'migrations-path': path.resolve('src', 'database', 'migrations'), +}; diff --git a/src/database/config.js b/src/database/config.js new file mode 100644 index 0000000..a13f133 --- /dev/null +++ b/src/database/config.js @@ -0,0 +1,20 @@ +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, + define: { + timestamps: false, + freezeTableName: true, + underscored: true, + quoteIdentifiers: false, + }, + }, +}; diff --git a/src/database/migrations/20250308000000-create-table.js b/src/database/migrations/20250308000000-create-table.js new file mode 100644 index 0000000..bb77410 --- /dev/null +++ b/src/database/migrations/20250308000000-create-table.js @@ -0,0 +1,30 @@ +'use strict'; +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('Users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + firstName: { + type: Sequelize.STRING, + }, + lastName: { + type: Sequelize.STRING, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('Users'); + }, +}; diff --git a/src/dependencies.js b/src/dependencies.js index f9d9aa8..a93620d 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -5,7 +5,7 @@ function buildDependencies() { const errors = require('./errors'); const constants = require('./constants'); - const sequelize = require('./database'); + const sequelize = require('./database/database'); const { DataTypes } = require('sequelize'); const ModelsProvider = require('./models'); const models = new ModelsProvider({ sequelize, DataTypes }).provide(); From f5ced9888e5032451adc88169f7cacac2d1455df Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 9 Mar 2025 17:13:26 +0100 Subject: [PATCH 117/176] first schema --- .../migrations/20250308000000-create-table.js | 30 ------------------- .../migrations/20250308000000-first-schema.js | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 src/database/migrations/20250308000000-create-table.js create mode 100644 src/database/migrations/20250308000000-first-schema.js diff --git a/src/database/migrations/20250308000000-create-table.js b/src/database/migrations/20250308000000-create-table.js deleted file mode 100644 index bb77410..0000000 --- a/src/database/migrations/20250308000000-create-table.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('Users', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: Sequelize.INTEGER, - }, - firstName: { - type: Sequelize.STRING, - }, - lastName: { - type: Sequelize.STRING, - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, - }); - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('Users'); - }, -}; diff --git a/src/database/migrations/20250308000000-first-schema.js b/src/database/migrations/20250308000000-first-schema.js new file mode 100644 index 0000000..3326bde --- /dev/null +++ b/src/database/migrations/20250308000000-first-schema.js @@ -0,0 +1,30 @@ +'use strict'; +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable( + 'AppInviteCreated', + { + 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, + }, + }, + { + tableName: 'app_invite_created', + } + ); + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('Users'); + }, +}; From 9d4967a41d65ff07012d78409009b208f97ff6ff Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 12:47:09 +0100 Subject: [PATCH 118/176] first couple models working, now let's add more --- .../migrations/20250308000000-first-schema.js | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/database/migrations/20250308000000-first-schema.js b/src/database/migrations/20250308000000-first-schema.js index 3326bde..a368e2f 100644 --- a/src/database/migrations/20250308000000-first-schema.js +++ b/src/database/migrations/20250308000000-first-schema.js @@ -1,28 +1,54 @@ 'use strict'; module.exports = { up: (queryInterface, Sequelize) => { - return queryInterface.createTable( - 'AppInviteCreated', - { - 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, - }, - }, - { - tableName: 'app_invite_created', - } - ); + 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 } + ), + ]); + }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Users'); From afc8a6d04d9fda0e66ff00c19125b57b772aba86 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:02:33 +0100 Subject: [PATCH 119/176] nostr challenges --- .../migrations/20250308000000-first-schema.js | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema.js b/src/database/migrations/20250308000000-first-schema.js index a368e2f..8e7bfe9 100644 --- a/src/database/migrations/20250308000000-first-schema.js +++ b/src/database/migrations/20250308000000-first-schema.js @@ -47,6 +47,111 @@ module.exports = { }, { 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( + '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_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( + 'nostr_challenge_completed', + { + uuid: { + type: Sequelize.UUID, + allowNull: false, + unique: true, + primaryKey: true, + }, + challenge: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + references: { + model: { + tableName: 'nostr_challenge_created', + }, + key: 'challenge', + }, + }, + signed_event: { + type: Sequelize.JSONB, + allowNull: false, + }, + public_key: { + type: Sequelize.STRING, + allowNull: false, + }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + }, + }, + { transaction: t } + ), ]); }); }, From 3b2edb4ca99a178fa1f7f493a47d3c5ca8bf6561 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:05:33 +0100 Subject: [PATCH 120/176] relationship --- src/associations.js | 15 +++++++++++++++ src/models/NostrChallengeCompleted.js | 1 + src/models/NostrChallengeCreated.js | 1 + 3 files changed, 17 insertions(+) diff --git a/src/associations.js b/src/associations.js index b43246a..45747be 100644 --- a/src/associations.js +++ b/src/associations.js @@ -5,6 +5,21 @@ class AssociationsDefiner { } 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', }); diff --git a/src/models/NostrChallengeCompleted.js b/src/models/NostrChallengeCompleted.js index b57196b..e569097 100644 --- a/src/models/NostrChallengeCompleted.js +++ b/src/models/NostrChallengeCompleted.js @@ -17,6 +17,7 @@ class NostrChallengeCompletedProvider { challenge: { type: this.DataTypes.STRING, allowNull: false, + unique: true, }, signed_event: { type: this.DataTypes.JSONB, diff --git a/src/models/NostrChallengeCreated.js b/src/models/NostrChallengeCreated.js index f0f0e91..f35ddc7 100644 --- a/src/models/NostrChallengeCreated.js +++ b/src/models/NostrChallengeCreated.js @@ -17,6 +17,7 @@ class NostrChallengeCreatedProvider { challenge: { type: this.DataTypes.STRING, allowNull: false, + unique: true, }, expires_at: { type: this.DataTypes.DATE, From 317ca7ded2288e870ec8657f28f899d1ba892242 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:43:55 +0100 Subject: [PATCH 121/176] split FKs into another file --- src/database/config.js | 1 + ... => 20250308000000-first-schema-tables.js} | 69 +++++++++---------- .../20250308000001-first-schema-fks.js | 42 +++++++++++ 3 files changed, 74 insertions(+), 38 deletions(-) rename src/database/migrations/{20250308000000-first-schema.js => 20250308000000-first-schema-tables.js} (95%) create mode 100644 src/database/migrations/20250308000001-first-schema-fks.js diff --git a/src/database/config.js b/src/database/config.js index a13f133..2800b22 100644 --- a/src/database/config.js +++ b/src/database/config.js @@ -10,6 +10,7 @@ module.exports = { database: process.env.POSTGRES_DB, username: process.env.POSTGRES_USER, password: process.env.POSTGRES_PASSWORD, + logging: console.log, define: { timestamps: false, freezeTableName: true, diff --git a/src/database/migrations/20250308000000-first-schema.js b/src/database/migrations/20250308000000-first-schema-tables.js similarity index 95% rename from src/database/migrations/20250308000000-first-schema.js rename to src/database/migrations/20250308000000-first-schema-tables.js index 8e7bfe9..3407606 100644 --- a/src/database/migrations/20250308000000-first-schema.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -48,7 +48,7 @@ module.exports = { { transaction: t } ), queryInterface.createTable( - 'login_challenge_completed', + 'nostr_challenge_created', { uuid: { type: Sequelize.UUID, @@ -56,13 +56,14 @@ module.exports = { unique: true, primaryKey: true, }, - nostr_challenge_completed_uuid: { - type: Sequelize.UUID, - allowNull: false, - }, - public_key: { + challenge: { type: Sequelize.STRING, allowNull: false, + unique: true, + }, + expires_at: { + type: Sequelize.DATE, + allowNull: false, }, created_at: { type: Sequelize.DATE, @@ -91,32 +92,6 @@ module.exports = { }, { 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( 'nostr_challenge_completed', { @@ -130,12 +105,6 @@ module.exports = { type: Sequelize.STRING, allowNull: false, unique: true, - references: { - model: { - tableName: 'nostr_challenge_created', - }, - key: 'challenge', - }, }, signed_event: { type: Sequelize.JSONB, @@ -152,6 +121,30 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js new file mode 100644 index 0000000..7ede52a --- /dev/null +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -0,0 +1,42 @@ +'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', + }), + queryInterface.addConstraint('nostr_challenge_completed', { + fields: ['challenge'], + type: 'foreign key', + references: { + table: 'nostr_challenge_created', + field: 'challenge', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), + queryInterface.addConstraint('login_challenge_completed', { + fields: ['nostr_challenge_completed_uuid'], + type: 'foreign key', + references: { + table: 'nostr_challenge_completed', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), + ]); + }); + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('Users'); + }, +}; From 8c4c9dfe991b3ca97b4809ddc30aeb4cf78a0f77 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:46:14 +0100 Subject: [PATCH 122/176] add nymset --- .../20250308000000-first-schema-tables.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 3407606..cef411b 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -145,6 +145,30 @@ module.exports = { }, { 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 } + ), ]); }); }, From 01c9fca0931862b7d5d1d1ac0ff8dbf78dbee4ca Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:51:13 +0100 Subject: [PATCH 123/176] offercreated offerdeleted --- .../20250308000000-first-schema-tables.js | 43 +++++++++++++++++++ .../20250308000001-first-schema-fks.js | 10 +++++ 2 files changed, 53 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index cef411b..16c5456 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -1,4 +1,7 @@ 'use strict'; + +const { query } = require('express'); + module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.sequelize.transaction((t) => { @@ -169,6 +172,46 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index 7ede52a..79d1544 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -33,6 +33,16 @@ module.exports = { onDelete: 'cascade', onUpdate: 'cascade', }), + queryInterface.addConstraint('offer_deleted', { + fields: ['offer_uuid'], + type: 'foreign key', + references: { + table: 'offer_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), ]); }); }, From d3e419c98b19fabb09ef274b0cb27460b5a19037 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:54:08 +0100 Subject: [PATCH 124/176] offerdetailsset --- .../20250308000000-first-schema-tables.js | 64 +++++++++++++++++++ .../20250308000001-first-schema-fks.js | 10 +++ src/models/OfferDetailsSet.js | 2 +- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 16c5456..8cb5e18 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -212,6 +212,70 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index 79d1544..56137b2 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -43,6 +43,16 @@ module.exports = { onDelete: 'cascade', onUpdate: 'cascade', }), + queryInterface.addConstraint('offer_details_set', { + fields: ['offer_uuid'], + type: 'foreign key', + references: { + table: 'offer_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), ]); }); }, diff --git a/src/models/OfferDetailsSet.js b/src/models/OfferDetailsSet.js index 766f148..d2023d7 100644 --- a/src/models/OfferDetailsSet.js +++ b/src/models/OfferDetailsSet.js @@ -15,7 +15,7 @@ class OfferDetailsSetProvider { primaryKey: true, }, offer_uuid: { - type: this.DataTypes.STRING, + type: this.DataTypes.UUID, allowNull: false, }, wants: { From bccde12a046667a76ad539450e2dea2f5533af1d Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:56:00 +0100 Subject: [PATCH 125/176] session created --- .../20250308000000-first-schema-tables.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 8cb5e18..60a4dd7 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -276,6 +276,26 @@ module.exports = { }, { 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 } + ), ]); }); }, From 78362f1067624460817de66fb19706049141dd6f Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 13:57:35 +0100 Subject: [PATCH 126/176] session related to publickey --- .../20250308000000-first-schema-tables.js | 24 +++++++++++++++++++ .../20250308000001-first-schema-fks.js | 10 ++++++++ 2 files changed, 34 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 60a4dd7..7506bb8 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -296,6 +296,30 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index 56137b2..54c86b6 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -53,6 +53,16 @@ module.exports = { onDelete: 'cascade', onUpdate: 'cascade', }), + queryInterface.addConstraint('session_related_to_public_key', { + fields: ['session_uuid'], + type: 'foreign key', + references: { + table: 'session_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), ]); }); }, From 3e0a4772aaf7fac7cbc1095dd871f139def40b47 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 14:02:41 +0100 Subject: [PATCH 127/176] signupchallengecreated --- .../20250308000000-first-schema-tables.js | 24 +++++++++++++++++++ .../20250308000001-first-schema-fks.js | 20 ++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 7506bb8..73cba27 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -320,6 +320,30 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index 54c86b6..573d238 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -63,6 +63,26 @@ module.exports = { onDelete: 'cascade', onUpdate: 'cascade', }), + queryInterface.addConstraint('sign_up_challenge_created', { + fields: ['nostr_challenge_uuid'], + type: 'foreign key', + references: { + table: 'nostr_challenge_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), + queryInterface.addConstraint('sign_up_challenge_created', { + fields: ['app_invite_uuid'], + type: 'foreign key', + references: { + table: 'app_invite_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), ]); }); }, From 613ded0cf1b98f9cd35983643a7c4d4da4bcaf04 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 14:08:16 +0100 Subject: [PATCH 128/176] add signupchallengecompleted --- .../20250308000000-first-schema-tables.js | 28 +++++++++++++++++++ .../20250308000001-first-schema-fks.js | 20 +++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index 73cba27..c22bb65 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -344,6 +344,34 @@ module.exports = { }, { 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 } + ), ]); }); }, diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index 573d238..c79cad7 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -83,6 +83,26 @@ module.exports = { onDelete: 'cascade', onUpdate: 'cascade', }), + 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', + }), + queryInterface.addConstraint('sign_up_challenge_completed', { + fields: ['app_invite_uuid'], + type: 'foreign key', + references: { + table: 'app_invite_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', + }), ]); }); }, From 9c4581d33d7f9288e0980f81b898a58953b0b9fb Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 15:37:37 +0100 Subject: [PATCH 129/176] now only active offers are shown --- src/services/offerService.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/services/offerService.js b/src/services/offerService.js index b2696aa..67884f6 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -54,21 +54,23 @@ class OfferServiceProvider { }; const getOffersByPublicKey = async (publicKey) => { - const offers = await this.models.OfferCreated.findAll({ + const activeOffers = await this.models.OfferCreated.findAll({ where: { public_key: publicKey, + '$OfferDeleted.uuid$': null, }, + include: { model: this.models.OfferDeleted, required: false }, }); - console.log(offers); + console.log(activeOffers); - if (!offers) { + if (!activeOffers) { return []; } const offersToReturn = []; - if (offers) { - for (const someOffer of offers) { + if (activeOffers) { + for (const someOffer of activeOffers) { const offerDetails = await this.models.OfferDetailsSet.findOne({ where: { offer_uuid: someOffer.uuid, From 5b35bb603df15bfa26547bade3e5eaf171eada6a Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 15:38:23 +0100 Subject: [PATCH 130/176] rename --- src/routes/apiRoutes.js | 4 +++- src/services/offerService.js | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/routes/apiRoutes.js b/src/routes/apiRoutes.js index b93b1f0..102cf6a 100644 --- a/src/routes/apiRoutes.js +++ b/src/routes/apiRoutes.js @@ -274,7 +274,9 @@ class ApiRoutesProvider { const publicKey = req.cookies.publicKey; const offers = - await this.services.offerService.getOffersByPublicKey(publicKey); + await this.services.offerService.getActiveOffersByPublicKey( + publicKey + ); if (!offers) { return res.status(404).json({ diff --git a/src/services/offerService.js b/src/services/offerService.js index 67884f6..09c4079 100644 --- a/src/services/offerService.js +++ b/src/services/offerService.js @@ -53,7 +53,7 @@ class OfferServiceProvider { }); }; - const getOffersByPublicKey = async (publicKey) => { + const getActiveOffersByPublicKey = async (publicKey) => { const activeOffers = await this.models.OfferCreated.findAll({ where: { public_key: publicKey, @@ -102,7 +102,11 @@ class OfferServiceProvider { return offersToReturn; }; - return { createOffer, getOffersByPublicKey, deleteOffer }; + return { + createOffer, + getActiveOffersByPublicKey, + deleteOffer, + }; } } From 107edb70e731cc0721c8e19508270d036ab4dce3 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 15:51:47 +0100 Subject: [PATCH 131/176] move associations file --- src/{ => database}/associations.js | 0 src/dependencies.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{ => database}/associations.js (100%) diff --git a/src/associations.js b/src/database/associations.js similarity index 100% rename from src/associations.js rename to src/database/associations.js diff --git a/src/dependencies.js b/src/dependencies.js index a93620d..c961b30 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -10,7 +10,7 @@ function buildDependencies() { const ModelsProvider = require('./models'); const models = new ModelsProvider({ sequelize, DataTypes }).provide(); - const AssociationsDefiner = require('./associations'); + const AssociationsDefiner = require('./database/associations'); new AssociationsDefiner({ models, DataTypes }).define(); /* sequelize From 78509e657b7feadb47ce09a8d612f85d86a175d1 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 15:52:15 +0100 Subject: [PATCH 132/176] remove sync foreva --- src/dependencies.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/dependencies.js b/src/dependencies.js index c961b30..fd12f87 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -13,15 +13,6 @@ function buildDependencies() { const AssociationsDefiner = require('./database/associations'); new AssociationsDefiner({ models, DataTypes }).define(); - /* sequelize - .sync({ alter: true }) - .then(() => { - console.log('Database synced'); - }) - .catch((err) => { - console.error('Error syncing the database:', err); - }); - */ const ServicesProvider = require('./services'); const services = new ServicesProvider({ models, From 4f1d6b4cfbf5897f8525b743c1f34104e66cd479 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 16:04:27 +0100 Subject: [PATCH 133/176] deleting offer updates offer list --- src/public/javascript/offers.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 5bcd2cc..c3746fe 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -541,7 +541,10 @@ class Offer { deleteActionText.innerText = 'Eliminar'; deleteActionArea.append(deleteActionIcon, deleteActionText); deleteActionArea.addEventListener('click', async () => { - deleteOfferByUuid(this.uuid); + await deleteOfferByUuid(this.uuid); + await myOffers.getOffersFromApi(); + await myOffers.render(); + // toggle popup }); actionButtonsArea.append(editActionArea, deleteActionArea); @@ -601,9 +604,6 @@ async function deleteOfferByUuid(offerUuid) { 'Content-Type': 'application/json', }, }); - - myOffers.getOffersFromApi(); - myOffers.render(); } buttonStartCreateOffer.addEventListener('click', () => { From 56fa3e20e7cd5e006d1bb0f21a253f73e2aa39ab Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 16:11:57 +0100 Subject: [PATCH 134/176] refactor into function --- src/public/javascript/offers.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index c3746fe..54f20c6 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -177,6 +177,12 @@ async function publishOffer() { body: JSON.stringify({ offerDetails }), }); + toggleOfferCreatedAlert(); + + toggleCreateOfferModal(); +} + +function toggleOfferCreatedAlert() { offerCreatedPopup.classList.remove('max-size-zero'); offerCreatedPopup.classList.add('revealed'); setTimeout(() => { @@ -185,8 +191,6 @@ async function publishOffer() { setTimeout(() => { offerCreatedPopup.classList.add('max-size-zero'); }, 4000); - - toggleCreateOfferModal(); } class Offer { From 7fcf62e64751f4cb2d662b4812aa8cb25c1703a3 Mon Sep 17 00:00:00 2001 From: counterweight Date: Mon, 10 Mar 2025 16:45:16 +0100 Subject: [PATCH 135/176] delete offer popup --- src/public/javascript/offers.js | 14 +++++++++++++- src/views/offers.ejs | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js index 54f20c6..f38f202 100644 --- a/src/public/javascript/offers.js +++ b/src/public/javascript/offers.js @@ -45,6 +45,7 @@ const bigNotesAcceptedCheckbox = document.getElementById( const publishOfferButton = document.getElementById('button-submit-offer'); const offerCreatedPopup = document.getElementById('offer-created-confirmation'); +const offerDeletedPopup = document.getElementById('offer-deleted-confirmation'); const ownOffersContainer = document.getElementById('own-offers-container'); @@ -193,6 +194,17 @@ function toggleOfferCreatedAlert() { }, 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; @@ -548,7 +560,7 @@ class Offer { await deleteOfferByUuid(this.uuid); await myOffers.getOffersFromApi(); await myOffers.render(); - // toggle popup + toggleOfferDeletedAlert(); }); actionButtonsArea.append(editActionArea, deleteActionArea); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 17c03b7..b04d0a1 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -201,6 +201,13 @@

¡Oferta creada! Puedes verla en tus ofertas.

+
+ +

¡Oferta eliminada!

+
<%- include("partials/appCommonScripts") %> From e81533db4c825281b216d0945eb084a02b3924a8 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 12 Mar 2025 16:28:44 +0100 Subject: [PATCH 136/176] move public --- {src/public => public}/css/createProfile.css | 0 {src/public => public}/css/invite.css | 0 {src/public => public}/css/offers.css | 0 {src/public => public}/css/seca.css | 0 .../img/bolt-lightning-black.svg | 0 .../img/bolt-lightning-gray.svg | 0 .../img/bolt-lightning-lasecagold.svg | 0 {src/public => public}/img/bolt-lightning.svg | 0 {src/public => public}/img/chains-black.svg | 0 {src/public => public}/img/chains-gray.svg | 0 .../img/chains-lasecagold.svg | 0 {src/public => public}/img/chains.svg | 0 .../img/circle-check-green.svg | 0 .../img/circle-check-white.svg | 0 .../img/circle-xmark-gray.svg | 0 {src/public => public}/img/circle-xmark.svg | 0 {src/public => public}/img/edit.svg | 0 {src/public => public}/img/envelope.svg | 0 {src/public => public}/img/eur-bill-gray.svg | 0 .../img/eur-bill-lasecagold.svg | 0 {src/public => public}/img/eur-bill.svg | 0 .../img/laseca-logo-transparent-textonly.svg | 0 .../img/laseca_logo_white.png | Bin .../img/lasecagold_ostrich.svg | 0 .../public => public}/img/many-users-gray.svg | 0 .../img/many-users-lasecagold.svg | 0 {src/public => public}/img/many-users.svg | 0 .../img/message-exclamation.svg | 0 {src/public => public}/img/phone.svg | 0 .../img/signal-messenger.svg | 0 .../public => public}/img/square-whatsapp.svg | 0 {src/public => public}/img/telegram.svg | 0 .../img/trash-can-darkred.svg | 0 .../img/trash-can-lasecagold.svg | 0 {src/public => public}/img/trash-can.svg | 0 {src/public => public}/img/user-gray.svg | 0 .../public => public}/img/user-group-gray.svg | 0 .../img/user-group-lasecagold.svg | 0 {src/public => public}/img/user-group.svg | 0 .../public => public}/img/user-lasecagold.svg | 0 {src/public => public}/img/user.svg | 0 {src/public => public}/img/white_ostrich.svg | 0 {src/public => public}/javascript/app.js | 0 .../javascript/createProfile.js | 0 {src/public => public}/javascript/home.js | 0 {src/public => public}/javascript/invite.js | 0 {src/public => public}/javascript/login.js | 0 public/javascript/offers.js | 697 ++++++++++++++++++ {src/public => public}/javascript/utils.js | 0 src/app.js | 2 +- src/public/javascript/offers.js | 685 ----------------- 51 files changed, 698 insertions(+), 686 deletions(-) rename {src/public => public}/css/createProfile.css (100%) rename {src/public => public}/css/invite.css (100%) rename {src/public => public}/css/offers.css (100%) rename {src/public => public}/css/seca.css (100%) rename {src/public => public}/img/bolt-lightning-black.svg (100%) rename {src/public => public}/img/bolt-lightning-gray.svg (100%) rename {src/public => public}/img/bolt-lightning-lasecagold.svg (100%) rename {src/public => public}/img/bolt-lightning.svg (100%) rename {src/public => public}/img/chains-black.svg (100%) rename {src/public => public}/img/chains-gray.svg (100%) rename {src/public => public}/img/chains-lasecagold.svg (100%) rename {src/public => public}/img/chains.svg (100%) rename {src/public => public}/img/circle-check-green.svg (100%) rename {src/public => public}/img/circle-check-white.svg (100%) rename {src/public => public}/img/circle-xmark-gray.svg (100%) rename {src/public => public}/img/circle-xmark.svg (100%) rename {src/public => public}/img/edit.svg (100%) rename {src/public => public}/img/envelope.svg (100%) rename {src/public => public}/img/eur-bill-gray.svg (100%) rename {src/public => public}/img/eur-bill-lasecagold.svg (100%) rename {src/public => public}/img/eur-bill.svg (100%) rename {src/public => public}/img/laseca-logo-transparent-textonly.svg (100%) rename {src/public => public}/img/laseca_logo_white.png (100%) rename {src/public => public}/img/lasecagold_ostrich.svg (100%) rename {src/public => public}/img/many-users-gray.svg (100%) rename {src/public => public}/img/many-users-lasecagold.svg (100%) rename {src/public => public}/img/many-users.svg (100%) rename {src/public => public}/img/message-exclamation.svg (100%) rename {src/public => public}/img/phone.svg (100%) rename {src/public => public}/img/signal-messenger.svg (100%) rename {src/public => public}/img/square-whatsapp.svg (100%) rename {src/public => public}/img/telegram.svg (100%) rename {src/public => public}/img/trash-can-darkred.svg (100%) rename {src/public => public}/img/trash-can-lasecagold.svg (100%) rename {src/public => public}/img/trash-can.svg (100%) rename {src/public => public}/img/user-gray.svg (100%) rename {src/public => public}/img/user-group-gray.svg (100%) rename {src/public => public}/img/user-group-lasecagold.svg (100%) rename {src/public => public}/img/user-group.svg (100%) rename {src/public => public}/img/user-lasecagold.svg (100%) rename {src/public => public}/img/user.svg (100%) rename {src/public => public}/img/white_ostrich.svg (100%) rename {src/public => public}/javascript/app.js (100%) rename {src/public => public}/javascript/createProfile.js (100%) rename {src/public => public}/javascript/home.js (100%) rename {src/public => public}/javascript/invite.js (100%) rename {src/public => public}/javascript/login.js (100%) create mode 100644 public/javascript/offers.js rename {src/public => public}/javascript/utils.js (100%) delete mode 100644 src/public/javascript/offers.js diff --git a/src/public/css/createProfile.css b/public/css/createProfile.css similarity index 100% rename from src/public/css/createProfile.css rename to public/css/createProfile.css diff --git a/src/public/css/invite.css b/public/css/invite.css similarity index 100% rename from src/public/css/invite.css rename to public/css/invite.css diff --git a/src/public/css/offers.css b/public/css/offers.css similarity index 100% rename from src/public/css/offers.css rename to public/css/offers.css diff --git a/src/public/css/seca.css b/public/css/seca.css similarity index 100% rename from src/public/css/seca.css rename to public/css/seca.css diff --git a/src/public/img/bolt-lightning-black.svg b/public/img/bolt-lightning-black.svg similarity index 100% rename from src/public/img/bolt-lightning-black.svg rename to public/img/bolt-lightning-black.svg diff --git a/src/public/img/bolt-lightning-gray.svg b/public/img/bolt-lightning-gray.svg similarity index 100% rename from src/public/img/bolt-lightning-gray.svg rename to public/img/bolt-lightning-gray.svg diff --git a/src/public/img/bolt-lightning-lasecagold.svg b/public/img/bolt-lightning-lasecagold.svg similarity index 100% rename from src/public/img/bolt-lightning-lasecagold.svg rename to public/img/bolt-lightning-lasecagold.svg diff --git a/src/public/img/bolt-lightning.svg b/public/img/bolt-lightning.svg similarity index 100% rename from src/public/img/bolt-lightning.svg rename to public/img/bolt-lightning.svg diff --git a/src/public/img/chains-black.svg b/public/img/chains-black.svg similarity index 100% rename from src/public/img/chains-black.svg rename to public/img/chains-black.svg diff --git a/src/public/img/chains-gray.svg b/public/img/chains-gray.svg similarity index 100% rename from src/public/img/chains-gray.svg rename to public/img/chains-gray.svg diff --git a/src/public/img/chains-lasecagold.svg b/public/img/chains-lasecagold.svg similarity index 100% rename from src/public/img/chains-lasecagold.svg rename to public/img/chains-lasecagold.svg diff --git a/src/public/img/chains.svg b/public/img/chains.svg similarity index 100% rename from src/public/img/chains.svg rename to public/img/chains.svg diff --git a/src/public/img/circle-check-green.svg b/public/img/circle-check-green.svg similarity index 100% rename from src/public/img/circle-check-green.svg rename to public/img/circle-check-green.svg diff --git a/src/public/img/circle-check-white.svg b/public/img/circle-check-white.svg similarity index 100% rename from src/public/img/circle-check-white.svg rename to public/img/circle-check-white.svg diff --git a/src/public/img/circle-xmark-gray.svg b/public/img/circle-xmark-gray.svg similarity index 100% rename from src/public/img/circle-xmark-gray.svg rename to public/img/circle-xmark-gray.svg diff --git a/src/public/img/circle-xmark.svg b/public/img/circle-xmark.svg similarity index 100% rename from src/public/img/circle-xmark.svg rename to public/img/circle-xmark.svg diff --git a/src/public/img/edit.svg b/public/img/edit.svg similarity index 100% rename from src/public/img/edit.svg rename to public/img/edit.svg diff --git a/src/public/img/envelope.svg b/public/img/envelope.svg similarity index 100% rename from src/public/img/envelope.svg rename to public/img/envelope.svg diff --git a/src/public/img/eur-bill-gray.svg b/public/img/eur-bill-gray.svg similarity index 100% rename from src/public/img/eur-bill-gray.svg rename to public/img/eur-bill-gray.svg diff --git a/src/public/img/eur-bill-lasecagold.svg b/public/img/eur-bill-lasecagold.svg similarity index 100% rename from src/public/img/eur-bill-lasecagold.svg rename to public/img/eur-bill-lasecagold.svg diff --git a/src/public/img/eur-bill.svg b/public/img/eur-bill.svg similarity index 100% rename from src/public/img/eur-bill.svg rename to public/img/eur-bill.svg diff --git a/src/public/img/laseca-logo-transparent-textonly.svg b/public/img/laseca-logo-transparent-textonly.svg similarity index 100% rename from src/public/img/laseca-logo-transparent-textonly.svg rename to public/img/laseca-logo-transparent-textonly.svg diff --git a/src/public/img/laseca_logo_white.png b/public/img/laseca_logo_white.png similarity index 100% rename from src/public/img/laseca_logo_white.png rename to public/img/laseca_logo_white.png diff --git a/src/public/img/lasecagold_ostrich.svg b/public/img/lasecagold_ostrich.svg similarity index 100% rename from src/public/img/lasecagold_ostrich.svg rename to public/img/lasecagold_ostrich.svg diff --git a/src/public/img/many-users-gray.svg b/public/img/many-users-gray.svg similarity index 100% rename from src/public/img/many-users-gray.svg rename to public/img/many-users-gray.svg diff --git a/src/public/img/many-users-lasecagold.svg b/public/img/many-users-lasecagold.svg similarity index 100% rename from src/public/img/many-users-lasecagold.svg rename to public/img/many-users-lasecagold.svg diff --git a/src/public/img/many-users.svg b/public/img/many-users.svg similarity index 100% rename from src/public/img/many-users.svg rename to public/img/many-users.svg diff --git a/src/public/img/message-exclamation.svg b/public/img/message-exclamation.svg similarity index 100% rename from src/public/img/message-exclamation.svg rename to public/img/message-exclamation.svg diff --git a/src/public/img/phone.svg b/public/img/phone.svg similarity index 100% rename from src/public/img/phone.svg rename to public/img/phone.svg diff --git a/src/public/img/signal-messenger.svg b/public/img/signal-messenger.svg similarity index 100% rename from src/public/img/signal-messenger.svg rename to public/img/signal-messenger.svg diff --git a/src/public/img/square-whatsapp.svg b/public/img/square-whatsapp.svg similarity index 100% rename from src/public/img/square-whatsapp.svg rename to public/img/square-whatsapp.svg diff --git a/src/public/img/telegram.svg b/public/img/telegram.svg similarity index 100% rename from src/public/img/telegram.svg rename to public/img/telegram.svg diff --git a/src/public/img/trash-can-darkred.svg b/public/img/trash-can-darkred.svg similarity index 100% rename from src/public/img/trash-can-darkred.svg rename to public/img/trash-can-darkred.svg diff --git a/src/public/img/trash-can-lasecagold.svg b/public/img/trash-can-lasecagold.svg similarity index 100% rename from src/public/img/trash-can-lasecagold.svg rename to public/img/trash-can-lasecagold.svg diff --git a/src/public/img/trash-can.svg b/public/img/trash-can.svg similarity index 100% rename from src/public/img/trash-can.svg rename to public/img/trash-can.svg diff --git a/src/public/img/user-gray.svg b/public/img/user-gray.svg similarity index 100% rename from src/public/img/user-gray.svg rename to public/img/user-gray.svg diff --git a/src/public/img/user-group-gray.svg b/public/img/user-group-gray.svg similarity index 100% rename from src/public/img/user-group-gray.svg rename to public/img/user-group-gray.svg diff --git a/src/public/img/user-group-lasecagold.svg b/public/img/user-group-lasecagold.svg similarity index 100% rename from src/public/img/user-group-lasecagold.svg rename to public/img/user-group-lasecagold.svg diff --git a/src/public/img/user-group.svg b/public/img/user-group.svg similarity index 100% rename from src/public/img/user-group.svg rename to public/img/user-group.svg diff --git a/src/public/img/user-lasecagold.svg b/public/img/user-lasecagold.svg similarity index 100% rename from src/public/img/user-lasecagold.svg rename to public/img/user-lasecagold.svg diff --git a/src/public/img/user.svg b/public/img/user.svg similarity index 100% rename from src/public/img/user.svg rename to public/img/user.svg diff --git a/src/public/img/white_ostrich.svg b/public/img/white_ostrich.svg similarity index 100% rename from src/public/img/white_ostrich.svg rename to public/img/white_ostrich.svg diff --git a/src/public/javascript/app.js b/public/javascript/app.js similarity index 100% rename from src/public/javascript/app.js rename to public/javascript/app.js diff --git a/src/public/javascript/createProfile.js b/public/javascript/createProfile.js similarity index 100% rename from src/public/javascript/createProfile.js rename to public/javascript/createProfile.js diff --git a/src/public/javascript/home.js b/public/javascript/home.js similarity index 100% rename from src/public/javascript/home.js rename to public/javascript/home.js diff --git a/src/public/javascript/invite.js b/public/javascript/invite.js similarity index 100% rename from src/public/javascript/invite.js rename to public/javascript/invite.js diff --git a/src/public/javascript/login.js b/public/javascript/login.js similarity index 100% rename from src/public/javascript/login.js rename to public/javascript/login.js diff --git a/public/javascript/offers.js b/public/javascript/offers.js new file mode 100644 index 0000000..4e57b19 --- /dev/null +++ b/public/javascript/offers.js @@ -0,0 +1,697 @@ +function offersPage() { + 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 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'); + + 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 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; + } + } + + 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() { + 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 }), + }); + + 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(); + }); + + 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); + }); + } + + myTrustedTrustedCheckbox.addEventListener('click', () => { + applyTrustCheckboxConstraints(myTrustedTrustedCheckbox); + }); + + allMembersCheckbox.addEventListener('click', () => { + applyTrustCheckboxConstraints(allMembersCheckbox); + }); + + publishOfferButton.addEventListener('click', async () => { + await publishOffer(); + await myOffers.getOffersFromApi(); + await myOffers.render(); + }); + + updateBtcInput(); + + const myOffers = new MyOffers(ownOffersContainer); +} + +offersPage; diff --git a/src/public/javascript/utils.js b/public/javascript/utils.js similarity index 100% rename from src/public/javascript/utils.js rename to public/javascript/utils.js diff --git a/src/app.js b/src/app.js index dc3cf83..76a3146 100644 --- a/src/app.js +++ b/src/app.js @@ -21,7 +21,7 @@ function createApp(dependencies) { app.use('/', dependencies.webRoutes); app.use('/api', dependencies.apiRoutes); - app.use(express.static(path.join(__dirname, 'public'))); + app.use(express.static(path.join(__dirname, '../public'))); app.disable('etag'); //avoids 304 responses diff --git a/src/public/javascript/offers.js b/src/public/javascript/offers.js deleted file mode 100644 index f38f202..0000000 --- a/src/public/javascript/offers.js +++ /dev/null @@ -1,685 +0,0 @@ -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 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'); - -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 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; - } -} - -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() { - 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 }), - }); - - 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(); -}); - -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); - }); -} - -myTrustedTrustedCheckbox.addEventListener('click', () => { - applyTrustCheckboxConstraints(myTrustedTrustedCheckbox); -}); - -allMembersCheckbox.addEventListener('click', () => { - applyTrustCheckboxConstraints(allMembersCheckbox); -}); - -publishOfferButton.addEventListener('click', async () => { - await publishOffer(); - await myOffers.getOffersFromApi(); - await myOffers.render(); -}); - -updateBtcInput(); - -const myOffers = new MyOffers(ownOffersContainer); From f034f29d94ad4686d40aac2f7daae1cd7d6ad990 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 12 Mar 2025 18:56:36 +0100 Subject: [PATCH 137/176] install webpack --- package-lock.json | 1118 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 6 +- 2 files changed, 1122 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2bea970..4068beb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,18 @@ "playwright": "^1.50.1", "prettier": "^3.5.1", "prettier-plugin-ejs": "^1.0.3", - "sequelize-cli": "^6.6.2" + "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" } }, "node_modules/@eslint-community/eslint-utils": { @@ -290,6 +301,64 @@ "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", @@ -494,6 +563,38 @@ "@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", @@ -517,6 +618,208 @@ "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", @@ -629,6 +932,45 @@ "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", @@ -775,6 +1117,38 @@ "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", @@ -798,6 +1172,12 @@ "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", @@ -870,6 +1250,26 @@ "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", @@ -893,6 +1293,15 @@ "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", @@ -929,6 +1338,20 @@ "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", @@ -954,6 +1377,12 @@ "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", @@ -1243,6 +1672,12 @@ "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", @@ -1286,6 +1721,19 @@ "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", @@ -1295,6 +1743,18 @@ "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", @@ -1317,6 +1777,12 @@ "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", @@ -1658,6 +2124,15 @@ "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", @@ -1741,6 +2216,31 @@ "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", @@ -1824,6 +2324,15 @@ "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", @@ -2044,6 +2553,12 @@ "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", @@ -2270,6 +2785,25 @@ "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", @@ -2321,6 +2855,15 @@ "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", @@ -2399,6 +2942,18 @@ "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", @@ -2410,6 +2965,15 @@ "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", @@ -2442,6 +3006,35 @@ "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", @@ -2571,6 +3164,12 @@ "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", @@ -2601,6 +3200,15 @@ "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", @@ -2613,6 +3221,15 @@ "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", @@ -2728,6 +3345,12 @@ "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", @@ -2942,6 +3565,12 @@ "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", @@ -2988,6 +3617,12 @@ "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", @@ -3138,6 +3773,15 @@ "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", @@ -3310,6 +3954,76 @@ "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", @@ -3531,6 +4245,15 @@ } ] }, + "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", @@ -3580,6 +4303,18 @@ "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", @@ -3589,6 +4324,15 @@ "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", @@ -3609,6 +4353,27 @@ "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", @@ -3701,6 +4466,59 @@ "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", @@ -3868,6 +4686,15 @@ "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", @@ -3893,6 +4720,18 @@ "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", @@ -4090,6 +4929,25 @@ "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", @@ -4255,6 +5113,15 @@ "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", @@ -4310,6 +5177,64 @@ "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", @@ -4450,6 +5375,36 @@ "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", @@ -4499,6 +5454,161 @@ "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", @@ -4522,6 +5632,12 @@ "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", diff --git a/package.json b/package.json index 26f47dc..a1170cb 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "start": "node src/app.js", "start:container": "docker compose up -d --build", "stop:container": "docker compose down", + "build": "webpack", + "watch": "webpack --watch", "cli": "node src/cli.js", "test": "playwright test", "lint": "eslint . --fix", @@ -37,6 +39,8 @@ "playwright": "^1.50.1", "prettier": "^3.5.1", "prettier-plugin-ejs": "^1.0.3", - "sequelize-cli": "^6.6.2" + "sequelize-cli": "^6.6.2", + "webpack": "^5.98.0", + "webpack-cli": "^6.0.1" } } From 5b6ab8779e19aa8ae936b70c73b5add41efb39b7 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 12 Mar 2025 18:56:53 +0100 Subject: [PATCH 138/176] configure webpack --- webpack.config.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 webpack.config.js diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..6cedde0 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,12 @@ +const path = require('path'); + +module.exports = { + entry: { + invite: './src/front/pages/invite.js', + }, + output: { + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'public'), + }, + mode: 'development', +}; From ae64424f54cffc846ac7114f99c697fc47ff0999 Mon Sep 17 00:00:00 2001 From: counterweight Date: Wed, 12 Mar 2025 18:57:17 +0100 Subject: [PATCH 139/176] refactor invite page to pickup webpacked code --- public/javascript/invite.js | 74 --------------------------------- src/front/pages/invite.js | 83 +++++++++++++++++++++++++++++++++++++ src/views/invite.ejs | 6 +-- 3 files changed, 86 insertions(+), 77 deletions(-) delete mode 100644 public/javascript/invite.js create mode 100644 src/front/pages/invite.js diff --git a/public/javascript/invite.js b/public/javascript/invite.js deleted file mode 100644 index 70bb66d..0000000 --- a/public/javascript/invite.js +++ /dev/null @@ -1,74 +0,0 @@ -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'); - } -}; - -const signUpConfirmation = document.querySelector('#sign-up-success'); - -function showConfirmationAndRedirect() { - signUpConfirmation.classList.add('revealed'); - setTimeout(() => { - window.location.href = '/createProfile'; - }, 5000); -} - -async function acceptInvite() { - let challengeResponse; - try { - challengeResponse = await fetch('/api/signup/nostr-challenge', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - const { challenge } = await challengeResponse.json(); - - let pubkey; - try { - pubkey = await window.nostr.getPublicKey(); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - const event = { - kind: 22242, - created_at: Math.floor(Date.now() / 1000), - tags: [['challenge', challenge]], - content: 'Sign this challenge to authenticate', - pubkey: pubkey, - }; - - let signedEvent; - try { - signedEvent = await window.nostr.signEvent(event); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - - let verifyResponse; - try { - verifyResponse = await fetch('/api/signup/nostr-verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(signedEvent), - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - if (verifyResponse.ok) { - showConfirmationAndRedirect(); - } -} diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js new file mode 100644 index 0000000..c5b947e --- /dev/null +++ b/src/front/pages/invite.js @@ -0,0 +1,83 @@ +const invitesFunction = () => { + 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'); + } + }; + + const signUpConfirmation = document.querySelector('#sign-up-success'); + + function showConfirmationAndRedirect() { + signUpConfirmation.classList.add('revealed'); + setTimeout(() => { + window.location.href = '/createProfile'; + }, 5000); + } + + async function acceptInvite() { + let challengeResponse; + try { + challengeResponse = await fetch('/api/signup/nostr-challenge', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + const { challenge } = await challengeResponse.json(); + + let pubkey; + try { + pubkey = await window.nostr.getPublicKey(); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + const event = { + kind: 22242, + created_at: Math.floor(Date.now() / 1000), + tags: [['challenge', challenge]], + content: 'Sign this challenge to authenticate', + pubkey: pubkey, + }; + + let signedEvent; + try { + signedEvent = await window.nostr.signEvent(event); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + + let verifyResponse; + try { + verifyResponse = await fetch('/api/signup/nostr-verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(signedEvent), + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + if (verifyResponse.ok) { + showConfirmationAndRedirect(); + } + } + + const signUpButton = document.getElementById('nostr-signup-button'); + signUpButton.addEventListener('click', () => { + acceptInvite(); + }); +}; + +invitesFunction(); diff --git a/src/views/invite.ejs b/src/views/invite.ejs index 3957b59..d5e4dc9 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -21,7 +21,7 @@ />

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

- +
- +
- + From eb1cfbb64c3f92d6e94ae3a802f28b0379fce0d4 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:05:12 +0100 Subject: [PATCH 140/176] next page --- public/javascript/createProfile.js | 121 ---------------------------- src/front/pages/createProfile.js | 125 +++++++++++++++++++++++++++++ src/views/createProfile.ejs | 2 +- src/views/invite.ejs | 2 +- webpack.config.js | 3 +- 5 files changed, 129 insertions(+), 124 deletions(-) delete mode 100644 public/javascript/createProfile.js create mode 100644 src/front/pages/createProfile.js diff --git a/public/javascript/createProfile.js b/public/javascript/createProfile.js deleted file mode 100644 index 4be8920..0000000 --- a/public/javascript/createProfile.js +++ /dev/null @@ -1,121 +0,0 @@ -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); diff --git a/src/front/pages/createProfile.js b/src/front/pages/createProfile.js new file mode 100644 index 0000000..51e61dd --- /dev/null +++ b/src/front/pages/createProfile.js @@ -0,0 +1,125 @@ +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/views/createProfile.ejs b/src/views/createProfile.ejs index 3613440..d4be5ec 100644 --- a/src/views/createProfile.ejs +++ b/src/views/createProfile.ejs @@ -130,5 +130,5 @@
- + diff --git a/src/views/invite.ejs b/src/views/invite.ejs index d5e4dc9..d7d8f79 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -110,6 +110,6 @@
- + diff --git a/webpack.config.js b/webpack.config.js index 6cedde0..9e61a4e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,10 +3,11 @@ const path = require('path'); module.exports = { entry: { invite: './src/front/pages/invite.js', + createProfile: './src/front/pages/createProfile.js', }, output: { filename: '[name].bundle.js', - path: path.resolve(__dirname, 'public'), + path: path.resolve(__dirname, 'public', 'javascript'), }, mode: 'development', }; From 9dbe299a3260976d972512670f30639a02db4eff Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:08:54 +0100 Subject: [PATCH 141/176] login page --- public/javascript/home.js | 0 public/javascript/login.js | 72 --------------------------------- src/front/pages/login.js | 81 ++++++++++++++++++++++++++++++++++++++ src/views/login.ejs | 6 +-- webpack.config.js | 1 + 5 files changed, 85 insertions(+), 75 deletions(-) delete mode 100644 public/javascript/home.js delete mode 100644 public/javascript/login.js create mode 100644 src/front/pages/login.js diff --git a/public/javascript/home.js b/public/javascript/home.js deleted file mode 100644 index e69de29..0000000 diff --git a/public/javascript/login.js b/public/javascript/login.js deleted file mode 100644 index 36175b4..0000000 --- a/public/javascript/login.js +++ /dev/null @@ -1,72 +0,0 @@ -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', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - const { challenge } = await challengeResponse.json(); - - let pubkey; - try { - pubkey = await window.nostr.getPublicKey(); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - const event = { - kind: 22242, - created_at: Math.floor(Date.now() / 1000), - tags: [['challenge', challenge]], - content: 'Sign this challenge to authenticate', - pubkey: pubkey, - }; - - let signedEvent; - try { - signedEvent = await window.nostr.signEvent(event); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - - let verifyResponse; - try { - verifyResponse = await fetch('/api/login/nostr-verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(signedEvent), - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - if (verifyResponse.status === 403) { - document.querySelector('#rejected-public-key').style.display = 'block'; - } - - if (verifyResponse.ok) { - document.querySelector('#sign-up-success').style.display = 'block'; - setTimeout(() => { - window.location.href = '/home'; - }, 1000); - } -} diff --git a/src/front/pages/login.js b/src/front/pages/login.js new file mode 100644 index 0000000..121f3e1 --- /dev/null +++ b/src/front/pages/login.js @@ -0,0 +1,81 @@ +const loginsFunction = () => { + 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', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + const { challenge } = await challengeResponse.json(); + + let pubkey; + try { + pubkey = await window.nostr.getPublicKey(); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + const event = { + kind: 22242, + created_at: Math.floor(Date.now() / 1000), + tags: [['challenge', challenge]], + content: 'Sign this challenge to authenticate', + pubkey: pubkey, + }; + + let signedEvent; + try { + signedEvent = await window.nostr.signEvent(event); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + + let verifyResponse; + try { + verifyResponse = await fetch('/api/login/nostr-verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(signedEvent), + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + if (verifyResponse.status === 403) { + document.querySelector('#rejected-public-key').style.display = 'block'; + } + + if (verifyResponse.ok) { + document.querySelector('#sign-up-success').style.display = 'block'; + setTimeout(() => { + window.location.href = '/home'; + }, 1000); + } + } + + const loginButton = document.getElementById('login-button'); + loginButton.addEventListener('click', () => { + login(); + }); +}; + +loginsFunction(); diff --git a/src/views/login.ejs b/src/views/login.ejs index a4467ec..a954697 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -13,7 +13,7 @@
-
+
- +
- + diff --git a/webpack.config.js b/webpack.config.js index 9e61a4e..e51ce85 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ module.exports = { entry: { invite: './src/front/pages/invite.js', createProfile: './src/front/pages/createProfile.js', + login: './src/front/pages/login.js', }, output: { filename: '[name].bundle.js', From 161135e3152800f96f46f88ba4b6972486b41d5f Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:16:01 +0100 Subject: [PATCH 142/176] offers --- {public/javascript => src/front/pages}/offers.js | 2 +- src/views/offers.ejs | 2 +- webpack.config.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename {public/javascript => src/front/pages}/offers.js (99%) diff --git a/public/javascript/offers.js b/src/front/pages/offers.js similarity index 99% rename from public/javascript/offers.js rename to src/front/pages/offers.js index 4e57b19..b619ab0 100644 --- a/public/javascript/offers.js +++ b/src/front/pages/offers.js @@ -694,4 +694,4 @@ function offersPage() { const myOffers = new MyOffers(ownOffersContainer); } -offersPage; +offersPage(); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index b04d0a1..2c99fe7 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -210,6 +210,6 @@
<%- include("partials/appCommonScripts") %> - + diff --git a/webpack.config.js b/webpack.config.js index e51ce85..9312cea 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ module.exports = { invite: './src/front/pages/invite.js', createProfile: './src/front/pages/createProfile.js', login: './src/front/pages/login.js', + offers: './src/front/pages/offers.js', }, output: { filename: '[name].bundle.js', From 0f7ec6f521f5cb02b2e048cd0c2f9a3efc3dbe48 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:16:58 +0100 Subject: [PATCH 143/176] inline utils --- public/javascript/utils.js | 3 --- src/front/pages/offers.js | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 public/javascript/utils.js diff --git a/public/javascript/utils.js b/public/javascript/utils.js deleted file mode 100644 index e8964e2..0000000 --- a/public/javascript/utils.js +++ /dev/null @@ -1,3 +0,0 @@ -function formatNumberWithSpaces(num) { - return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); -} diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js index b619ab0..e85add2 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,4 +1,8 @@ function offersPage() { + function formatNumberWithSpaces(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); + } + const buttonStartCreateOffer = document.getElementById( 'button-start-create-offer' ); From 17517f798d456c9a6687daf79ba65cf95256256b Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:28:07 +0100 Subject: [PATCH 144/176] home --- src/front/pages/home.js | 14 ++++++++++++++ src/front/pages/offers.js | 11 +++++++++++ src/views/home.ejs | 2 +- webpack.config.js | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/front/pages/home.js diff --git a/src/front/pages/home.js b/src/front/pages/home.js new file mode 100644 index 0000000..92bd6bb --- /dev/null +++ b/src/front/pages/home.js @@ -0,0 +1,14 @@ +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/offers.js b/src/front/pages/offers.js index e85add2..b0954d3 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -3,6 +3,17 @@ function offersPage() { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); } + 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' ); diff --git a/src/views/home.ejs b/src/views/home.ejs index 9584e63..a764e25 100644 --- a/src/views/home.ejs +++ b/src/views/home.ejs @@ -11,6 +11,6 @@ <%- include("partials/appCommonHeader") %> <%- include("partials/appCommonScripts") %> - + diff --git a/webpack.config.js b/webpack.config.js index 9312cea..86f8a98 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,6 +6,7 @@ module.exports = { createProfile: './src/front/pages/createProfile.js', login: './src/front/pages/login.js', offers: './src/front/pages/offers.js', + home: './src/front/pages/home.js', }, output: { filename: '[name].bundle.js', From faf8d61ef838405bc95e0662d27b65511286c8bb Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:28:13 +0100 Subject: [PATCH 145/176] home --- public/javascript/app.js | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 public/javascript/app.js diff --git a/public/javascript/app.js b/public/javascript/app.js deleted file mode 100644 index aa28857..0000000 --- a/public/javascript/app.js +++ /dev/null @@ -1,10 +0,0 @@ -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'; -}); From 5a7c9549821646aa0b1b506f183066ea4b917457 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:33:10 +0100 Subject: [PATCH 146/176] ignore bundle files --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 846e04f..d83cd91 100644 --- a/.gitignore +++ b/.gitignore @@ -136,10 +136,10 @@ dist .pnp.* -test-results/* - -# Playwright /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ + +# webpack bundles +/public/javascript/* \ No newline at end of file From 0b3e5e83a3c4f602ad73237985a283b24cf7b8cc Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:45:28 +0100 Subject: [PATCH 147/176] extract util --- src/front/pages/invite.js | 22 ++++++++++++++-------- src/front/utils/checkNostrExtension.js | 9 +++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 src/front/utils/checkNostrExtension.js diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index c5b947e..bca4e92 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -1,12 +1,18 @@ +const checkNostrExtension = require('../utils/checkNostrExtension'); + const invitesFunction = () => { - 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'); - } + window.onload = () => { + checkNostrExtension( + window, + () => { + console.log('Nostr extension present'); + }, + () => { + console.log('Nostr extension not present'); + document.querySelector('#nostr-signup-button').disabled = true; + document.querySelector('#no-extension-nudges').style.display = 'block'; + } + ); }; const signUpConfirmation = document.querySelector('#sign-up-success'); diff --git a/src/front/utils/checkNostrExtension.js b/src/front/utils/checkNostrExtension.js new file mode 100644 index 0000000..b1d9610 --- /dev/null +++ b/src/front/utils/checkNostrExtension.js @@ -0,0 +1,9 @@ +function checkNostrExtension(window, successCallback, failureCallback) { + if (!window.nostr) { + failureCallback(); + } else { + successCallback(); + } +} + +module.exports = checkNostrExtension; From 8131de0c96b65976ab9b23301f5dbfc4cb8a9464 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 11:48:40 +0100 Subject: [PATCH 148/176] use util --- src/front/pages/login.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/front/pages/login.js b/src/front/pages/login.js index 121f3e1..752c376 100644 --- a/src/front/pages/login.js +++ b/src/front/pages/login.js @@ -1,12 +1,18 @@ +const checkNostrExtension = require('../utils/checkNostrExtension'); + const loginsFunction = () => { - 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'); - } + window.onload = () => { + checkNostrExtension( + window, + () => { + console.log('Nostr extension present'); + }, + () => { + console.log('Nostr extension not present'); + document.querySelector('#login-button').disabled = true; + document.querySelector('#no-extension-nudges').style.display = 'block'; + } + ); }; async function login() { From 545c54bf813e4eaaf27319dc1c95aa18fc4a5ffb Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 13 Mar 2025 15:36:00 +0100 Subject: [PATCH 149/176] stuff really --- src/constants.js | 7 ++ src/front/components/NostrSignUpButton.js | 0 src/front/pages/invite.js | 125 +++++++++++----------- src/front/services/signupService.js | 57 ++++++++++ src/views/invite.ejs | 17 +-- 5 files changed, 130 insertions(+), 76 deletions(-) create mode 100644 src/front/components/NostrSignUpButton.js create mode 100644 src/front/services/signupService.js diff --git a/src/constants.js b/src/constants.js index 8bec536..79c5699 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,7 +1,14 @@ 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', +}; module.exports = { DEFAULT_SESSION_DURATION_SECONDS, DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS, + API_PATHS, + DEFAULT_REDIRECT_DELAY, }; diff --git a/src/front/components/NostrSignUpButton.js b/src/front/components/NostrSignUpButton.js new file mode 100644 index 0000000..e69de29 diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index bca4e92..d8f1672 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -1,6 +1,63 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); +const signupService = require('../services/signupService'); +const constants = require('../../constants'); + +class NostrSignupButton { + constructor({ parentElement, id, onClickCallback }) { + this.element = null; + this.parentElement = parentElement; + this.id = id; + this.onClickCallback = onClickCallback; + } + + 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 = 'Alta con Nostr'; + + thisButton.appendChild(figure); + thisButton.appendChild(paragraph); + + thisButton.addEventListener('click', () => { + this.onClickCallback(); + }); + + this.element = thisButton; + this.parentElement.appendChild(this.element); + } + + disable() { + this.element.disabled = true; + } +} + +const nostrSignupArea = document.getElementById('nostr-signup-area'); +const noExtensionNudges = document.getElementById('no-extension-nudges'); +const signUpSuccessNotification = document.getElementById('sign-up-success'); const invitesFunction = () => { + const signupButton = new NostrSignupButton({ + parentElement: nostrSignupArea, + id: 'nostr-signup-button', + onClickCallback: () => { + acceptInvite(); + }, + }); + signupButton.render(); + window.onload = () => { checkNostrExtension( window, @@ -9,81 +66,27 @@ const invitesFunction = () => { }, () => { console.log('Nostr extension not present'); - document.querySelector('#nostr-signup-button').disabled = true; - document.querySelector('#no-extension-nudges').style.display = 'block'; + signupButton.disable(); + noExtensionNudges.style.display = 'block'; } ); }; - const signUpConfirmation = document.querySelector('#sign-up-success'); - function showConfirmationAndRedirect() { - signUpConfirmation.classList.add('revealed'); + signUpSuccessNotification.classList.add('revealed'); setTimeout(() => { - window.location.href = '/createProfile'; - }, 5000); + window.location.href = constants.API_PATHS.createProfile; + }, constants.DEFAULT_REDIRECT_DELAY); } async function acceptInvite() { - let challengeResponse; - try { - challengeResponse = await fetch('/api/signup/nostr-challenge', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - const { challenge } = await challengeResponse.json(); - - let pubkey; - try { - pubkey = await window.nostr.getPublicKey(); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - const event = { - kind: 22242, - created_at: Math.floor(Date.now() / 1000), - tags: [['challenge', challenge]], - content: 'Sign this challenge to authenticate', - pubkey: pubkey, - }; - - let signedEvent; - try { - signedEvent = await window.nostr.signEvent(event); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - - let verifyResponse; - try { - verifyResponse = await fetch('/api/signup/nostr-verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(signedEvent), - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } + const verifyResponse = + await signupService.requestAndRespondSignUpChallenge(); if (verifyResponse.ok) { showConfirmationAndRedirect(); } } - - const signUpButton = document.getElementById('nostr-signup-button'); - signUpButton.addEventListener('click', () => { - acceptInvite(); - }); }; invitesFunction(); diff --git a/src/front/services/signupService.js b/src/front/services/signupService.js new file mode 100644 index 0000000..03f2469 --- /dev/null +++ b/src/front/services/signupService.js @@ -0,0 +1,57 @@ +const requestAndRespondSignUpChallenge = async () => { + let challengeResponse; + try { + challengeResponse = await fetch('/api/signup/nostr-challenge', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + const { challenge } = await challengeResponse.json(); + + let pubkey; + try { + pubkey = await window.nostr.getPublicKey(); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + const event = { + kind: 22242, + created_at: Math.floor(Date.now() / 1000), + tags: [['challenge', challenge]], + content: 'Sign this challenge to authenticate', + pubkey: pubkey, + }; + + let signedEvent; + try { + signedEvent = await window.nostr.signEvent(event); + } catch (error) { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + return; + } + + let verifyResponse; + try { + verifyResponse = await fetch('/api/signup/nostr-verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(signedEvent), + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + return verifyResponse; +}; + +module.exports = { + requestAndRespondSignUpChallenge, +}; diff --git a/src/views/invite.ejs b/src/views/invite.ejs index d7d8f79..5319a26 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -21,21 +21,8 @@ />

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

-
- +
+
Date: Fri, 14 Mar 2025 16:14:01 +0100 Subject: [PATCH 150/176] warnings and stuff --- src/front/components/NostrSignUpButton.js | 0 src/front/components/NostrSignupButton.js | 42 ++++++++ src/front/pages/invite.js | 126 ++++++++++++++-------- src/views/invite.ejs | 48 +-------- 4 files changed, 128 insertions(+), 88 deletions(-) delete mode 100644 src/front/components/NostrSignUpButton.js create mode 100644 src/front/components/NostrSignupButton.js diff --git a/src/front/components/NostrSignUpButton.js b/src/front/components/NostrSignUpButton.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/front/components/NostrSignupButton.js b/src/front/components/NostrSignupButton.js new file mode 100644 index 0000000..8075a40 --- /dev/null +++ b/src/front/components/NostrSignupButton.js @@ -0,0 +1,42 @@ +class NostrSignupButton { + constructor({ parentElement, id, onClickCallback }) { + this.element = null; + this.parentElement = parentElement; + this.id = id; + this.onClickCallback = onClickCallback; + } + + 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 = 'Alta con Nostr'; + + thisButton.appendChild(figure); + thisButton.appendChild(paragraph); + + thisButton.addEventListener('click', () => { + this.onClickCallback(); + }); + + this.element = thisButton; + this.parentElement.appendChild(this.element); + } + + disable() { + this.element.disabled = true; + } +} +module.exports = NostrSignupButton; diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index d8f1672..22e2a82 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -1,53 +1,38 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); const signupService = require('../services/signupService'); const constants = require('../../constants'); +const NostrSignupButton = require('../components/NostrSignupButton'); -class NostrSignupButton { - constructor({ parentElement, id, onClickCallback }) { - this.element = null; - this.parentElement = parentElement; - this.id = id; - this.onClickCallback = onClickCallback; - } - - 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 = 'Alta con Nostr'; - - thisButton.appendChild(figure); - thisButton.appendChild(paragraph); - - thisButton.addEventListener('click', () => { - this.onClickCallback(); - }); - - this.element = thisButton; - this.parentElement.appendChild(this.element); - } - - disable() { - this.element.disabled = true; - } -} - +const warningsContainer = document.getElementById('warnings-container'); const nostrSignupArea = document.getElementById('nostr-signup-area'); const noExtensionNudges = document.getElementById('no-extension-nudges'); const signUpSuccessNotification = document.getElementById('sign-up-success'); +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'; + } +} + const invitesFunction = () => { const signupButton = new NostrSignupButton({ parentElement: nostrSignupArea, @@ -58,6 +43,61 @@ const invitesFunction = () => { }); 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(); + window.onload = () => { checkNostrExtension( window, @@ -67,7 +107,7 @@ const invitesFunction = () => { () => { console.log('Nostr extension not present'); signupButton.disable(); - noExtensionNudges.style.display = 'block'; + noExtensionWarning.display(); } ); }; diff --git a/src/views/invite.ejs b/src/views/invite.ejs index 5319a26..06c71e1 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -22,8 +22,8 @@

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

-
+
- + +

From 4c28cebdcef5ef1d1a2daef4e9f15930c9da626f Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 14 Mar 2025 16:24:53 +0100 Subject: [PATCH 151/176] oportunistic linting stuff --- .eslintignore | 1 + .../20250308000000-first-schema-tables.js | 2 - .../20250308000001-first-schema-fks.js | 220 +++++++++++------- src/front/pages/invite.js | 10 +- src/front/services/signupService.js | 6 +- src/views/invite.ejs | 17 +- 6 files changed, 143 insertions(+), 113 deletions(-) create mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..906a333 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +public/javascript/* \ No newline at end of file diff --git a/src/database/migrations/20250308000000-first-schema-tables.js b/src/database/migrations/20250308000000-first-schema-tables.js index c22bb65..3fb5cb9 100644 --- a/src/database/migrations/20250308000000-first-schema-tables.js +++ b/src/database/migrations/20250308000000-first-schema-tables.js @@ -1,7 +1,5 @@ 'use strict'; -const { query } = require('express'); - module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.sequelize.transaction((t) => { diff --git a/src/database/migrations/20250308000001-first-schema-fks.js b/src/database/migrations/20250308000001-first-schema-fks.js index c79cad7..2052b0a 100644 --- a/src/database/migrations/20250308000001-first-schema-fks.js +++ b/src/database/migrations/20250308000001-first-schema-fks.js @@ -3,106 +3,146 @@ 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', + queryInterface.addConstraint( + 'login_challenge_created', + { + fields: ['nostr_challenge_uuid'], + type: 'foreign key', + references: { + table: 'nostr_challenge_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('nostr_challenge_completed', { - fields: ['challenge'], - type: 'foreign key', - references: { - table: 'nostr_challenge_created', - field: 'challenge', + { transaction: t } + ), + queryInterface.addConstraint( + 'nostr_challenge_completed', + { + fields: ['challenge'], + type: 'foreign key', + references: { + table: 'nostr_challenge_created', + field: 'challenge', + }, + onDelete: 'cascade', + onUpdate: 'cascade', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('login_challenge_completed', { - fields: ['nostr_challenge_completed_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_completed', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('offer_deleted', { - fields: ['offer_uuid'], - type: 'foreign key', - references: { - table: 'offer_created', - field: 'uuid', + { transaction: t } + ), + queryInterface.addConstraint( + 'offer_deleted', + { + fields: ['offer_uuid'], + type: 'foreign key', + references: { + table: 'offer_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('offer_details_set', { - fields: ['offer_uuid'], - type: 'foreign key', - references: { - table: 'offer_created', - field: 'uuid', + { transaction: t } + ), + queryInterface.addConstraint( + 'offer_details_set', + { + fields: ['offer_uuid'], + type: 'foreign key', + references: { + table: 'offer_created', + field: 'uuid', + }, + onDelete: 'cascade', + onUpdate: 'cascade', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('session_related_to_public_key', { - fields: ['session_uuid'], - type: 'foreign key', - references: { - table: 'session_created', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('sign_up_challenge_created', { - fields: ['nostr_challenge_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_created', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('sign_up_challenge_created', { - fields: ['app_invite_uuid'], - type: 'foreign key', - references: { - table: 'app_invite_created', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('sign_up_challenge_completed', { - fields: ['nostr_challenge_completed_uuid'], - type: 'foreign key', - references: { - table: 'nostr_challenge_completed', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), - queryInterface.addConstraint('sign_up_challenge_completed', { - fields: ['app_invite_uuid'], - type: 'foreign key', - references: { - table: 'app_invite_created', - field: 'uuid', + { 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', }, - onDelete: 'cascade', - onUpdate: 'cascade', - }), + { transaction: t } + ), ]); }); }, diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index 22e2a82..5a7678c 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -5,7 +5,6 @@ const NostrSignupButton = require('../components/NostrSignupButton'); const warningsContainer = document.getElementById('warnings-container'); const nostrSignupArea = document.getElementById('nostr-signup-area'); -const noExtensionNudges = document.getElementById('no-extension-nudges'); const signUpSuccessNotification = document.getElementById('sign-up-success'); class WarningDiv { @@ -120,8 +119,13 @@ const invitesFunction = () => { } async function acceptInvite() { - const verifyResponse = - await signupService.requestAndRespondSignUpChallenge(); + const verifyResponse = await signupService.requestAndRespondSignUpChallenge( + { + onNostrErrorCallback: () => { + rejectedNostrWarning.display(); + }, + } + ); if (verifyResponse.ok) { showConfirmationAndRedirect(); diff --git a/src/front/services/signupService.js b/src/front/services/signupService.js index 03f2469..d494c6c 100644 --- a/src/front/services/signupService.js +++ b/src/front/services/signupService.js @@ -1,4 +1,4 @@ -const requestAndRespondSignUpChallenge = async () => { +const requestAndRespondSignUpChallenge = async ({ onNostrErrorCallback }) => { let challengeResponse; try { challengeResponse = await fetch('/api/signup/nostr-challenge', { @@ -18,7 +18,7 @@ const requestAndRespondSignUpChallenge = async () => { try { pubkey = await window.nostr.getPublicKey(); } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + onNostrErrorCallback(); return; } const event = { @@ -33,7 +33,7 @@ const requestAndRespondSignUpChallenge = async () => { try { signedEvent = await window.nostr.signEvent(event); } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + onNostrErrorCallback(); return; } diff --git a/src/views/invite.ejs b/src/views/invite.ejs index 06c71e1..ad4a406 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -21,21 +21,8 @@ />

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

-
-
-
- - -
+
+

From 29a4c0ca694451a98dc0d88c78bf0aa5fb9d8652 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 14 Mar 2025 16:57:55 +0100 Subject: [PATCH 152/176] popup component --- src/front/components/PopupNotification.js | 25 +++++++++++++ src/front/components/WarningDiv.js | 25 +++++++++++++ src/front/pages/invite.js | 45 +++++++---------------- src/views/invite.ejs | 8 +--- 4 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 src/front/components/PopupNotification.js create mode 100644 src/front/components/WarningDiv.js diff --git a/src/front/components/PopupNotification.js b/src/front/components/PopupNotification.js new file mode 100644 index 0000000..e2a0588 --- /dev/null +++ b/src/front/components/PopupNotification.js @@ -0,0 +1,25 @@ +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/WarningDiv.js b/src/front/components/WarningDiv.js new file mode 100644 index 0000000..ab4fa93 --- /dev/null +++ b/src/front/components/WarningDiv.js @@ -0,0 +1,25 @@ +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/pages/invite.js b/src/front/pages/invite.js index 5a7678c..e67ca9c 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -2,37 +2,14 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); const signupService = require('../services/signupService'); const constants = require('../../constants'); const NostrSignupButton = require('../components/NostrSignupButton'); - -const warningsContainer = document.getElementById('warnings-container'); -const nostrSignupArea = document.getElementById('nostr-signup-area'); -const signUpSuccessNotification = document.getElementById('sign-up-success'); - -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'; - } -} +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', @@ -94,9 +71,15 @@ const invitesFunction = () => { 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, @@ -112,7 +95,7 @@ const invitesFunction = () => { }; function showConfirmationAndRedirect() { - signUpSuccessNotification.classList.add('revealed'); + signUpSuccessPopup.display(); setTimeout(() => { window.location.href = constants.API_PATHS.createProfile; }, constants.DEFAULT_REDIRECT_DELAY); diff --git a/src/views/invite.ejs b/src/views/invite.ejs index ad4a406..d6ddaea 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -23,13 +23,7 @@

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

-
- -

- ¡Bien! Hemos dado de alta tu clave de Nostr. Te vamos a redirigir - a la seca, espera un momento. -

-
+

¿No tienes cuenta de Nostr?

Date: Fri, 14 Mar 2025 17:03:29 +0100 Subject: [PATCH 153/176] inline useless functions --- src/front/pages/invite.js | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index e67ca9c..f79e7e6 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -13,8 +13,20 @@ const invitesFunction = () => { const signupButton = new NostrSignupButton({ parentElement: nostrSignupArea, id: 'nostr-signup-button', - onClickCallback: () => { - acceptInvite(); + onClickCallback: async () => { + const verifyResponse = + await signupService.requestAndRespondSignUpChallenge({ + onNostrErrorCallback: () => { + rejectedNostrWarning.display(); + }, + }); + + if (verifyResponse.ok) { + signUpSuccessPopup.display(); + setTimeout(() => { + window.location.href = constants.API_PATHS.createProfile; + }, constants.DEFAULT_REDIRECT_DELAY); + } }, }); signupButton.render(); @@ -93,27 +105,6 @@ const invitesFunction = () => { } ); }; - - function showConfirmationAndRedirect() { - signUpSuccessPopup.display(); - setTimeout(() => { - window.location.href = constants.API_PATHS.createProfile; - }, constants.DEFAULT_REDIRECT_DELAY); - } - - async function acceptInvite() { - const verifyResponse = await signupService.requestAndRespondSignUpChallenge( - { - onNostrErrorCallback: () => { - rejectedNostrWarning.display(); - }, - } - ); - - if (verifyResponse.ok) { - showConfirmationAndRedirect(); - } - } }; invitesFunction(); From 8ae4fddd12e0cde929484b15c9f77a407315194c Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 14 Mar 2025 18:28:53 +0100 Subject: [PATCH 154/176] refactor name --- src/front/pages/invite.js | 4 ++-- src/front/services/{signupService.js => inviteService.js} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/front/services/{signupService.js => inviteService.js} (100%) diff --git a/src/front/pages/invite.js b/src/front/pages/invite.js index f79e7e6..d795db7 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -1,5 +1,5 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); -const signupService = require('../services/signupService'); +const inviteService = require('../services/inviteService'); const constants = require('../../constants'); const NostrSignupButton = require('../components/NostrSignupButton'); const WarningDiv = require('../components/WarningDiv'); @@ -15,7 +15,7 @@ const invitesFunction = () => { id: 'nostr-signup-button', onClickCallback: async () => { const verifyResponse = - await signupService.requestAndRespondSignUpChallenge({ + await inviteService.requestAndRespondSignUpChallenge({ onNostrErrorCallback: () => { rejectedNostrWarning.display(); }, diff --git a/src/front/services/signupService.js b/src/front/services/inviteService.js similarity index 100% rename from src/front/services/signupService.js rename to src/front/services/inviteService.js From add7891e94a23d2d01fe845aeaafeef185d9b641 Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 14 Mar 2025 18:52:00 +0100 Subject: [PATCH 155/176] login service --- src/front/pages/login.js | 60 +++++---------------------- src/front/services/loginService.js | 65 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 49 deletions(-) create mode 100644 src/front/services/loginService.js diff --git a/src/front/pages/login.js b/src/front/pages/login.js index 752c376..cf5fb92 100644 --- a/src/front/pages/login.js +++ b/src/front/pages/login.js @@ -1,6 +1,9 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); +const loginService = require('../services/loginService'); const loginsFunction = () => { + const rejectedNostrWarning = document.querySelector('#rejected-nostr-nudges'); + window.onload = () => { checkNostrExtension( window, @@ -16,55 +19,14 @@ const loginsFunction = () => { }; async function login() { - let challengeResponse; - try { - challengeResponse = await fetch('/api/login/nostr-challenge', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } - - const { challenge } = await challengeResponse.json(); - - let pubkey; - try { - pubkey = await window.nostr.getPublicKey(); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - const event = { - kind: 22242, - created_at: Math.floor(Date.now() / 1000), - tags: [['challenge', challenge]], - content: 'Sign this challenge to authenticate', - pubkey: pubkey, - }; - - let signedEvent; - try { - signedEvent = await window.nostr.signEvent(event); - } catch (error) { - document.querySelector('#rejected-nostr-nudges').style.display = 'block'; - return; - } - - let verifyResponse; - try { - verifyResponse = await fetch('/api/login/nostr-verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(signedEvent), - }); - } catch (error) { - console.log(`Something went wrong: ${error}`); - return; - } + const verifyResponse = await loginService.requestAndRespondLoginChallenge({ + onRejectedPubKeyCallback: () => { + rejectedNostrWarning.style.display = 'block'; + }, + onRejectedSignatureCallback: () => { + rejectedNostrWarning.style.display = 'block'; + }, + }); if (verifyResponse.status === 403) { document.querySelector('#rejected-public-key').style.display = 'block'; diff --git a/src/front/services/loginService.js b/src/front/services/loginService.js new file mode 100644 index 0000000..d1f5815 --- /dev/null +++ b/src/front/services/loginService.js @@ -0,0 +1,65 @@ +const requestAndRespondLoginChallenge = async ({ + onRejectedPubKeyCallback, + onRejectedSignatureCallback, +}) => { + onRejectedPubKeyCallback = () => { + document.querySelector('#rejected-nostr-nudges').style.display = 'block'; + }; + onRejectedSignatureCallback = onRejectedPubKeyCallback; + + let challengeResponse; + try { + challengeResponse = await fetch('/api/login/nostr-challenge', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + const { challenge } = await challengeResponse.json(); + + let pubkey; + try { + pubkey = await window.nostr.getPublicKey(); + } catch (error) { + onRejectedPubKeyCallback(); + return; + } + const event = { + kind: 22242, + created_at: Math.floor(Date.now() / 1000), + tags: [['challenge', challenge]], + content: 'Sign this challenge to authenticate', + pubkey: pubkey, + }; + + let signedEvent; + try { + signedEvent = await window.nostr.signEvent(event); + } catch (error) { + onRejectedSignatureCallback(); + return; + } + + let verifyResponse; + try { + verifyResponse = await fetch('/api/login/nostr-verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(signedEvent), + }); + } catch (error) { + console.log(`Something went wrong: ${error}`); + return; + } + + return verifyResponse; +}; + +module.exports = { + requestAndRespondLoginChallenge, +}; From 4006523c8cec433367aa72b8ece1152124574e3f Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 12:46:55 +0100 Subject: [PATCH 156/176] lots of stuff --- src/constants.js | 1 + .../{NostrSignupButton.js => nostrButtons.js} | 31 +++- src/front/pages/invite.js | 12 +- src/front/pages/login.js | 150 +++++++++++++----- src/front/utils/checkNostrExtension.js | 2 +- src/views/login.ejs | 76 +-------- 6 files changed, 152 insertions(+), 120 deletions(-) rename src/front/components/{NostrSignupButton.js => nostrButtons.js} (59%) diff --git a/src/constants.js b/src/constants.js index 79c5699..7af448b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -4,6 +4,7 @@ const DEFAULT_REDIRECT_DELAY = 3 * 1000; // 3seconds times milliseconds; const API_PATHS = { createProfile: '/createProfile', + home: '/home', }; module.exports = { diff --git a/src/front/components/NostrSignupButton.js b/src/front/components/nostrButtons.js similarity index 59% rename from src/front/components/NostrSignupButton.js rename to src/front/components/nostrButtons.js index 8075a40..31513f5 100644 --- a/src/front/components/NostrSignupButton.js +++ b/src/front/components/nostrButtons.js @@ -1,9 +1,10 @@ -class NostrSignupButton { - constructor({ parentElement, id, onClickCallback }) { +class NostrButton { + constructor({ parentElement, id, onClickCallback, buttonText }) { this.element = null; this.parentElement = parentElement; this.id = id; this.onClickCallback = onClickCallback; + this.buttonText = buttonText; } render() { @@ -22,7 +23,7 @@ class NostrSignupButton { figure.appendChild(img); const paragraph = document.createElement('p'); - paragraph.textContent = 'Alta con Nostr'; + paragraph.textContent = this.buttonText; thisButton.appendChild(figure); thisButton.appendChild(paragraph); @@ -36,7 +37,27 @@ class NostrSignupButton { } disable() { - this.element.disabled = true; + if (this.element) { + this.element.disabled = true; + } } } -module.exports = NostrSignupButton; + +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/invite.js b/src/front/pages/invite.js index d795db7..4b58b37 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -1,7 +1,7 @@ const checkNostrExtension = require('../utils/checkNostrExtension'); const inviteService = require('../services/inviteService'); const constants = require('../../constants'); -const NostrSignupButton = require('../components/NostrSignupButton'); +const { NostrSignupButton } = require('../components/nostrButtons'); const WarningDiv = require('../components/WarningDiv'); const PopupNotification = require('../components/PopupNotification'); @@ -93,17 +93,17 @@ const invitesFunction = () => { signUpSuccessPopup.render(); window.onload = () => { - checkNostrExtension( + checkNostrExtension({ window, - () => { + successCallback: () => { console.log('Nostr extension present'); }, - () => { + : () => { console.log('Nostr extension not present'); signupButton.disable(); noExtensionWarning.display(); - } - ); + }, + }); }; }; diff --git a/src/front/pages/login.js b/src/front/pages/login.js index cf5fb92..5620a99 100644 --- a/src/front/pages/login.js +++ b/src/front/pages/login.js @@ -1,49 +1,127 @@ 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 loginsFunction = () => { - const rejectedNostrWarning = document.querySelector('#rejected-nostr-nudges'); +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( + checkNostrExtension({ window, - () => { + successCallback: () => { console.log('Nostr extension present'); }, - () => { + failureCallback: () => { console.log('Nostr extension not present'); - document.querySelector('#login-button').disabled = true; - document.querySelector('#no-extension-nudges').style.display = 'block'; - } - ); - }; - - async function login() { - const verifyResponse = await loginService.requestAndRespondLoginChallenge({ - onRejectedPubKeyCallback: () => { - rejectedNostrWarning.style.display = 'block'; - }, - onRejectedSignatureCallback: () => { - rejectedNostrWarning.style.display = 'block'; + nostrLoginButton.disable(); + noExtensionWarning.display(); }, }); - - if (verifyResponse.status === 403) { - document.querySelector('#rejected-public-key').style.display = 'block'; - } - - if (verifyResponse.ok) { - document.querySelector('#sign-up-success').style.display = 'block'; - setTimeout(() => { - window.location.href = '/home'; - }, 1000); - } - } - - const loginButton = document.getElementById('login-button'); - loginButton.addEventListener('click', () => { - login(); - }); + }; }; -loginsFunction(); +loginFunction(); diff --git a/src/front/utils/checkNostrExtension.js b/src/front/utils/checkNostrExtension.js index b1d9610..b16d293 100644 --- a/src/front/utils/checkNostrExtension.js +++ b/src/front/utils/checkNostrExtension.js @@ -1,4 +1,4 @@ -function checkNostrExtension(window, successCallback, failureCallback) { +function checkNostrExtension({ window, successCallback, failureCallback }) { if (!window.nostr) { failureCallback(); } else { diff --git a/src/views/login.ejs b/src/views/login.ejs index a954697..2cf9dcb 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -1,7 +1,7 @@ - Hello World + @@ -13,78 +13,10 @@
-
- -
- - - - From 2b7b761737f2d37018268e370b032b706edc5bde Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 12:53:44 +0100 Subject: [PATCH 157/176] styles and typo --- public/css/login.css | 39 +++++++++++++++++++++++++++++++++++++++ src/front/pages/invite.js | 2 +- src/views/login.ejs | 4 +++- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 public/css/login.css diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 0000000..f457888 --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,39 @@ +@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/src/front/pages/invite.js b/src/front/pages/invite.js index 4b58b37..24bc3b5 100644 --- a/src/front/pages/invite.js +++ b/src/front/pages/invite.js @@ -98,7 +98,7 @@ const invitesFunction = () => { successCallback: () => { console.log('Nostr extension present'); }, - : () => { + failureCallback: () => { console.log('Nostr extension not present'); signupButton.disable(); noExtensionWarning.display(); diff --git a/src/views/login.ejs b/src/views/login.ejs index 2cf9dcb..d2870e6 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -5,11 +5,12 @@ +
-

Bienvenido a la seca

+
@@ -18,6 +19,7 @@
+
From 4ee00edb0449f942c63a05f999300e1fb95badd5 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 14:56:51 +0100 Subject: [PATCH 158/176] extract out function --- src/front/pages/offers.js | 6 ++---- src/front/utils/formatNumbersWithSpaces.js | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 src/front/utils/formatNumbersWithSpaces.js diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js index b0954d3..b7a271c 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,8 +1,6 @@ -function offersPage() { - function formatNumberWithSpaces(num) { - return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); - } +const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); +function offersPage() { const navbuttonHome = document.getElementById('navbutton-home'); const navbuttonOffers = document.getElementById('navbutton-offers'); diff --git a/src/front/utils/formatNumbersWithSpaces.js b/src/front/utils/formatNumbersWithSpaces.js new file mode 100644 index 0000000..1d1ee44 --- /dev/null +++ b/src/front/utils/formatNumbersWithSpaces.js @@ -0,0 +1,5 @@ +function formatNumberWithSpaces(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); +} + +module.exports = formatNumberWithSpaces; From c82fc895b726dbc5fc202d0e380306381449cab4 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 15:26:57 +0100 Subject: [PATCH 159/176] button extracted --- src/front/pages/offers.js | 40 +++++++++++++++++++++++++++++++-------- src/views/offers.ejs | 7 ------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js index b7a271c..54d72a5 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,6 +1,38 @@ const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); +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); + } +} + function offersPage() { + 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 navbuttonHome = document.getElementById('navbutton-home'); const navbuttonOffers = document.getElementById('navbutton-offers'); @@ -58,8 +90,6 @@ function offersPage() { 'large-bills-checkbox' ); - const publishOfferButton = document.getElementById('button-submit-offer'); - const offerCreatedPopup = document.getElementById( 'offer-created-confirmation' ); @@ -696,12 +726,6 @@ function offersPage() { applyTrustCheckboxConstraints(allMembersCheckbox); }); - publishOfferButton.addEventListener('click', async () => { - await publishOffer(); - await myOffers.getOffersFromApi(); - await myOffers.render(); - }); - updateBtcInput(); const myOffers = new MyOffers(ownOffersContainer); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 2c99fe7..d492002 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -179,12 +179,6 @@
-
- <%- include("partials/appCommonScripts") %> From 47c50ad07834f4970a0438c14bf9e9dbfd0aed07 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 15:28:44 +0100 Subject: [PATCH 160/176] move to different file --- src/front/components/PublishOfferButton.js | 21 +++++++++++++++++++++ src/front/pages/offers.js | 21 +-------------------- 2 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 src/front/components/PublishOfferButton.js diff --git a/src/front/components/PublishOfferButton.js b/src/front/components/PublishOfferButton.js new file mode 100644 index 0000000..d43f8c1 --- /dev/null +++ b/src/front/components/PublishOfferButton.js @@ -0,0 +1,21 @@ +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/pages/offers.js b/src/front/pages/offers.js index 54d72a5..65a848c 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,24 +1,5 @@ const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); - -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); - } -} +const PublishOfferButton = require('../components/PublishOfferButton'); function offersPage() { const publishOfferButton = new PublishOfferButton({ From ceba684d77f10e179c4eb7ffba052ff34491be69 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 16:36:31 +0100 Subject: [PATCH 161/176] buy or sell button group --- src/front/components/BuyOrSellButtonGroup.js | 56 ++++++++++++++++++++ src/front/pages/offers.js | 37 +++---------- src/views/offers.ejs | 11 ---- 3 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 src/front/components/BuyOrSellButtonGroup.js diff --git a/src/front/components/BuyOrSellButtonGroup.js b/src/front/components/BuyOrSellButtonGroup.js new file mode 100644 index 0000000..ed03c73 --- /dev/null +++ b/src/front/components/BuyOrSellButtonGroup.js @@ -0,0 +1,56 @@ +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'; + } + } +} diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js index 65a848c..b6d4e2f 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,5 +1,6 @@ const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); const PublishOfferButton = require('../components/PublishOfferButton'); +const BuyOrSellButtonGroup = require('../components/BuyOrSellButtonGroup'); function offersPage() { const publishOfferButton = new PublishOfferButton({ @@ -13,6 +14,12 @@ function offersPage() { }); publishOfferButton.render(); + const buyOrSellButtonGroup = new BuyOrSellButtonGroup({ + parentElement: document.getElementById('buy-or-sell-area'), + id: 'button-group-buy-or-sell', + }); + buyOrSellButtonGroup.render(); + // ----------- const navbuttonHome = document.getElementById('navbutton-home'); const navbuttonOffers = document.getElementById('navbutton-offers'); @@ -34,12 +41,6 @@ function offersPage() { 'create-offer-modal-root' ); const viewMyOffersRoot = document.getElementById('view-my-offers-root'); - 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( @@ -98,16 +99,6 @@ function offersPage() { 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]+)/; @@ -168,13 +159,7 @@ function offersPage() { } async function publishOffer() { - let wants; - if (buyButton.classList.contains('selected')) { - wants = 'BTC'; - } - if (sellButton.classList.contains('selected')) { - wants = 'EUR'; - } + const wants = buyOrSellButtonGroup.wants(); const premium = parseInt(premiumValue.innerText.match(/\d+/)[0]) / 100; const trade_amount_eur = eurAmountInput.value; @@ -669,12 +654,6 @@ function offersPage() { toggleCreateOfferModal(); }); - buyOrSellButtons.forEach((button) => { - button.addEventListener('click', () => { - toggleBuyOrSellButtonGroup(); - }); - }); - buttonIncreasePremium.addEventListener('click', () => { modifyPremiumValue(1); }); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index d492002..81a1664 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -36,17 +36,6 @@

Añade los detalles de tu oferta

-
- -

Premium

From 1da32520612fc54912f51f4700ea2053ff944d11 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 16:36:50 +0100 Subject: [PATCH 162/176] missing export --- src/front/components/BuyOrSellButtonGroup.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/front/components/BuyOrSellButtonGroup.js b/src/front/components/BuyOrSellButtonGroup.js index ed03c73..d563e54 100644 --- a/src/front/components/BuyOrSellButtonGroup.js +++ b/src/front/components/BuyOrSellButtonGroup.js @@ -54,3 +54,5 @@ class BuyOrSellButtonGroup { } } } + +module.exports = BuyOrSellButtonGroup; From b8579a5370b46825a15f37ce01647be129fe1d3f Mon Sep 17 00:00:00 2001 From: counterweight Date: Sat, 15 Mar 2025 17:28:48 +0100 Subject: [PATCH 163/176] premium selector --- public/css/seca.css | 2 +- src/front/components/PremiumSelector.js | 66 +++++++++++++++++++++++++ src/front/pages/offers.js | 35 +++---------- src/views/offers.ejs | 11 +---- 4 files changed, 76 insertions(+), 38 deletions(-) create mode 100644 src/front/components/PremiumSelector.js diff --git a/public/css/seca.css b/public/css/seca.css index 23b4802..1cc3034 100644 --- a/public/css/seca.css +++ b/public/css/seca.css @@ -117,7 +117,7 @@ h1 { } .full-screen-modal-background { - position: absolute; + position: fixed; width: 100%; height: 100%; top: 0; diff --git a/src/front/components/PremiumSelector.js b/src/front/components/PremiumSelector.js new file mode 100644 index 0000000..f098f89 --- /dev/null +++ b/src/front/components/PremiumSelector.js @@ -0,0 +1,66 @@ +class PremiumSelector { + constructor({ parentElement, id }) { + this.element = null; + this.parentElement = parentElement; + this.id = id; + + 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; + } + + getPremium() { + return parseInt(this.premiumValue.innerText.match(/-?\d+/)[0]) / 100; + } +} + +module.exports = PremiumSelector; diff --git a/src/front/pages/offers.js b/src/front/pages/offers.js index b6d4e2f..8fb4426 100644 --- a/src/front/pages/offers.js +++ b/src/front/pages/offers.js @@ -1,6 +1,7 @@ const formatNumberWithSpaces = require('../utils/formatNumbersWithSpaces'); const PublishOfferButton = require('../components/PublishOfferButton'); const BuyOrSellButtonGroup = require('../components/BuyOrSellButtonGroup'); +const PremiumSelector = require('../components/PremiumSelector'); function offersPage() { const publishOfferButton = new PublishOfferButton({ @@ -20,6 +21,12 @@ function offersPage() { }); buyOrSellButtonGroup.render(); + const premiumSelector = new PremiumSelector({ + parentElement: document.getElementById('premium-content-area'), + id: 'premium-selector-area', + }); + premiumSelector.render(); + // ----------- const navbuttonHome = document.getElementById('navbutton-home'); const navbuttonOffers = document.getElementById('navbutton-offers'); @@ -42,15 +49,6 @@ function offersPage() { ); const viewMyOffersRoot = document.getElementById('view-my-offers-root'); - 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'); @@ -90,15 +88,6 @@ function offersPage() { viewMyOffersRoot.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 readIntFromEurAmountInput() { const eurAmountFieldValue = eurAmountInput.value; const regularExpression = /([\d\s]+)/; @@ -161,7 +150,7 @@ function offersPage() { async function publishOffer() { const wants = buyOrSellButtonGroup.wants(); - const premium = parseInt(premiumValue.innerText.match(/\d+/)[0]) / 100; + const premium = premiumSelector.getPremium(); const trade_amount_eur = eurAmountInput.value; const location_details = placeInput.value; const time_availability_details = timeInput.value; @@ -654,14 +643,6 @@ function offersPage() { toggleCreateOfferModal(); }); - buttonIncreasePremium.addEventListener('click', () => { - modifyPremiumValue(1); - }); - - buttonDecreasePremium.addEventListener('click', () => { - modifyPremiumValue(-1); - }); - eurAmountInput.addEventListener('blur', () => { validateAndFormatEurAmountInput(); updateBtcInput(); diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 81a1664..46d9e3c 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -40,16 +40,7 @@

Premium

-
-
0%
-
- -
-
+

Tu precio: 90 000€/BTC From bc3ed21da4a83b3cc1a8e37f7e24cf90ad2a4b08 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 17 Mar 2025 16:01:02 +0100 Subject: [PATCH 164/176] a few small notes --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index de7c287..6098e02 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,16 @@ 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`. From 536408482b6bb63500cd5d52fa4f534523214878 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Mon, 17 Mar 2025 16:23:12 +0100 Subject: [PATCH 165/176] formatting --- src/views/invite.ejs | 2 +- src/views/login.ejs | 12 +++++------- src/views/offers.ejs | 7 ++----- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/views/invite.ejs b/src/views/invite.ejs index d6ddaea..b7815ab 100644 --- a/src/views/invite.ejs +++ b/src/views/invite.ejs @@ -23,7 +23,7 @@

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

- +

¿No tienes cuenta de Nostr?

diff --git a/src/views/offers.ejs b/src/views/offers.ejs index 46d9e3c..ef55506 100644 --- a/src/views/offers.ejs +++ b/src/views/offers.ejs @@ -35,12 +35,10 @@

Añade los detalles de tu oferta

-
-
+

Premium

-

Tu precio: 90 000€/BTC @@ -158,8 +156,7 @@ >

-
-
+