fix: nip-11 doc and tests

Signed-off-by: Ricardo Arturo Cabral Mejía <me@ricardocabral.io>
This commit is contained in:
Ricardo Arturo Cabral Mejía
2023-01-27 16:30:26 -05:00
parent 9c010e7865
commit 4ec6f20cc9
14 changed files with 95 additions and 62 deletions

View File

@ -1,16 +1,4 @@
FROM node:18-alpine3.16
ENV DB_HOST=db-test
ENV DB_PORT=5432
ENV DB_NAME=postgres
ENV DB_USER=postgres
ENV DB_PASSWORD=postgres
ENV DB_MIN_POOL_SIZE=1
ENV DB_MAX_POOL_SIZE=2
ENV REDIS_HOST=cache-test
ENV REDIS_PORT=6379
ENV REDIS_USER=default
ENV REDIS_PASSWORD=nostr_ts_relay_test
FROM node:18.8-alpine3.16
WORKDIR /code

4
package-lock.json generated
View File

@ -68,9 +68,9 @@
"sinon": "15.0.1",
"sinon-chai": "^3.7.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.1",
"ts-node": "10.9.1",
"ts-node-dev": "^1.1.8",
"typescript": "4.6",
"typescript": "4.6.4",
"uuid": "^8.3.2"
}
},

View File

@ -105,9 +105,9 @@
"sinon": "15.0.1",
"sinon-chai": "^3.7.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.1",
"ts-node": "10.9.1",
"ts-node-dev": "^1.1.8",
"typescript": "4.6",
"typescript": "4.6.4",
"uuid": "^8.3.2"
},
"dependencies": {
@ -121,15 +121,15 @@
"express": "4.18.2",
"helmet": "6.0.1",
"joi": "17.7.0",
"knex": "2.4.0",
"js-yaml": "4.1.0",
"knex": "2.4.0",
"pg": "8.8.0",
"pg-query-stream": "4.2.4",
"ramda": "0.28.0",
"redis": "4.5.1",
"tor-control-ts": "^1.0.0",
"rxjs": "7.8.0",
"ws": "8.12.0"
"ws": "8.12.0",
"tor-control-ts": "^1.0.0"
},
"config": {
"commitizen": {

View File

@ -2,12 +2,12 @@
PROJECT_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/.."
SCRIPTS_DIR="${PROJECT_ROOT}/scripts"
$SCRIPTS_DIR/stop
git stash -u
git pull
git stash pop
$SCRIPTS_DIR/stop
$SCRIPTS_DIR/start

View File

@ -41,16 +41,18 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter
}
public close(callback?: () => void): void {
this.webServer.removeAllListeners()
this.webServer.close()
if (typeof callback !== 'undefined') {
callback()
}
debug('closing')
this.webServer.close(() => {
this.webServer.removeAllListeners()
this.removeAllListeners()
if (typeof callback !== 'undefined') {
callback()
}
})
debug('closed')
}
protected onClose() {
debug('stopped listening to incoming connections')
this.close()
}
}

View File

@ -245,7 +245,11 @@ export class WebSocketAdapter extends EventEmitter implements IWebSocketAdapter
const handlers = abortableMessageHandlers.get(this.client)
if (Array.isArray(handlers) && handlers.length) {
for (const handler of handlers) {
handler.abort()
try {
handler.abort()
} catch (error) {
console.error('Unable to abort message handler', error)
}
}
}

View File

@ -39,7 +39,6 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
.on(WebSocketServerAdapterEvent.Broadcast, this.onBroadcast.bind(this))
this.webSocketServer
.on(WebSocketServerAdapterEvent.Close, this.onClose.bind(this))
.on(WebSocketServerAdapterEvent.Connection, this.onConnection.bind(this))
.on('error', (error) => {
debug('error: %o', error)
@ -48,19 +47,23 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
}
public close(callback?: () => void): void {
debug('closing')
clearInterval(this.heartbeatInterval)
this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
debug('terminating client')
webSocket.terminate()
super.close(() => {
debug('closing')
clearInterval(this.heartbeatInterval)
this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
debug('terminating client %s', this.webSocketsAdapters.get(webSocket).getClientId())
webSocket.terminate()
})
debug('closing web socket server')
this.webSocketServer.close(() => {
this.webSocketServer.removeAllListeners()
if (typeof callback !== 'undefined') {
callback()
}
debug('closed')
})
})
this.removeAllListeners()
this.webSocketServer.removeAllListeners()
this.webSocketServer.close(() => {
this.webServer.close(callback)
super.close()
})
debug('closed')
}
private onBroadcast(event: Event) {
@ -98,8 +101,4 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
webSocketAdapter.emit(WebSocketAdapterEvent.Heartbeat)
})
}
protected onClose() {
this.close()
}
}

View File

@ -14,6 +14,7 @@ import { SettingsStatic } from '../utils/settings'
const debug = createLogger('app-primary')
export class App implements IRunnable {
private workers: WeakMap<Worker, string>
private watchers: FSWatcher[] | undefined
public constructor(
@ -23,6 +24,8 @@ export class App implements IRunnable {
) {
debug('starting')
this.workers = new WeakMap()
this.cluster
.on('message', this.onClusterMessage.bind(this))
.on('exit', this.onClusterExit.bind(this))
@ -65,14 +68,16 @@ export class App implements IRunnable {
for (let i = 0; i < workerCount; i++) {
debug('starting worker')
this.cluster.fork({
const worker = this.cluster.fork({
WORKER_TYPE: 'worker',
})
this.workers.set(worker, 'worker')
}
this.cluster.fork({
const worker = this.cluster.fork({
WORKER_TYPE: 'maintenance',
})
this.workers.set(worker, 'maintenance')
logCentered(`${workerCount} workers started`, width)
@ -100,12 +105,22 @@ export class App implements IRunnable {
private onClusterExit(deadWorker: Worker, code: number, signal: string) {
debug('worker %s died', deadWorker.process.pid)
if (code === 0 || signal === 'SIGINT') {
return
}
debug('starting worker')
const newWorker = this.cluster.fork()
debug('started worker %s', newWorker.process.pid)
setTimeout(() => {
debug('starting worker')
const workerType = this.workers.get(deadWorker)
if (!workerType) {
throw new Error('Mistakes were made')
}
const newWorker = this.cluster.fork({
WORKER_TYPE: workerType,
})
this.workers.set(newWorker, workerType)
debug('started worker %s', newWorker.process.pid)
}, 10000)
}
private onExit() {

View File

@ -58,5 +58,6 @@ export class AppWorker implements IRunnable {
}
}
this.adapter.close(callback)
debug('closed')
}
}

View File

@ -9,6 +9,5 @@ export enum WebSocketAdapterEvent {
export enum WebSocketServerAdapterEvent {
Broadcast = 'broadcast',
Close = 'close',
Connection = 'connection'
}

View File

@ -2,15 +2,41 @@ import { NextFunction, Request, Response } from 'express'
import { path } from 'ramda'
import { createSettings } from '../../factories/settings-factory'
import packageJson from '../../../package.json'
export const rootRequestHandler = (_req: Request, res: Response, next: NextFunction) => {
export const rootRequestHandler = (request: Request, response: Response, next: NextFunction) => {
const settings = createSettings()
if (request.header('accept') === 'application/nostr+json') {
const {
info: { name, description, pubkey, contact },
} = settings
const relayInformationDocument = {
name,
description,
pubkey,
contact,
supported_nips: packageJson.supportedNips,
software: packageJson.repository.url,
version: packageJson.version,
}
response
.setHeader('conten-type', 'application/nostr+json')
.setHeader('access-control-allow-origin', '*')
.status(200)
.send(relayInformationDocument)
return
}
const admissionFeeEnabled = path(['payments','feeSchedules','admission', '0', 'enabled'])(settings)
if (admissionFeeEnabled) {
res.redirect(301, '/invoices')
response.redirect(301, '/invoices')
} else {
res.status(200).setHeader('content-type', 'text/plain; charset=utf8').send('Please use a Nostr client to connect.')
response.status(200).setHeader('content-type', 'text/plain; charset=utf8').send('Please use a Nostr client to connect.')
}
next()
}

View File

@ -57,7 +57,7 @@ services:
retries: 0
cache-test:
image: redis:7.0.5-alpine3.16
command: redis-server --save 20 1 --loglevel warning --requirepass nostr_ts_relay_test
command: redis-server --loglevel warning --requirepass nostr_ts_relay_test
networks:
- nostream-test
restart: always

View File

@ -170,10 +170,7 @@ Then(/(\w+) receives a text_note event from (\w+) with content "([^"]+?)"/, asyn
const ws = this.parameters.clients[name] as WebSocket
const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1]
const receivedEvent = await waitForNextEvent(ws, subscription.name, content)
console.log('receivedEvent', receivedEvent)
expect(receivedEvent.kind).to.equal(1)
console.log('name', name, this.parameters.identities[name].pubkey)
console.log('author', author, this.parameters.identities[author].pubkey)
expect(receivedEvent.pubkey).to.equal(this.parameters.identities[author].pubkey)
expect(receivedEvent.content).to.equal(content)
})

View File

@ -13,12 +13,12 @@ import { fromEvent, map, Observable, ReplaySubject, Subject, takeUntil } from 'r
import WebSocket, { MessageEvent } from 'ws'
import { connect, createIdentity, createSubscription, sendEvent } from './helpers'
import { getMasterDbClient, getReadReplicaDbClient } from '../../../src/database/client'
import { AppWorker } from '../../../src/app/worker'
import { CacheClient } from '../../../src/@types/cache'
import { DatabaseClient } from '../../../src/@types/base'
import { Event } from '../../../src/@types/event'
import { getCacheClient } from '../../../src/cache/client'
import { getMasterDbClient } from '../../../src/database/client'
import { SettingsStatic } from '../../../src/utils/settings'
import { workerFactory } from '../../../src/factories/worker-factory'
@ -27,6 +27,7 @@ export const isDraft = Symbol('draft')
let worker: AppWorker
let dbClient: DatabaseClient
let rrDbClient: DatabaseClient
let cacheClient: CacheClient
export const streams = new WeakMap<WebSocket, Observable<unknown>>()
@ -35,9 +36,8 @@ BeforeAll({ timeout: 1000 }, async function () {
process.env.RELAY_PORT = '18808'
cacheClient = getCacheClient()
dbClient = getMasterDbClient()
rrDbClient = getReadReplicaDbClient()
await dbClient.raw('SELECT 1=1')
await cacheClient.connect()
await cacheClient.ping()
const settings = SettingsStatic.createSettings()
@ -54,7 +54,9 @@ BeforeAll({ timeout: 1000 }, async function () {
})
AfterAll(async function() {
worker.close(async () => Promise.all([cacheClient.disconnect(), dbClient.destroy()]))
worker.close(async () => {
await Promise.all([cacheClient.disconnect(), dbClient.destroy(), rrDbClient.destroy()])
})
})
Before(function () {