@@ -93,6 +93,14 @@ <h1 class="text-lg font-medium flex items-center space-x-4">
9393 </ h1 >
9494
9595 < div class ="ml-auto flex items-center space-x-4 ">
96+ <!-- Token bar -->
97+ < div class ="flex items-center space-x-2 ">
98+ < input id ="token-input " type ="password " placeholder ="API token "
99+ class ="bg-dark border border-border rounded px-2 py-1 text-xs w-48 ">
100+ < button id ="token-save " class ="px-2 py-1 bg-primary text-dark rounded text-xs hover:bg-primarydim "> Set</ button >
101+ < button id ="token-clear " class ="px-2 py-1 border border-border rounded text-xs hover:bg-surface "> Clear</ button >
102+ < span id ="token-status " class ="text-xs "> </ span >
103+ </ div >
96104 <!-- Connection Status -->
97105 < div class ="flex items-center space-x-2 ">
98106 < div id ="ws-status " class ="flex items-center space-x-1 ">
@@ -318,6 +326,47 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
318326 </ main >
319327
320328 < script >
329+ // ========== Auth ==========
330+ function getToken ( ) { return sessionStorage . getItem ( 'crawl4ai_token' ) || '' ; }
331+
332+ function authFetch ( url , opts = { } ) {
333+ const token = getToken ( ) ;
334+ if ( token ) {
335+ opts . headers = { ...opts . headers , 'Authorization' : 'Bearer ' + token } ;
336+ }
337+ return fetch ( url , opts ) ;
338+ }
339+
340+ ( function initTokenBar ( ) {
341+ const input = document . getElementById ( 'token-input' ) ;
342+ const status = document . getElementById ( 'token-status' ) ;
343+ const saved = sessionStorage . getItem ( 'crawl4ai_token' ) ;
344+ if ( saved ) input . value = saved ;
345+
346+ function applyToken ( ) {
347+ const val = input . value . trim ( ) ;
348+ if ( ! val ) return ;
349+ sessionStorage . setItem ( 'crawl4ai_token' , val ) ;
350+ status . textContent = 'Token saved' ;
351+ status . className = 'text-xs text-green-400' ;
352+ setTimeout ( ( ) => { status . textContent = '' ; } , 2000 ) ;
353+ // Reconnect WebSocket with new token
354+ if ( websocket ) { websocket . close ( ) ; }
355+ wsReconnectAttempts = 0 ;
356+ connectWebSocket ( ) ;
357+ }
358+
359+ document . getElementById ( 'token-save' ) . addEventListener ( 'click' , applyToken ) ;
360+ input . addEventListener ( 'keydown' , e => { if ( e . key === 'Enter' ) applyToken ( ) ; } ) ;
361+ document . getElementById ( 'token-clear' ) . addEventListener ( 'click' , ( ) => {
362+ sessionStorage . removeItem ( 'crawl4ai_token' ) ;
363+ input . value = '' ;
364+ status . textContent = 'Token cleared' ;
365+ status . className = 'text-xs text-yellow-400' ;
366+ setTimeout ( ( ) => { status . textContent = '' ; } , 2000 ) ;
367+ } ) ;
368+ } ) ( ) ;
369+
321370 // ========== State Management ==========
322371 let autoRefresh = true ;
323372 let refreshInterval ;
@@ -368,7 +417,9 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
368417 wsReconnectAttempts ++ ;
369418
370419 const protocol = window . location . protocol === 'https:' ? 'wss:' : 'ws:' ;
371- const wsUrl = `${ protocol } //${ window . location . host } /monitor/ws` ;
420+ let wsUrl = `${ protocol } //${ window . location . host } /monitor/ws` ;
421+ const wsToken = getToken ( ) ;
422+ if ( wsToken ) wsUrl += `?token=${ encodeURIComponent ( wsToken ) } ` ;
372423
373424 websocket = new WebSocket ( wsUrl ) ;
374425
@@ -643,7 +694,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
643694
644695 async function fetchHealth ( ) {
645696 try {
646- const res = await fetch ( '/monitor/health' ) ;
697+ const res = await authFetch ( '/monitor/health' ) ;
647698 const data = await res . json ( ) ;
648699
649700 // Container metrics
@@ -691,7 +742,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
691742 async function fetchRequests ( ) {
692743 try {
693744 const filter = document . getElementById ( 'filter-requests' ) ?. value || 'all' ;
694- const res = await fetch ( `/monitor/requests?status=${ filter } &limit=50` ) ;
745+ const res = await authFetch ( `/monitor/requests?status=${ filter } &limit=50` ) ;
695746 const data = await res . json ( ) ;
696747
697748 // Active requests
@@ -735,7 +786,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
735786
736787 async function fetchBrowsers ( ) {
737788 try {
738- const res = await fetch ( '/monitor/browsers' ) ;
789+ const res = await authFetch ( '/monitor/browsers' ) ;
739790 const data = await res . json ( ) ;
740791
741792 document . getElementById ( 'browser-count' ) . textContent = data . summary . total_count ;
@@ -775,7 +826,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
775826
776827 async function fetchJanitorLog ( ) {
777828 try {
778- const res = await fetch ( '/monitor/logs/janitor?limit=100' ) ;
829+ const res = await authFetch ( '/monitor/logs/janitor?limit=100' ) ;
779830 const data = await res . json ( ) ;
780831
781832 const logEl = document . getElementById ( 'janitor-log' ) ;
@@ -803,7 +854,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
803854
804855 async function fetchErrors ( ) {
805856 try {
806- const res = await fetch ( '/monitor/logs/errors?limit=100' ) ;
857+ const res = await authFetch ( '/monitor/logs/errors?limit=100' ) ;
807858 const data = await res . json ( ) ;
808859
809860 const logEl = document . getElementById ( 'errors-log' ) ;
@@ -830,7 +881,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
830881
831882 async function fetchEndpointStats ( ) {
832883 try {
833- const res = await fetch ( '/monitor/endpoints/stats' ) ;
884+ const res = await authFetch ( '/monitor/endpoints/stats' ) ;
834885 const data = await res . json ( ) ;
835886
836887 const tbody = document . getElementById ( 'endpoints-table-body' ) ;
@@ -861,7 +912,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
861912 async function fetchTimeline ( ) {
862913 try {
863914 const metric = document . getElementById ( 'timeline-metric' ) . value ;
864- const res = await fetch ( `/monitor/timeline?metric=${ metric } ` ) ;
915+ const res = await authFetch ( `/monitor/timeline?metric=${ metric } ` ) ;
865916 const data = await res . json ( ) ;
866917
867918 drawTimeline ( data , metric ) ;
@@ -968,7 +1019,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
9681019 if ( ! confirm ( `Kill browser ${ sig } ?` ) ) return ;
9691020
9701021 try {
971- const res = await fetch ( '/monitor/actions/kill_browser' , {
1022+ const res = await authFetch ( '/monitor/actions/kill_browser' , {
9721023 method : 'POST' ,
9731024 headers : { 'Content-Type' : 'application/json' } ,
9741025 body : JSON . stringify ( { sig} )
@@ -986,7 +1037,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
9861037 if ( ! confirm ( `Restart browser ${ sig } ?` ) ) return ;
9871038
9881039 try {
989- const res = await fetch ( '/monitor/actions/restart_browser' , {
1040+ const res = await authFetch ( '/monitor/actions/restart_browser' , {
9901041 method : 'POST' ,
9911042 headers : { 'Content-Type' : 'application/json' } ,
9921043 body : JSON . stringify ( { sig} )
@@ -1004,7 +1055,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
10041055 if ( ! confirm ( 'Force cleanup all cold pool browsers?' ) ) return ;
10051056
10061057 try {
1007- const res = await fetch ( '/monitor/actions/cleanup' , { method : 'POST' } ) ;
1058+ const res = await authFetch ( '/monitor/actions/cleanup' , { method : 'POST' } ) ;
10081059 const data = await res . json ( ) ;
10091060
10101061 showActionStatus ( `✅ Killed ${ data . killed_browsers } browsers` , true ) ;
@@ -1018,7 +1069,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
10181069 if ( ! confirm ( 'Restart permanent browser? This will briefly interrupt service.' ) ) return ;
10191070
10201071 try {
1021- const res = await fetch ( '/monitor/actions/restart_browser' , {
1072+ const res = await authFetch ( '/monitor/actions/restart_browser' , {
10221073 method : 'POST' ,
10231074 headers : { 'Content-Type' : 'application/json' } ,
10241075 body : JSON . stringify ( { sig : 'permanent' } )
@@ -1036,7 +1087,7 @@ <h2 class="text-sm font-medium mb-3 text-accent">Control Actions</h2>
10361087 if ( ! confirm ( 'Reset all endpoint statistics?' ) ) return ;
10371088
10381089 try {
1039- const res = await fetch ( '/monitor/stats/reset' , { method : 'POST' } ) ;
1090+ const res = await authFetch ( '/monitor/stats/reset' , { method : 'POST' } ) ;
10401091 const data = await res . json ( ) ;
10411092
10421093 showActionStatus ( '✅ Stats reset' , true ) ;
0 commit comments