@@ -8,16 +8,17 @@ import jwt from "../security/jwt.js";
88import stream from "../stream/stream.js" ;
99import match from "../processing/match.js" ;
1010
11- import { env , isCluster , setTunnelPort } from "../config.js" ;
11+ import { env } from "../config.js" ;
1212import { extract } from "../processing/url.js" ;
13- import { Green , Bright , Cyan } from "../misc/console-text.js" ;
13+ import { Bright , Cyan } from "../misc/console-text.js" ;
1414import { hashHmac } from "../security/secrets.js" ;
1515import { createStore } from "../store/redis-ratelimit.js" ;
1616import { randomizeCiphers } from "../misc/randomize-ciphers.js" ;
1717import { verifyTurnstileToken } from "../security/turnstile.js" ;
1818import { friendlyServiceName } from "../processing/service-alias.js" ;
19- import { verifyStream , getInternalStream } from "../stream/manage.js" ;
19+ import { verifyStream } from "../stream/manage.js" ;
2020import { createResponse , normalizeRequest , getIP } from "../processing/request.js" ;
21+ import { setupTunnelHandler } from "./itunnel.js" ;
2122
2223import * as APIKeys from "../security/api-keys.js" ;
2324import * as Cookies from "../processing/cookie/manager.js" ;
@@ -47,28 +48,31 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
4748 const startTime = new Date ( ) ;
4849 const startTimestamp = startTime . getTime ( ) ;
4950
50- const serverInfo = JSON . stringify ( {
51- cobalt : {
52- version : version ,
53- url : env . apiURL ,
54- startTime : `${ startTimestamp } ` ,
55- durationLimit : env . durationLimit ,
56- turnstileSitekey : env . sessionEnabled ? env . turnstileSitekey : undefined ,
57- services : [ ...env . enabledServices ] . map ( e => {
58- return friendlyServiceName ( e ) ;
59- } ) ,
60- } ,
61- git,
62- } )
51+ const getServerInfo = ( ) => {
52+ return JSON . stringify ( {
53+ cobalt : {
54+ version : version ,
55+ url : env . apiURL ,
56+ startTime : `${ startTimestamp } ` ,
57+ turnstileSitekey : env . sessionEnabled ? env . turnstileSitekey : undefined ,
58+ services : [ ...env . enabledServices ] . map ( e => {
59+ return friendlyServiceName ( e ) ;
60+ } ) ,
61+ } ,
62+ git,
63+ } ) ;
64+ }
65+
66+ const serverInfo = getServerInfo ( ) ;
6367
6468 const handleRateExceeded = ( _ , res ) => {
65- const { status , body } = createResponse ( "error" , {
69+ const { body } = createResponse ( "error" , {
6670 code : "error.api.rate_exceeded" ,
6771 context : {
6872 limit : env . rateLimitWindow
6973 }
7074 } ) ;
71- return res . status ( status ) . json ( body ) ;
75+ return res . status ( 429 ) . json ( body ) ;
7276 } ;
7377
7478 const keyGenerator = ( req ) => hashHmac ( getIP ( req ) , 'rate' ) . toString ( 'base64url' ) ;
@@ -94,14 +98,14 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
9498 } ) ;
9599
96100 const apiTunnelLimiter = rateLimit ( {
97- windowMs : env . rateLimitWindow * 1000 ,
98- limit : ( req ) => req . rateLimitMax || env . rateLimitMax ,
101+ windowMs : env . tunnelRateLimitWindow * 1000 ,
102+ limit : env . tunnelRateLimitMax ,
99103 standardHeaders : 'draft-6' ,
100104 legacyHeaders : false ,
101- keyGenerator : req => req . rateLimitKey || keyGenerator ( req ) ,
105+ keyGenerator : req => keyGenerator ( req ) ,
102106 store : await createStore ( 'tunnel' ) ,
103107 handler : ( _ , res ) => {
104- return res . sendStatus ( 429 )
108+ return res . sendStatus ( 429 ) ;
105109 }
106110 } ) ;
107111
@@ -180,6 +184,7 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
180184 }
181185
182186 req . rateLimitKey = hashHmac ( token , 'rate' ) ;
187+ req . isSession = true ;
183188 } catch {
184189 return fail ( res , "error.api.generic" ) ;
185190 }
@@ -244,6 +249,7 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
244249 if ( ! parsed ) {
245250 return fail ( res , "error.api.link.invalid" ) ;
246251 }
252+
247253 if ( "error" in parsed ) {
248254 let context ;
249255 if ( parsed ?. context ) {
@@ -257,13 +263,23 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
257263 host : parsed . host ,
258264 patternMatch : parsed . patternMatch ,
259265 params : normalizedRequest ,
266+ isSession : req . isSession ?? false ,
260267 } ) ;
261268
262269 res . status ( result . status ) . json ( result . body ) ;
263270 } catch {
264271 fail ( res , "error.api.generic" ) ;
265272 }
266- } )
273+ } ) ;
274+
275+ app . use ( '/tunnel' , cors ( {
276+ methods : [ 'GET' ] ,
277+ exposedHeaders : [
278+ 'Estimated-Content-Length' ,
279+ 'Content-Disposition'
280+ ] ,
281+ ...corsConfig ,
282+ } ) ) ;
267283
268284 app . get ( '/tunnel' , apiTunnelLimiter , async ( req , res ) => {
269285 const id = String ( req . query . id ) ;
@@ -294,35 +310,11 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
294310 }
295311
296312 return stream ( res , streamInfo ) ;
297- } )
298-
299- const itunnelHandler = ( req , res ) => {
300- if ( ! req . ip . endsWith ( '127.0.0.1' ) ) {
301- return res . sendStatus ( 403 ) ;
302- }
303-
304- if ( String ( req . query . id ) . length !== 21 ) {
305- return res . sendStatus ( 400 ) ;
306- }
307-
308- const streamInfo = getInternalStream ( req . query . id ) ;
309- if ( ! streamInfo ) {
310- return res . sendStatus ( 404 ) ;
311- }
312-
313- streamInfo . headers = new Map ( [
314- ...( streamInfo . headers || [ ] ) ,
315- ...Object . entries ( req . headers )
316- ] ) ;
317-
318- return stream ( res , { type : 'internal' , data : streamInfo } ) ;
319- } ;
320-
321- app . get ( '/itunnel' , itunnelHandler ) ;
313+ } ) ;
322314
323315 app . get ( '/' , ( _ , res ) => {
324316 res . type ( 'json' ) ;
325- res . status ( 200 ) . send ( serverInfo ) ;
317+ res . status ( 200 ) . send ( env . envFile ? getServerInfo ( ) : serverInfo ) ;
326318 } )
327319
328320 app . get ( '/favicon.ico' , ( req , res ) => {
@@ -342,10 +334,6 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
342334 setInterval ( randomizeCiphers , 1000 * 60 * 30 ) ; // shuffle ciphers every 30 minutes
343335
344336 if ( env . externalProxy ) {
345- if ( env . freebindCIDR ) {
346- throw new Error ( 'Freebind is not available when external proxy is enabled' )
347- }
348-
349337 setGlobalDispatcher ( new ProxyAgent ( env . externalProxy ) )
350338 }
351339
@@ -384,17 +372,5 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
384372 }
385373 } ) ;
386374
387- if ( isCluster ) {
388- const istreamer = express ( ) ;
389- istreamer . get ( '/itunnel' , itunnelHandler ) ;
390- const server = istreamer . listen ( {
391- port : 0 ,
392- host : '127.0.0.1' ,
393- exclusive : true
394- } , ( ) => {
395- const { port } = server . address ( ) ;
396- console . log ( `${ Green ( '[✓]' ) } cobalt sub-instance running on 127.0.0.1:${ port } ` ) ;
397- setTunnelPort ( port ) ;
398- } ) ;
399- }
375+ setupTunnelHandler ( ) ;
400376}
0 commit comments