From 9c010e78657012167a13293f274d66a880363fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Arturo=20Cabral=20Mej=C3=ADa?= Date: Fri, 27 Jan 2023 10:20:55 -0500 Subject: [PATCH] fix: lots of bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ricardo Arturo Cabral Mejía --- Caddyfile.local | 2 +- docker-compose.local.yml | 4 +-- docker-compose.tor.yml | 2 +- docker-compose.yml | 2 -- src/@types/adapters.ts | 3 ++- src/@types/base.ts | 2 +- src/@types/settings.ts | 2 +- src/adapters/web-server-adapter.ts | 13 ++++++++-- src/adapters/web-socket-server-adapter.ts | 25 ++++++++++--------- src/app/worker.ts | 4 +-- src/factories/maintenance-worker-factory.ts | 7 +++--- src/factories/payments-service-factory.ts | 7 +++--- .../post-invoice-controller-factory.ts | 7 +++--- .../zebedee-callback-controller-factory.ts | 7 +++--- .../rate-limiter-middleware.ts | 4 +++ src/repositories/event-repository.ts | 4 +-- src/services/payments-service.ts | 4 +-- src/utils/settings.ts | 6 ++++- 18 files changed, 62 insertions(+), 43 deletions(-) diff --git a/Caddyfile.local b/Caddyfile.local index 020238d..b294367 100644 --- a/Caddyfile.local +++ b/Caddyfile.local @@ -5,5 +5,5 @@ nostream.localtest.me { tls /root/certs/nostream.localtest.me.pem /root/certs/nostream.localtest.me-key.pem - reverse_proxy nostr-ts-relay:8008 + reverse_proxy nostream:8008 } diff --git a/docker-compose.local.yml b/docker-compose.local.yml index b5db73d..9fff116 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,7 +1,7 @@ services: - relay: + nostream: volumes: - - ${PWD}/.nostr.local:/home/node/ + - ${PWD}/.nostr.local:/home/node caddy: image: caddy:2.6.2-alpine container_name: caddy diff --git a/docker-compose.tor.yml b/docker-compose.tor.yml index dffde4e..99db939 100644 --- a/docker-compose.tor.yml +++ b/docker-compose.tor.yml @@ -4,7 +4,7 @@ services: container_name: tor user: toruser depends_on: - - relay + - nostream volumes: - ${PWD}/tor/torrc:/etc/tor/torrc - ${PWD}/.nostr/tor/data:/var/lib/tor diff --git a/docker-compose.yml b/docker-compose.yml index 0098630..ad2900f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,8 +38,6 @@ services: # DEBUG: "primary:*" # DEBUG: "worker:*" # DEBUG: "knex:query" - env_file: - - test.env user: node:node volumes: - ${PWD}/.nostr:/home/node/.nostr diff --git a/src/@types/adapters.ts b/src/@types/adapters.ts index eef29e6..0e491c3 100644 --- a/src/@types/adapters.ts +++ b/src/@types/adapters.ts @@ -3,11 +3,12 @@ import { SubscriptionFilter } from './subscription' export interface IWebSocketServerAdapter extends EventEmitter, IWebServerAdapter { getConnectedClients(): number - close(callback: () => void): void + close(callback?: () => void): void } export interface IWebServerAdapter extends EventEmitter { listen(port: number): void + close(callback?: () => void): void } diff --git a/src/@types/base.ts b/src/@types/base.ts index 8b4b515..24ac07d 100644 --- a/src/@types/base.ts +++ b/src/@types/base.ts @@ -28,7 +28,7 @@ export type Factory = (input: TInput) => TOutput export type DatabaseClient = Knex -export type DatabaseTransaction = Knex.Transaction +export type DatabaseTransaction = any> = Knex.Transaction export interface ContextMetadata { remoteAddress: SocketAddress diff --git a/src/@types/settings.ts b/src/@types/settings.ts index 93a88ec..cd19855 100644 --- a/src/@types/settings.ts +++ b/src/@types/settings.ts @@ -154,7 +154,7 @@ export interface PaymentsProcessors { export interface Settings { info: Info payments?: Payments - paymentProcessors?: PaymentsProcessors + paymentsProcessors?: PaymentsProcessors network: Network workers?: Worker limits?: Limits diff --git a/src/adapters/web-server-adapter.ts b/src/adapters/web-server-adapter.ts index 4f49df7..685b2ad 100644 --- a/src/adapters/web-server-adapter.ts +++ b/src/adapters/web-server-adapter.ts @@ -10,7 +10,7 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter public constructor( protected readonly webServer: Server, ) { - debug('web server starting') + debug('created') super() this.webServer .on('error', this.onError.bind(this)) @@ -40,8 +40,17 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter socket.end('HTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\n') } + public close(callback?: () => void): void { + this.webServer.removeAllListeners() + this.webServer.close() + if (typeof callback !== 'undefined') { + callback() + } + debug('closed') + } + protected onClose() { debug('stopped listening to incoming connections') - this.webServer.removeAllListeners() + this.close() } } diff --git a/src/adapters/web-socket-server-adapter.ts b/src/adapters/web-socket-server-adapter.ts index d5d0093..d57578d 100644 --- a/src/adapters/web-socket-server-adapter.ts +++ b/src/adapters/web-socket-server-adapter.ts @@ -30,6 +30,7 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock >, private readonly settings: () => Settings, ) { + debug('created') super(webServer) this.webSocketsAdapters = new WeakMap() @@ -46,11 +47,20 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock this.heartbeatInterval = setInterval(this.onHeartbeat.bind(this), WSS_CLIENT_HEALTH_PROBE_INTERVAL) } - public close(callback: () => void): void { - this.onClose() + public close(callback?: () => void): void { + debug('closing') + clearInterval(this.heartbeatInterval) + this.webSocketServer.clients.forEach((webSocket: WebSocket) => { + debug('terminating client') + webSocket.terminate() + }) + this.removeAllListeners() + this.webSocketServer.removeAllListeners() this.webSocketServer.close(() => { this.webServer.close(callback) + super.close() }) + debug('closed') } private onBroadcast(event: Event) { @@ -90,15 +100,6 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock } protected onClose() { - debug('closing') - clearInterval(this.heartbeatInterval) - this.webSocketServer.clients.forEach((webSocket: WebSocket) => { - debug('terminating client') - webSocket.terminate() - }) - this.removeAllListeners() - this.webSocketServer.removeAllListeners() - super.onClose() - debug('closed') + this.close() } } diff --git a/src/app/worker.ts b/src/app/worker.ts index 6cba6ec..eeb49dc 100644 --- a/src/app/worker.ts +++ b/src/app/worker.ts @@ -57,8 +57,6 @@ export class AppWorker implements IRunnable { watcher.close() } } - if (typeof callback !== 'undefined') { - this.adapter.close(callback) - } + this.adapter.close(callback) } } diff --git a/src/factories/maintenance-worker-factory.ts b/src/factories/maintenance-worker-factory.ts index fddb418..2436c99 100644 --- a/src/factories/maintenance-worker-factory.ts +++ b/src/factories/maintenance-worker-factory.ts @@ -1,18 +1,19 @@ +import { getMasterDbClient, getReadReplicaDbClient } from '../database/client' import { createPaymentsProcessor } from './payments-processor-factory' import { createSettings } from './settings-factory' import { EventRepository } from '../repositories/event-repository' -import { getDbClient } from '../database/client' import { InvoiceRepository } from '../repositories/invoice-repository' import { MaintenanceWorker } from '../app/maintenance-worker' import { PaymentsService } from '../services/payments-service' import { UserRepository } from '../repositories/user-repository' export const maintenanceWorkerFactory = () => { - const dbClient = getDbClient() + const dbClient = getMasterDbClient() + const rrDbClient = getReadReplicaDbClient() const paymentsProcessor = createPaymentsProcessor() const userRepository = new UserRepository(dbClient) const invoiceRepository = new InvoiceRepository(dbClient) - const eventRepository = new EventRepository(dbClient) + const eventRepository = new EventRepository(dbClient, rrDbClient) const paymentsService = new PaymentsService( dbClient, diff --git a/src/factories/payments-service-factory.ts b/src/factories/payments-service-factory.ts index 633bf08..abd97dd 100644 --- a/src/factories/payments-service-factory.ts +++ b/src/factories/payments-service-factory.ts @@ -1,17 +1,18 @@ +import { getMasterDbClient, getReadReplicaDbClient } from '../database/client' import { createPaymentsProcessor } from './payments-processor-factory' import { createSettings } from './settings-factory' import { EventRepository } from '../repositories/event-repository' -import { getDbClient } from '../database/client' import { InvoiceRepository } from '../repositories/invoice-repository' import { PaymentsService } from '../services/payments-service' import { UserRepository } from '../repositories/user-repository' export const createPaymentsService = () => { - const dbClient = getDbClient() + const dbClient = getMasterDbClient() + const rrDbClient = getReadReplicaDbClient() const invoiceRepository = new InvoiceRepository(dbClient) const userRepository = new UserRepository(dbClient) const paymentsProcessor = createPaymentsProcessor() - const eventRepository = new EventRepository(dbClient) + const eventRepository = new EventRepository(dbClient, rrDbClient) return new PaymentsService( dbClient, diff --git a/src/factories/post-invoice-controller-factory.ts b/src/factories/post-invoice-controller-factory.ts index f067a16..e3f57b2 100644 --- a/src/factories/post-invoice-controller-factory.ts +++ b/src/factories/post-invoice-controller-factory.ts @@ -1,7 +1,7 @@ +import { getMasterDbClient, getReadReplicaDbClient } from '../database/client' import { createPaymentsProcessor } from './payments-processor-factory' import { createSettings } from './settings-factory' import { EventRepository } from '../repositories/event-repository' -import { getDbClient } from '../database/client' import { IController } from '../@types/controllers' import { InvoiceRepository } from '../repositories/invoice-repository' import { PaymentsService } from '../services/payments-service' @@ -10,8 +10,9 @@ import { slidingWindowRateLimiterFactory } from './rate-limiter-factory' import { UserRepository } from '../repositories/user-repository' export const createPostInvoiceController = (): IController => { - const dbClient = getDbClient() - const eventRepository = new EventRepository(dbClient) + const dbClient = getMasterDbClient() + const rrDbClient = getReadReplicaDbClient() + const eventRepository = new EventRepository(dbClient, rrDbClient) const invoiceRepository = new InvoiceRepository(dbClient) const userRepository = new UserRepository(dbClient) const paymentsProcessor = createPaymentsProcessor() diff --git a/src/factories/zebedee-callback-controller-factory.ts b/src/factories/zebedee-callback-controller-factory.ts index 8349e60..28fd957 100644 --- a/src/factories/zebedee-callback-controller-factory.ts +++ b/src/factories/zebedee-callback-controller-factory.ts @@ -1,7 +1,7 @@ +import { getMasterDbClient, getReadReplicaDbClient } from '../database/client' import { createPaymentsProcessor } from './payments-processor-factory' import { createSettings } from './settings-factory' import { EventRepository } from '../repositories/event-repository' -import { getDbClient } from '../database/client' import { IController } from '../@types/controllers' import { InvoiceRepository } from '../repositories/invoice-repository' import { PaymentsService } from '../services/payments-service' @@ -9,8 +9,9 @@ import { UserRepository } from '../repositories/user-repository' import { ZebedeeCallbackController } from '../controllers/callbacks/zebedee-callback-controller' export const createZebedeeCallbackController = (): IController => { - const dbClient = getDbClient() - const eventRepository = new EventRepository(dbClient) + const dbClient = getMasterDbClient() + const rrDbClient = getReadReplicaDbClient() + const eventRepository = new EventRepository(dbClient, rrDbClient) const invoiceRepotistory = new InvoiceRepository(dbClient) const userRepository = new UserRepository(dbClient) const paymentsProcessor = createPaymentsProcessor() diff --git a/src/handlers/request-handlers/rate-limiter-middleware.ts b/src/handlers/request-handlers/rate-limiter-middleware.ts index 2c296fc..e9a2e18 100644 --- a/src/handlers/request-handlers/rate-limiter-middleware.ts +++ b/src/handlers/request-handlers/rate-limiter-middleware.ts @@ -29,6 +29,10 @@ export async function isRateLimited(remoteAddress: string, settings: Settings): ipWhitelist = [], } = settings.limits?.connection ?? {} + if (typeof rateLimits === 'undefined') { + return false + } + if (ipWhitelist.includes(remoteAddress)) { return false } diff --git a/src/repositories/event-repository.ts b/src/repositories/event-repository.ts index b3996e4..b9fb6a9 100644 --- a/src/repositories/event-repository.ts +++ b/src/repositories/event-repository.ts @@ -139,7 +139,7 @@ export class EventRepository implements IEventRepository { ifElse( isEmpty, () => andWhereRaw('1 = 0', bd), - forEach((criterion: string[]) => void orWhereRaw( + forEach((criterion: string) => void orWhereRaw( '"event_tags" @> ?', [ JSON.stringify([[filterName[1], criterion]]) as any, @@ -195,7 +195,7 @@ export class EventRepository implements IEventRepository { const toJSON = (input: any) => JSON.stringify(input) - const row = applySpec({ + const row = applySpec({ event_id: pipe(prop('id'), toBuffer), event_pubkey: pipe(prop('pubkey'), toBuffer), event_created_at: prop('created_at'), diff --git a/src/services/payments-service.ts b/src/services/payments-service.ts index e507187..c068d61 100644 --- a/src/services/payments-service.ts +++ b/src/services/payments-service.ts @@ -234,8 +234,8 @@ Amount: ${amount.toString()} ${unit} ⚠️ By paying this invoice, you confirm that you have read and agree to the Terms of Service: ${terms.toString()} - -⏳ Expires at ${invoice.expiresAt.toISOString()} +${invoice.expiresAt ? ` +⏳ Expires at ${invoice.expiresAt.toISOString()}` : ''} ${invoice.bolt11}`, tags: [ diff --git a/src/utils/settings.ts b/src/utils/settings.ts index 4a39c67..e9e226e 100644 --- a/src/utils/settings.ts +++ b/src/utils/settings.ts @@ -15,7 +15,7 @@ export enum SettingsFileTypes { } export class SettingsStatic { - static _settings: Settings + static _settings: Settings | undefined public static getSettingsFileBasePath(): string { return process.env.NOSTR_CONFIG_DIR ?? join(process.cwd(), '.nostr') @@ -93,6 +93,10 @@ export class SettingsStatic { SettingsStatic._settings = mergeDeepRight({}, defaults) } + if (typeof SettingsStatic._settings === 'undefined') { + throw new Error('Unable to set settings') + } + return SettingsStatic._settings } catch (error) { debug('error reading config file at %s: %o', settingsFilePath, error)