11import { useState , useEffect , useCallback } from 'react' ;
22import incidentsData from '../data/incidents.json' ;
33
4+ const statusApiUrl = import . meta. env . VITE_STATUS_API_URL || '' ;
5+
46export type ComponentStatus = {
57 id : string ;
68 name : string ;
@@ -26,73 +28,33 @@ export type StatusData = {
2628 lastUpdated : string ;
2729} ;
2830
29- const MOCK_COMPONENTS : ComponentStatus [ ] = [
30- {
31- id : 'rpc-eth' ,
32- name : 'Ethereum RPC' ,
33- status : 'operational' ,
34- uptime90Days : Array ( 90 ) . fill ( 1 ) ,
35- latencyMs : 42 ,
36- } ,
37- {
38- id : 'rpc-sol' ,
39- name : 'Solana RPC' ,
40- status : 'operational' ,
41- uptime90Days : Array ( 90 ) . fill ( 1 ) ,
42- latencyMs : 28 ,
43- } ,
44- {
45- id : 'scanner' ,
46- name : 'Wraith Scanner' ,
47- status : 'operational' ,
48- uptime90Days : Array ( 90 ) . fill ( 1 ) ,
49- latencyMs : 65 ,
50- } ,
51- {
52- id : 'docs' ,
53- name : 'Documentation' ,
54- status : 'operational' ,
55- uptime90Days : Array ( 90 ) . fill ( 1 ) ,
56- latencyMs : 15 ,
57- } ,
58- {
59- id : 'marketing' ,
60- name : 'Marketing Web' ,
61- status : 'operational' ,
62- uptime90Days : Array ( 90 ) . fill ( 1 ) ,
63- latencyMs : 20 ,
64- } ,
65- ] ;
66-
6731export function useStatus ( ) {
6832 const [ data , setData ] = useState < StatusData | null > ( null ) ;
6933 const [ loading , setLoading ] = useState ( true ) ;
7034 const [ error , setError ] = useState < string | null > ( null ) ;
71- const [ lastUpdated , setLastUpdated ] = useState < string > ( new Date ( ) . toISOString ( ) ) ;
35+ const [ lastUpdated , setLastUpdated ] = useState < string | null > ( null ) ;
7236
7337 const fetchStatus = useCallback ( async ( ) => {
38+ if ( ! statusApiUrl ) {
39+ setData ( null ) ;
40+ setError ( 'Status endpoint unavailable' ) ;
41+ setLoading ( false ) ;
42+ setLastUpdated ( null ) ;
43+ return ;
44+ }
45+
7446 try {
75- // Respect DNT header if enabled in browser
76- const isDnt =
77- navigator . doNotTrack === '1' ||
78- ( window as unknown as { doNotTrack ?: string } ) . doNotTrack === '1' ;
7947 const headers : HeadersInit = {
8048 Accept : 'application/json' ,
8149 } ;
82- if ( isDnt ) {
83- // DNT honored, ensure no cookies are sent/requested
84- }
85-
86- // Try fetching from public status endpoint or fallback gracefully to mock data
87- let apiComponents = MOCK_COMPONENTS ;
88- try {
89- const res = await fetch ( '/api/status' , { headers, credentials : 'omit' } ) ;
90- if ( res . ok ) {
91- const json = await res . json ( ) ;
92- if ( json . components ) apiComponents = json . components ;
93- }
94- } catch {
95- // Fallback gracefully if endpoint is unreachable without spinner-forever
50+ // Status polling sends no telemetry and never includes cookies.
51+ const res = await fetch ( statusApiUrl , { headers, credentials : 'omit' } ) ;
52+ if ( ! res . ok ) throw new Error ( `Status endpoint returned ${ res . status } ` ) ;
53+ const json = await res . json ( ) ;
54+ if ( ! Array . isArray ( json . components ) ) throw new Error ( 'Status response has no components' ) ;
55+ const apiComponents = json . components as ComponentStatus [ ] ;
56+ if ( ! apiComponents . some ( ( component ) => / s t e l l a r / i. test ( component . name ) ) ) {
57+ throw new Error ( 'Status response is missing Stellar' ) ;
9658 }
9759
9860 setData ( {
@@ -106,11 +68,13 @@ export function useStatus() {
10668 lastUpdated : new Date ( ) . toISOString ( ) ,
10769 } ) ;
10870 setError ( null ) ;
71+ setLastUpdated ( new Date ( ) . toISOString ( ) ) ;
10972 } catch ( err ) {
73+ setData ( null ) ;
11074 setError ( err instanceof Error ? err . message : 'Failed to fetch status data' ) ;
75+ setLastUpdated ( null ) ;
11176 } finally {
11277 setLoading ( false ) ;
113- setLastUpdated ( new Date ( ) . toISOString ( ) ) ;
11478 }
11579 } , [ ] ) ;
11680
0 commit comments