@@ -27,13 +27,13 @@ var ScreenshotMutex sync.Mutex
2727// APIHandlers contains the handlers for API endpoints
2828type APIHandlers struct {
2929 queue * RequestQueue
30- pages map [string ]* chrome.Page
30+ pages map [string ][] * chrome.Page
3131 debug bool
3232 appConfig * config.AppConfig
3333}
3434
3535// NewAPIHandlers creates a new API handlers instance
36- func NewAPIHandlers (appConfig * config.AppConfig , queue * RequestQueue , pages map [string ]* chrome.Page , debug bool ) * APIHandlers {
36+ func NewAPIHandlers (appConfig * config.AppConfig , queue * RequestQueue , pages map [string ][] * chrome.Page , debug bool ) * APIHandlers {
3737 return & APIHandlers {
3838 queue : queue ,
3939 pages : pages ,
@@ -120,10 +120,21 @@ func (h *APIHandlers) TakeScreenshot(c *gin.Context) {
120120 c .Status (http .StatusNotFound )
121121 return
122122 }
123+ instanceIndex , ok := c .GetQuery ("index" )
124+ if ! ok {
125+ c .Status (http .StatusNotFound )
126+ return
127+ }
128+ index , err := strconv .ParseUint (instanceIndex , 10 , 64 )
129+ if err != nil {
130+ c .Status (http .StatusNotFound )
131+ return
132+ }
133+
123134 if page , hasKey := h .pages [instanceName ]; hasKey {
124135 var buf []byte
125- pageCtx := page .GetContext ()
126- if err : = chromedp .Run (pageCtx , chromedp .CaptureScreenshot (& buf )); err != nil {
136+ pageCtx := page [ index ] .GetContext ()
137+ if err = chromedp .Run (pageCtx , chromedp .CaptureScreenshot (& buf )); err != nil {
127138 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to take screenshot: %v" , err ), "code" : 500 })
128139 return
129140 }
@@ -140,20 +151,16 @@ func (h *APIHandlers) TakeScreenshot(c *gin.Context) {
140151func (h * APIHandlers ) ProxyPac (c * gin.Context ) {
141152 c .Status (http .StatusOK )
142153 c .Header ("Content-Type" , "application/x-ns-proxy-autoconfig" )
143- if h .appConfig .InstanceAlone {
144- index , ok := c .GetQuery ("index" )
145- if ok {
146- i , err := strconv .ParseUint (index , 10 , 32 )
147- if err != nil {
148- _ , _ = c .Writer .Write ([]byte (`function FindProxyForURL(url, host) {return "PROXY 127.0.0.1:3120";}` ))
149- return
150- }
151-
152- port := 3120 + i
153- _ , _ = c .Writer .Write ([]byte (fmt .Sprintf (`function FindProxyForURL(url, host) {return "PROXY 127.0.0.1:%d";}` , port )))
154+ index , ok := c .GetQuery ("index" )
155+ if ok {
156+ i , err := strconv .ParseUint (index , 10 , 32 )
157+ if err != nil {
158+ _ , _ = c .Writer .Write ([]byte (`function FindProxyForURL(url, host) {return "PROXY 127.0.0.1:3120";}` ))
159+ return
154160 }
155- } else {
156- _ , _ = c .Writer .Write ([]byte (`function FindProxyForURL(url, host) {return "PROXY 127.0.0.1:3120";}` ))
161+
162+ port := 3120 + i
163+ _ , _ = c .Writer .Write ([]byte (fmt .Sprintf (`function FindProxyForURL(url, host) {return "PROXY 127.0.0.1:%d";}` , port )))
157164 }
158165}
159166
@@ -200,9 +207,26 @@ func (h *APIHandlers) ChatCompletions(c *gin.Context) {
200207 instanceIndex = i
201208 }
202209 }
203- if page , ok := h .pages [instanceName ]; ok {
204- defer page .RequestMutex .Unlock ()
205- page .RequestMutex .Lock ()
210+ var page * chrome.Page
211+ defer func () {
212+ if page != nil {
213+ page .RequestMutex .Unlock ()
214+ }
215+ }()
216+
217+ if pages , ok := h .pages [instanceName ]; ok {
218+ locked := false
219+ for i := 0 ; i < len (pages ); i ++ {
220+ page = pages [i ]
221+ if page .RequestMutex .TryLock () {
222+ locked = true
223+ break
224+ }
225+ }
226+ if ! locked {
227+ page = pages [0 ]
228+ page .RequestMutex .Lock ()
229+ }
206230 } else {
207231 c .JSON (http .StatusNotFound , ErrorResponse {
208232 Error : ErrorDetail {
@@ -224,6 +248,7 @@ func (h *APIHandlers) ChatCompletions(c *gin.Context) {
224248 CreatedAt : time .Now (),
225249 Context : c ,
226250 InstanceName : instanceName ,
251+ Page : page ,
227252 }
228253
229254 // Add a task to queue
@@ -268,8 +293,7 @@ func (h *APIHandlers) ChatCompletions(c *gin.Context) {
268293 }
269294}
270295
271- func (h * APIHandlers ) handleContextCanceled (instanceIndex int ) {
272- page := h .pages [h .appConfig .Instance [instanceIndex ].Name ]
296+ func (h * APIHandlers ) handleContextCanceled (instanceIndex int , page * chrome.Page ) {
273297 r , err := runner .NewRunnerManager (h .appConfig .Instance [instanceIndex ], page , h .debug , false )
274298 if err != nil {
275299 log .Error (err )
@@ -305,7 +329,7 @@ func (h *APIHandlers) handleNonStreamingResponse(instanceIndex int, c *gin.Conte
305329 case <- c .Request .Context ().Done ():
306330 if c .Request .Context ().Err ().Error () == "context canceled" {
307331 log .Debugf ("Client disconnected: %v" , c .Request .Context ().Err ())
308- h .handleContextCanceled (instanceIndex )
332+ h .handleContextCanceled (instanceIndex , response . Page )
309333 response .Runner .Abort ()
310334 }
311335 return
@@ -356,7 +380,7 @@ func (h *APIHandlers) handleStreamingResponse(instanceIndex int, c *gin.Context,
356380 case <- c .Request .Context ().Done ():
357381 if c .Request .Context ().Err ().Error () == "context canceled" {
358382 log .Debugf ("Client disconnected: %v" , c .Request .Context ().Err ())
359- h .handleContextCanceled (instanceIndex )
383+ h .handleContextCanceled (instanceIndex , response . Page )
360384 response .Runner .Abort ()
361385 }
362386 return
@@ -416,10 +440,20 @@ func (h *APIHandlers) BrowserReload(c *gin.Context) {
416440 c .JSON (http .StatusNotFound , gin.H {"error" : fmt .Sprintf ("Page for instance '%s' not found" , instanceName ), "code" : 404 })
417441 return
418442 }
443+ instanceIndex , ok := c .GetQuery ("index" )
444+ if ! ok {
445+ c .Status (http .StatusNotFound )
446+ return
447+ }
448+ index , err := strconv .ParseUint (instanceIndex , 10 , 64 )
449+ if err != nil {
450+ c .Status (http .StatusNotFound )
451+ return
452+ }
419453
420454 // Navigate to the instance URL
421- pageCtx := page .GetContext ()
422- if err : = chromedp .Run (pageCtx , chromedp .Navigate (instanceConfig .URL )); err != nil {
455+ pageCtx := page [ index ] .GetContext ()
456+ if err = chromedp .Run (pageCtx , chromedp .Navigate (instanceConfig .URL )); err != nil {
423457 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to reload page: %v" , err ), "code" : 500 })
424458 return
425459 }
@@ -432,6 +466,7 @@ func (h *APIHandlers) BrowserReload(c *gin.Context) {
432466func (h * APIHandlers ) AuthUpload (c * gin.Context ) {
433467 var requestData struct {
434468 Name string `json:"name" binding:"required"`
469+ Index * int `json:"index" binding:"required"`
435470 Token string `json:"token" binding:"required"`
436471 Auth string `json:"auth" binding:"required"`
437472 }
@@ -469,7 +504,7 @@ func (h *APIHandlers) AuthUpload(c *gin.Context) {
469504 }
470505
471506 // Ensure the auth directory exists
472- authAbsPath , err := filepath .Abs (instanceConfig .Auth .File )
507+ authAbsPath , err := filepath .Abs (instanceConfig .Auth .Files [ * requestData . Index ] )
473508 if err != nil {
474509 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to get absolute path: %v" , err ), "code" : 500 })
475510 return
@@ -484,29 +519,29 @@ func (h *APIHandlers) AuthUpload(c *gin.Context) {
484519 }
485520
486521 // Write the auth info to file
487- if err = os .WriteFile (instanceConfig .Auth .File , []byte (requestData .Auth ), 0644 ); err != nil {
522+ if err = os .WriteFile (instanceConfig .Auth .Files [ * requestData . Index ] , []byte (requestData .Auth ), 0644 ); err != nil {
488523 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to write auth file: %v" , err ), "code" : 500 })
489524 return
490525 }
491526
492527 // Get the page for this instance
493- page , exists := h .pages [instanceConfig .Name ]
528+ pages , exists := h .pages [instanceConfig .Name ]
494529 if ! exists {
495530 c .JSON (http .StatusNotFound , gin.H {"error" : fmt .Sprintf ("Page for instance '%s' not found" , instanceConfig .Name ), "code" : 404 })
496531 return
497532 }
498533
499534 // Navigate to the instance URL
500- pageCtx := page .GetContext ()
535+ pageCtx := pages [ * requestData . Index ] .GetContext ()
501536 err = chrome .ClearCookies (pageCtx )
502537 if err != nil {
503538 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to load auth info: %v" , err ), "code" : 500 })
504539 return
505540 }
506- page .Close ()
541+ pages [ * requestData . Index ] .Close ()
507542
508543 pageLoaded := func () {
509- r , errNewRunnerManager := runner .NewRunnerManager (* instanceConfig , page , h .appConfig .Debug , false ) // Pass pageCtx
544+ r , errNewRunnerManager := runner .NewRunnerManager (* instanceConfig , pages [ * requestData . Index ] , h .appConfig .Debug , false ) // Pass pageCtx
510545 if errNewRunnerManager != nil {
511546 log .Error (errNewRunnerManager )
512547 }
@@ -517,72 +552,23 @@ func (h *APIHandlers) AuthUpload(c *gin.Context) {
517552 log .Debugf ("all of the init system rules are executed." )
518553 }
519554
520- page , err = h .pages [instanceConfig .Name ]. GetBrowserManager ().NewPage (instanceConfig .URL , instanceConfig .Adapter , instanceConfig .Auth .File , pageLoaded )
555+ p , err : = h.pages [instanceConfig.Name ][ * requestData. Index ]. GetBrowserManager ().NewPage (instanceConfig .URL , instanceConfig .Adapter , instanceConfig .Auth .Files [ * requestData . Index ] , pageLoaded )
521556 if err != nil {
522557 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to load auth info: %v" , err ), "code" : 500 })
523558 return
524559 }
525- h .pages [instanceConfig .Name ] = page
526- pageCtx = page .GetContext ()
560+ h.pages [instanceConfig.Name ][ * requestData. Index ] = p
561+ pageCtx = p .GetContext ()
527562
528563 if err = chromedp .Run (pageCtx , chromedp .Navigate (instanceConfig .URL )); err != nil {
529564 c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to reload page: %v" , err ), "code" : 500 })
530565 return
531566 }
532567
533- log .Debugf ("Successfully uploaded auth info for instance '%s' to file: %s" , requestData .Name , instanceConfig .Auth .File )
568+ log .Debugf ("Successfully uploaded auth info for instance '%s' to file: %s" , requestData .Name , instanceConfig .Auth .Files [ * requestData . Index ] )
534569 c .JSON (http .StatusOK , gin.H {"message" : "Auth info uploaded successfully" })
535570}
536571
537- // AuthDownload handles the /v1/auth/download endpoint
538- func (h * APIHandlers ) AuthDownload (c * gin.Context ) {
539- instanceName , ok := c .GetQuery ("name" )
540- if ! ok || instanceName == "" {
541- c .JSON (http .StatusBadRequest , gin.H {"error" : "Missing required parameter: name" , "code" : 400 })
542- return
543- }
544-
545- // Find the instance configuration
546- var instanceConfig * config.AppConfigInstance
547- for i := range h .appConfig .Instance {
548- if h .appConfig .Instance [i ].Name == instanceName {
549- instanceConfig = & h .appConfig .Instance [i ]
550- break
551- }
552- }
553-
554- if instanceConfig == nil {
555- c .JSON (http .StatusNotFound , gin.H {"error" : fmt .Sprintf ("Instance '%s' not found" , instanceName ), "code" : 404 })
556- return
557- }
558-
559- // Check if auth file exists
560- if _ , err := os .Stat (instanceConfig .Auth .File ); os .IsNotExist (err ) {
561- c .JSON (http .StatusNotFound , gin.H {"error" : fmt .Sprintf ("Auth file not found for instance '%s'" , instanceName ), "code" : 404 })
562- return
563- }
564-
565- // Read the auth file
566- authData , err := os .ReadFile (instanceConfig .Auth .File )
567- if err != nil {
568- c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Failed to read auth file: %v" , err ), "code" : 500 })
569- return
570- }
571-
572- // Validate the auth JSON format
573- var authInfo chrome.AuthInfo
574- if err = json .Unmarshal (authData , & authInfo ); err != nil {
575- c .JSON (http .StatusInternalServerError , gin.H {"error" : fmt .Sprintf ("Invalid auth file format: %v" , err ), "code" : 500 })
576- return
577- }
578-
579- log .Debugf ("Successfully downloaded auth info for instance '%s' from file: %s" , instanceName , instanceConfig .Auth .File )
580-
581- // Return the auth info as JSON
582- c .Header ("Content-Type" , "application/json" )
583- c .JSON (http .StatusOK , authInfo )
584- }
585-
586572// AuthUploadPage handles the GET /v1/auth/upload endpoint to serve the upload page
587573func (h * APIHandlers ) AuthUploadPage (c * gin.Context ) {
588574 // Serve the HTML content
0 commit comments