fix: lots of bugs

Signed-off-by: Ricardo Arturo Cabral Mejía <me@ricardocabral.io>
This commit is contained in:
Ricardo Arturo Cabral Mejía
2023-01-27 10:20:55 -05:00
parent f9c53eeeb8
commit 9c010e7865
18 changed files with 62 additions and 43 deletions

View File

@ -5,5 +5,5 @@
nostream.localtest.me { nostream.localtest.me {
tls /root/certs/nostream.localtest.me.pem /root/certs/nostream.localtest.me-key.pem tls /root/certs/nostream.localtest.me.pem /root/certs/nostream.localtest.me-key.pem
reverse_proxy nostr-ts-relay:8008 reverse_proxy nostream:8008
} }

View File

@ -1,7 +1,7 @@
services: services:
relay: nostream:
volumes: volumes:
- ${PWD}/.nostr.local:/home/node/ - ${PWD}/.nostr.local:/home/node
caddy: caddy:
image: caddy:2.6.2-alpine image: caddy:2.6.2-alpine
container_name: caddy container_name: caddy

View File

@ -4,7 +4,7 @@ services:
container_name: tor container_name: tor
user: toruser user: toruser
depends_on: depends_on:
- relay - nostream
volumes: volumes:
- ${PWD}/tor/torrc:/etc/tor/torrc - ${PWD}/tor/torrc:/etc/tor/torrc
- ${PWD}/.nostr/tor/data:/var/lib/tor - ${PWD}/.nostr/tor/data:/var/lib/tor

View File

@ -38,8 +38,6 @@ services:
# DEBUG: "primary:*" # DEBUG: "primary:*"
# DEBUG: "worker:*" # DEBUG: "worker:*"
# DEBUG: "knex:query" # DEBUG: "knex:query"
env_file:
- test.env
user: node:node user: node:node
volumes: volumes:
- ${PWD}/.nostr:/home/node/.nostr - ${PWD}/.nostr:/home/node/.nostr

View File

@ -3,11 +3,12 @@ import { SubscriptionFilter } from './subscription'
export interface IWebSocketServerAdapter extends EventEmitter, IWebServerAdapter { export interface IWebSocketServerAdapter extends EventEmitter, IWebServerAdapter {
getConnectedClients(): number getConnectedClients(): number
close(callback: () => void): void close(callback?: () => void): void
} }
export interface IWebServerAdapter extends EventEmitter { export interface IWebServerAdapter extends EventEmitter {
listen(port: number): void listen(port: number): void
close(callback?: () => void): void
} }

View File

@ -28,7 +28,7 @@ export type Factory<TOutput = any, TInput = void> = (input: TInput) => TOutput
export type DatabaseClient = Knex export type DatabaseClient = Knex
export type DatabaseTransaction<T = any> = Knex.Transaction<T, T[]> export type DatabaseTransaction<T extends Record<string, unknown> = any> = Knex.Transaction<T, T[]>
export interface ContextMetadata { export interface ContextMetadata {
remoteAddress: SocketAddress remoteAddress: SocketAddress

View File

@ -154,7 +154,7 @@ export interface PaymentsProcessors {
export interface Settings { export interface Settings {
info: Info info: Info
payments?: Payments payments?: Payments
paymentProcessors?: PaymentsProcessors paymentsProcessors?: PaymentsProcessors
network: Network network: Network
workers?: Worker workers?: Worker
limits?: Limits limits?: Limits

View File

@ -10,7 +10,7 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter
public constructor( public constructor(
protected readonly webServer: Server, protected readonly webServer: Server,
) { ) {
debug('web server starting') debug('created')
super() super()
this.webServer this.webServer
.on('error', this.onError.bind(this)) .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') 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() { protected onClose() {
debug('stopped listening to incoming connections') debug('stopped listening to incoming connections')
this.webServer.removeAllListeners() this.close()
} }
} }

View File

@ -30,6 +30,7 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
>, >,
private readonly settings: () => Settings, private readonly settings: () => Settings,
) { ) {
debug('created')
super(webServer) super(webServer)
this.webSocketsAdapters = new WeakMap() 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) this.heartbeatInterval = setInterval(this.onHeartbeat.bind(this), WSS_CLIENT_HEALTH_PROBE_INTERVAL)
} }
public close(callback: () => void): void { public close(callback?: () => void): void {
this.onClose() 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.webSocketServer.close(() => {
this.webServer.close(callback) this.webServer.close(callback)
super.close()
}) })
debug('closed')
} }
private onBroadcast(event: Event) { private onBroadcast(event: Event) {
@ -90,15 +100,6 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
} }
protected onClose() { protected onClose() {
debug('closing') this.close()
clearInterval(this.heartbeatInterval)
this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
debug('terminating client')
webSocket.terminate()
})
this.removeAllListeners()
this.webSocketServer.removeAllListeners()
super.onClose()
debug('closed')
} }
} }

View File

@ -57,8 +57,6 @@ export class AppWorker implements IRunnable {
watcher.close() watcher.close()
} }
} }
if (typeof callback !== 'undefined') { this.adapter.close(callback)
this.adapter.close(callback)
}
} }
} }

View File

@ -1,18 +1,19 @@
import { getMasterDbClient, getReadReplicaDbClient } from '../database/client'
import { createPaymentsProcessor } from './payments-processor-factory' import { createPaymentsProcessor } from './payments-processor-factory'
import { createSettings } from './settings-factory' import { createSettings } from './settings-factory'
import { EventRepository } from '../repositories/event-repository' import { EventRepository } from '../repositories/event-repository'
import { getDbClient } from '../database/client'
import { InvoiceRepository } from '../repositories/invoice-repository' import { InvoiceRepository } from '../repositories/invoice-repository'
import { MaintenanceWorker } from '../app/maintenance-worker' import { MaintenanceWorker } from '../app/maintenance-worker'
import { PaymentsService } from '../services/payments-service' import { PaymentsService } from '../services/payments-service'
import { UserRepository } from '../repositories/user-repository' import { UserRepository } from '../repositories/user-repository'
export const maintenanceWorkerFactory = () => { export const maintenanceWorkerFactory = () => {
const dbClient = getDbClient() const dbClient = getMasterDbClient()
const rrDbClient = getReadReplicaDbClient()
const paymentsProcessor = createPaymentsProcessor() const paymentsProcessor = createPaymentsProcessor()
const userRepository = new UserRepository(dbClient) const userRepository = new UserRepository(dbClient)
const invoiceRepository = new InvoiceRepository(dbClient) const invoiceRepository = new InvoiceRepository(dbClient)
const eventRepository = new EventRepository(dbClient) const eventRepository = new EventRepository(dbClient, rrDbClient)
const paymentsService = new PaymentsService( const paymentsService = new PaymentsService(
dbClient, dbClient,

View File

@ -1,17 +1,18 @@
import { getMasterDbClient, getReadReplicaDbClient } from '../database/client'
import { createPaymentsProcessor } from './payments-processor-factory' import { createPaymentsProcessor } from './payments-processor-factory'
import { createSettings } from './settings-factory' import { createSettings } from './settings-factory'
import { EventRepository } from '../repositories/event-repository' import { EventRepository } from '../repositories/event-repository'
import { getDbClient } from '../database/client'
import { InvoiceRepository } from '../repositories/invoice-repository' import { InvoiceRepository } from '../repositories/invoice-repository'
import { PaymentsService } from '../services/payments-service' import { PaymentsService } from '../services/payments-service'
import { UserRepository } from '../repositories/user-repository' import { UserRepository } from '../repositories/user-repository'
export const createPaymentsService = () => { export const createPaymentsService = () => {
const dbClient = getDbClient() const dbClient = getMasterDbClient()
const rrDbClient = getReadReplicaDbClient()
const invoiceRepository = new InvoiceRepository(dbClient) const invoiceRepository = new InvoiceRepository(dbClient)
const userRepository = new UserRepository(dbClient) const userRepository = new UserRepository(dbClient)
const paymentsProcessor = createPaymentsProcessor() const paymentsProcessor = createPaymentsProcessor()
const eventRepository = new EventRepository(dbClient) const eventRepository = new EventRepository(dbClient, rrDbClient)
return new PaymentsService( return new PaymentsService(
dbClient, dbClient,

View File

@ -1,7 +1,7 @@
import { getMasterDbClient, getReadReplicaDbClient } from '../database/client'
import { createPaymentsProcessor } from './payments-processor-factory' import { createPaymentsProcessor } from './payments-processor-factory'
import { createSettings } from './settings-factory' import { createSettings } from './settings-factory'
import { EventRepository } from '../repositories/event-repository' import { EventRepository } from '../repositories/event-repository'
import { getDbClient } from '../database/client'
import { IController } from '../@types/controllers' import { IController } from '../@types/controllers'
import { InvoiceRepository } from '../repositories/invoice-repository' import { InvoiceRepository } from '../repositories/invoice-repository'
import { PaymentsService } from '../services/payments-service' import { PaymentsService } from '../services/payments-service'
@ -10,8 +10,9 @@ import { slidingWindowRateLimiterFactory } from './rate-limiter-factory'
import { UserRepository } from '../repositories/user-repository' import { UserRepository } from '../repositories/user-repository'
export const createPostInvoiceController = (): IController => { export const createPostInvoiceController = (): IController => {
const dbClient = getDbClient() const dbClient = getMasterDbClient()
const eventRepository = new EventRepository(dbClient) const rrDbClient = getReadReplicaDbClient()
const eventRepository = new EventRepository(dbClient, rrDbClient)
const invoiceRepository = new InvoiceRepository(dbClient) const invoiceRepository = new InvoiceRepository(dbClient)
const userRepository = new UserRepository(dbClient) const userRepository = new UserRepository(dbClient)
const paymentsProcessor = createPaymentsProcessor() const paymentsProcessor = createPaymentsProcessor()

View File

@ -1,7 +1,7 @@
import { getMasterDbClient, getReadReplicaDbClient } from '../database/client'
import { createPaymentsProcessor } from './payments-processor-factory' import { createPaymentsProcessor } from './payments-processor-factory'
import { createSettings } from './settings-factory' import { createSettings } from './settings-factory'
import { EventRepository } from '../repositories/event-repository' import { EventRepository } from '../repositories/event-repository'
import { getDbClient } from '../database/client'
import { IController } from '../@types/controllers' import { IController } from '../@types/controllers'
import { InvoiceRepository } from '../repositories/invoice-repository' import { InvoiceRepository } from '../repositories/invoice-repository'
import { PaymentsService } from '../services/payments-service' import { PaymentsService } from '../services/payments-service'
@ -9,8 +9,9 @@ import { UserRepository } from '../repositories/user-repository'
import { ZebedeeCallbackController } from '../controllers/callbacks/zebedee-callback-controller' import { ZebedeeCallbackController } from '../controllers/callbacks/zebedee-callback-controller'
export const createZebedeeCallbackController = (): IController => { export const createZebedeeCallbackController = (): IController => {
const dbClient = getDbClient() const dbClient = getMasterDbClient()
const eventRepository = new EventRepository(dbClient) const rrDbClient = getReadReplicaDbClient()
const eventRepository = new EventRepository(dbClient, rrDbClient)
const invoiceRepotistory = new InvoiceRepository(dbClient) const invoiceRepotistory = new InvoiceRepository(dbClient)
const userRepository = new UserRepository(dbClient) const userRepository = new UserRepository(dbClient)
const paymentsProcessor = createPaymentsProcessor() const paymentsProcessor = createPaymentsProcessor()

View File

@ -29,6 +29,10 @@ export async function isRateLimited(remoteAddress: string, settings: Settings):
ipWhitelist = [], ipWhitelist = [],
} = settings.limits?.connection ?? {} } = settings.limits?.connection ?? {}
if (typeof rateLimits === 'undefined') {
return false
}
if (ipWhitelist.includes(remoteAddress)) { if (ipWhitelist.includes(remoteAddress)) {
return false return false
} }

View File

@ -139,7 +139,7 @@ export class EventRepository implements IEventRepository {
ifElse( ifElse(
isEmpty, isEmpty,
() => andWhereRaw('1 = 0', bd), () => andWhereRaw('1 = 0', bd),
forEach((criterion: string[]) => void orWhereRaw( forEach((criterion: string) => void orWhereRaw(
'"event_tags" @> ?', '"event_tags" @> ?',
[ [
JSON.stringify([[filterName[1], criterion]]) as any, JSON.stringify([[filterName[1], criterion]]) as any,
@ -195,7 +195,7 @@ export class EventRepository implements IEventRepository {
const toJSON = (input: any) => JSON.stringify(input) const toJSON = (input: any) => JSON.stringify(input)
const row = applySpec({ const row = applySpec<DBEvent>({
event_id: pipe(prop('id'), toBuffer), event_id: pipe(prop('id'), toBuffer),
event_pubkey: pipe(prop('pubkey'), toBuffer), event_pubkey: pipe(prop('pubkey'), toBuffer),
event_created_at: prop('created_at'), event_created_at: prop('created_at'),

View File

@ -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: ⚠️ By paying this invoice, you confirm that you have read and agree to the Terms of Service:
${terms.toString()} ${terms.toString()}
${invoice.expiresAt ? `
⏳ Expires at ${invoice.expiresAt.toISOString()} ⏳ Expires at ${invoice.expiresAt.toISOString()}` : ''}
${invoice.bolt11}`, ${invoice.bolt11}`,
tags: [ tags: [

View File

@ -15,7 +15,7 @@ export enum SettingsFileTypes {
} }
export class SettingsStatic { export class SettingsStatic {
static _settings: Settings static _settings: Settings | undefined
public static getSettingsFileBasePath(): string { public static getSettingsFileBasePath(): string {
return process.env.NOSTR_CONFIG_DIR ?? join(process.cwd(), '.nostr') return process.env.NOSTR_CONFIG_DIR ?? join(process.cwd(), '.nostr')
@ -93,6 +93,10 @@ export class SettingsStatic {
SettingsStatic._settings = mergeDeepRight({}, defaults) SettingsStatic._settings = mergeDeepRight({}, defaults)
} }
if (typeof SettingsStatic._settings === 'undefined') {
throw new Error('Unable to set settings')
}
return SettingsStatic._settings return SettingsStatic._settings
} catch (error) { } catch (error) {
debug('error reading config file at %s: %o', settingsFilePath, error) debug('error reading config file at %s: %o', settingsFilePath, error)