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 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 ;
2024-02-19 18:37:50 +07:00
let config = require ( process . env . BOSTR _CONFIG _PATH || "./config" ) ;
config . server _meta . version = version ;
2024-01-13 18:53:59 +07:00
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-02-23 13:11:00 +07:00
const serverAddr = ` ${ req . headers [ "x-forwarded-proto" ] ? . replace ( /http/i , "ws" ) || ( server . isStandaloneHTTPS ? "wss" : "ws" ) } :// ${ req . headers . host } ${ req . url } ` ;
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-23 13:11:00 +07:00
res . write ( ` \n Connect to this bouncer with nostr client: ${ serverAddr } ` ) ;
res . write ( ` \n \n - To make connection that only send whitelisted kind of events, Connect: ` ) ;
res . write ( ` \n ${ serverAddr } ?accept=0,1 ` ) ;
2024-02-18 19:13:41 +07:00
res . write ( ` \n (Will only send events with kind 0, and 1) ` ) ;
2024-02-23 13:11:00 +07:00
res . write ( ` \n \n - To make connection that do not send blacklisted kind of events, Connect: ` ) ;
res . write ( ` \n ${ serverAddr } ?reject=3,6,7 ` ) ;
2024-02-18 12:07:08 +07:00
res . write ( ` \n (Will not send events with kind 3, 6, and 7) ` ) ;
2024-02-23 13:11:00 +07:00
res . write ( ` \n \n - To make connection that override client's REQ limit, Connect: ` ) ;
2024-02-24 12:23:55 +07:00
res . write ( ` \n ${ serverAddr } ?limit=50 or ${ serverAddr } ?accurate=1&limit=50 ` ) ;
2024-02-23 13:11:00 +07:00
res . write ( ` \n (Will override REQ limit from client to 50 if exceeds) ` ) ;
2024-02-24 12:23:55 +07:00
res . write ( ` \n \n - To connect with accurate bouncing mode, Connect: ` ) ;
res . write ( ` \n ${ serverAddr } ?accurate=1 ` ) ;
res . write ( ` \n (Will consume lot of bandwidths) ` ) ;
2024-02-18 11:25:33 +07:00
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 ;
2024-02-18 20:07:46 +07:00
if ( config . blocked _hosts && config . blocked _hosts . includes ( ip ) ) return sock . destroy ( ) ;
2024-01-04 22:01:08 +07:00
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
} ) ;