2024-01-23 21:21:46 +07:00
"use strict" ;
2024-01-03 17:32:10 +07:00
const { version } = require ( "./package.json" ) ;
2023-10-31 14:02:14 +07:00
const WebSocket = require ( "ws" ) ;
const config = require ( "./config" ) ;
const http = require ( "http" ) ;
2024-01-13 06:36:24 -05:00
const http2 = require ( "http2" ) ;
2024-01-09 06:58:27 +07:00
const fs = require ( "fs" ) ;
2023-11-18 16:49:20 +07:00
const bouncer = require ( ` ./bouncer.js ` ) ;
2023-10-31 14:02:14 +07:00
// For log
const curD = _ => ( new Date ( ) ) . toLocaleString ( "ia" ) ;
const log = _ => console . log ( process . pid , curD ( ) , "-" , _ ) ;
// Server
2024-01-13 18:53:59 +07:00
let server = null ;
if (
fs . existsSync ( config . https ? . privKey ) &&
fs . existsSync ( config . https ? . certificate )
) {
2024-01-13 06:36:24 -05:00
let http2 _options = {
allowHTTP1 : true ,
key : fs . readFileSync ( config . https ? . privKey ) ,
cert : fs . readFileSync ( config . https ? . certificate ) ,
noDelay : true ,
dhparam : "auto" ,
paddingStrategy : http2 . constants . PADDING _STRATEGY _MAX
}
2024-01-13 18:53:59 +07:00
if ( fs . existsSync ( config . https ? . ticketKey ) )
http2 _options . ticketKeys = fs . readFileSync ( config . https ? . ticketKey ) ;
server = http2 . createSecureServer ( http2 _options ) ;
server . isStandaloneHTTPS = true ;
2024-01-13 06:36:24 -05:00
} else {
2024-01-13 18:53:59 +07:00
server = http . createServer ( { noDelay : true } )
server . isStandaloneHTTPS = false ;
2024-01-13 06:36:24 -05:00
}
2024-01-13 18:53:59 +07:00
2024-01-27 19:42:18 +07:00
const wss = new WebSocket . WebSocketServer ( {
noServer : true ,
allowSynchronousEvents : true ,
perMessageDeflate : config . perMessageDeflate || true
} ) ;
2024-01-04 22:01:08 +07:00
const lastConn = new Map ( ) ;
2023-10-31 14:02:14 +07:00
2024-01-09 06:58:27 +07:00
const favicon = fs . existsSync ( config . favicon ) ? fs . readFileSync ( config . favicon ) : null ;
2023-10-31 14:02:14 +07:00
server . on ( 'request' , ( req , res ) => {
2024-01-04 23:12:14 +07:00
log ( ` ${ req . headers [ "x-forwarded-for" ] ? . split ( "," ) [ 0 ] || req . socket . address ( ) ? . address } - ${ req . method } ${ req . url } [ ${ req . headers [ "user-agent" ] || "" } ] ` )
2023-10-31 14:02:14 +07:00
2023-11-13 18:32:29 +07:00
if ( req . headers . accept ? . includes ( "application/nostr+json" ) )
2023-10-31 14:02:14 +07:00
return res . writeHead ( 200 , {
2023-11-13 18:32:29 +07:00
"Content-Type" : "application/json" ,
"Access-Control-Allow-Origin" : "*"
2023-10-31 14:02:14 +07:00
} ) . end ( JSON . stringify ( config . server _meta ) ) ;
if ( req . url === "/" ) {
2023-11-13 18:35:50 +07:00
res . writeHead ( 200 , {
"Content-Type" : "text/plain"
} ) ;
2023-10-31 14:02:14 +07:00
res . write ( "Hello. This nostr bouncer (bostr) is bouncing the following relays:\n\n" ) ;
config . relays . forEach ( _ => {
res . write ( "- " + _ + "\n" ) ;
} ) ;
2023-10-31 20:01:22 +07:00
2023-10-31 20:09:03 +07:00
res . write ( ` \n I have ${ wss . clients . size } clients currently connected to this bouncer ${ ( process . env . CLUSTERS || config . clusters ) > 1 ? " on this cluster" : "" } . \n ` ) ;
2023-11-16 21:53:58 +07:00
if ( config ? . authorized _keys ? . length ) res . write ( "\nNOTE: This relay has configured for personal use only. Only authorized users could use this bostr relay.\n" ) ;
2024-02-18 11:25:33 +07:00
res . write ( ` \n Connect to this bouncer with nostr client: ${ req . headers [ "x-forwarded-proto" ] ? . replace ( /http/i , "ws" ) || ( server . isStandaloneHTTPS ? "wss" : "ws" ) } :// ${ req . headers . host } ${ req . url } ` ) ;
res . write ( ` \n \n - To make this bouncer only send you specific event kinds, Connect: ` ) ;
res . write ( ` \n ${ req . headers [ "x-forwarded-proto" ] ? . replace ( /http/i , "ws" ) || ( server . isStandaloneHTTPS ? "wss" : "ws" ) } :// ${ req . headers . host } ${ req . url } ?accept=0,1,2,3,4,5,6,7,8,16,1984,1985,13194,21000,23194,23195,30315 ` ) ;
res . write ( ` \n (Will only send events with kinds 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 1984, 1985, 13194, 21000, 23194, 23195, and 30315) ` ) ;
res . write ( ` \n - To make this bouncer not sending some specific kind of events, Connect: ` ) ;
res . write ( ` \n ${ req . headers [ "x-forwarded-proto" ] ? . replace ( /http/i , "ws" ) || ( server . isStandaloneHTTPS ? "wss" : "ws" ) } :// ${ req . headers . host } ${ req . url } ?reject=10000,10002 ` ) ;
res . write ( ` \n (Will not send events with kind 10000, and 10002) ` ) ;
res . end ( ` \n \n --- \n Powered by Bostr ( ${ version } ) - Open source Nostr bouncer \n https://github.com/Yonle/bostr ` ) ;
2024-02-18 11:47:49 +07:00
} else if ( req . url . includes ( "favicon" ) && favicon ) {
2024-01-09 06:58:27 +07:00
res . writeHead ( 200 , { "Content-Type" : "image/" + config . favicon ? . split ( "." ) . pop ( ) } ) ;
res . end ( favicon ) ;
2023-10-31 14:02:14 +07:00
} else {
res . writeHead ( 404 ) . end ( "What are you looking for?" ) ;
}
} ) ;
server . on ( 'upgrade' , ( req , sock , head ) => {
2024-01-23 21:25:56 +07:00
for ( const i of lastConn ) {
2024-01-04 22:15:50 +07:00
if ( config . incomming _ratelimit > ( Date . now ( ) - i [ 1 ] ) ) continue ;
lastConn . delete ( i [ 0 ] ) ;
}
2024-01-04 22:01:08 +07:00
const ip = req . headers [ "x-forwarded-for" ] ? . split ( "," ) [ 0 ] || sock . address ( ) ? . address ;
const lv = lastConn . get ( ip ) // last visit
if ( config . incomming _ratelimit && ( config . incomming _ratelimit > ( Date . now ( ) - lv ) ) ) {
2024-01-04 23:12:14 +07:00
log ( ` Rejected connection from ${ ip } as the last connection was ${ Date . now ( ) - lv } ms ago. ` ) ;
2024-01-04 22:01:08 +07:00
lastConn . set ( ip , Date . now ( ) ) ;
return sock . destroy ( ) ; // destroy.
}
lastConn . set ( ip , Date . now ( ) ) ;
2024-01-23 22:49:43 +07:00
wss . handleUpgrade ( req , sock , head , _ => bouncer ( _ , req , _ => lastConn . set ( ip , Date . now ( ) ) ) ) ;
2023-10-31 14:02:14 +07:00
} ) ;
const listened = server . listen ( process . env . PORT || config . port , config . address || "0.0.0.0" , _ => {
2024-01-13 18:53:59 +07:00
log ( "Bostr is now listening on " + ` ${ server . isStandaloneHTTPS ? "wss" : "ws" } :// ` + ( config . address || "0.0.0.0" ) + ":" + config . port ) ;
2023-10-31 14:02:14 +07:00
} ) ;