mirror of
https://github.com/Cameri/nostream.git
synced 2025-07-12 14:52:20 +02:00
fix: nip-11 doc and tests
Signed-off-by: Ricardo Arturo Cabral Mejía <me@ricardocabral.io>
This commit is contained in:
@ -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
4
package-lock.json
generated
@ -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"
|
||||
}
|
||||
},
|
||||
|
10
package.json
10
package.json
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -58,5 +58,6 @@ export class AppWorker implements IRunnable {
|
||||
}
|
||||
}
|
||||
this.adapter.close(callback)
|
||||
debug('closed')
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,5 @@ export enum WebSocketAdapterEvent {
|
||||
|
||||
export enum WebSocketServerAdapterEvent {
|
||||
Broadcast = 'broadcast',
|
||||
Close = 'close',
|
||||
Connection = 'connection'
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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 () {
|
||||
|
Reference in New Issue
Block a user