diff --git a/application/src/components/services/hooks/useUptimeData.ts b/application/src/components/services/hooks/useUptimeData.ts index 92c860d..91e5391 100644 --- a/application/src/components/services/hooks/useUptimeData.ts +++ b/application/src/components/services/hooks/useUptimeData.ts @@ -14,100 +14,102 @@ interface UseUptimeDataProps { export const useUptimeData = ({ serviceId, serviceType, status, interval }: UseUptimeDataProps) => { const [historyItems, setHistoryItems] = useState([]); - // Fetch real uptime history data if serviceId is provided with improved caching and error handling + // Fetch real uptime history data if serviceId is provided const { data: uptimeData, isLoading, error, isFetching, refetch } = useQuery({ queryKey: ['uptimeHistory', serviceId, serviceType], - queryFn: () => serviceId ? uptimeService.getUptimeHistory(serviceId, 50, undefined, undefined, serviceType) : Promise.resolve([]), + queryFn: () => { + if (!serviceId) { + console.log('No serviceId provided, skipping fetch'); + return Promise.resolve([]); + } + console.log(`Fetching uptime data for service ${serviceId} of type ${serviceType}`); + return uptimeService.getUptimeHistory(serviceId, 50, undefined, undefined, serviceType); + }, enabled: !!serviceId, - refetchInterval: 30000, // Refresh every 30 seconds - staleTime: 15000, // Consider data fresh for 15 seconds - placeholderData: (previousData) => previousData, // Show previous data while refetching - retry: 3, // Retry failed requests three times - retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10000), // Exponential backoff with max 10s + refetchInterval: 30000, + staleTime: 15000, + placeholderData: (previousData) => previousData, + retry: 3, + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10000), }); - // Filter uptime data to respect the service interval - const filterUptimeDataByInterval = (data: UptimeData[], intervalSeconds: number): UptimeData[] => { + // Filter and process uptime data + const processUptimeData = (data: UptimeData[], intervalSeconds: number): UptimeData[] => { if (!data || data.length === 0) return []; + console.log(`Processing ${data.length} uptime records for service ${serviceId}`); + // Sort data by timestamp (newest first) const sortedData = [...data].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() ); - const filtered: UptimeData[] = []; - let lastIncludedTime: number | null = null; - const intervalMs = intervalSeconds * 1000; // Convert to milliseconds + // Take the most recent 20 records to ensure we have enough data + const recentData = sortedData.slice(0, 20); - // Include the most recent record first - if (sortedData.length > 0) { - filtered.push(sortedData[0]); - lastIncludedTime = new Date(sortedData[0].timestamp).getTime(); - } + console.log(`Using ${recentData.length} most recent records`); - // Filter subsequent records to maintain proper interval spacing - for (let i = 1; i < sortedData.length && filtered.length < 20; i++) { - const currentTime = new Date(sortedData[i].timestamp).getTime(); - - // Only include if enough time has passed since the last included record - if (lastIncludedTime && (lastIncludedTime - currentTime) >= intervalMs) { - filtered.push(sortedData[i]); - lastIncludedTime = currentTime; - } - } - - return filtered; + return recentData; }; // Update history items when data changes useEffect(() => { if (uptimeData && uptimeData.length > 0) { - // Filter data based on the service interval - const filteredData = filterUptimeDataByInterval(uptimeData, interval); - setHistoryItems(filteredData); - } else if (status === "paused" || (uptimeData && uptimeData.length === 0)) { - // For paused services with no history, or empty history data, show all as paused + console.log(`Received ${uptimeData.length} uptime records for service ${serviceId}`); + const processedData = processUptimeData(uptimeData, interval); + setHistoryItems(processedData); + } else if (!serviceId || (uptimeData && uptimeData.length === 0)) { + // Generate placeholder data when no real data is available + console.log(`No uptime data available for service ${serviceId}, generating placeholder`); + const statusValue = (status === "up" || status === "down" || status === "warning" || status === "paused") ? status - : "paused"; // Default to paused if not a valid status + : "paused"; const placeholderHistory: UptimeData[] = Array(20).fill(null).map((_, index) => ({ - id: `placeholder-${index}`, + id: `placeholder-${serviceId}-${index}`, serviceId: serviceId || "", timestamp: new Date(Date.now() - (index * interval * 1000)).toISOString(), status: statusValue as "up" | "down" | "warning" | "paused", responseTime: 0 })); + setHistoryItems(placeholderHistory); } }, [uptimeData, serviceId, status, interval]); - // Ensure we always have 20 items by padding with the last known status + // Ensure we always have exactly 20 items for consistent display const getDisplayItems = (): UptimeData[] => { - const displayItems = [...historyItems]; - if (displayItems.length < 20) { - const lastItem = displayItems.length > 0 ? displayItems[displayItems.length - 1] : null; + const items = [...historyItems]; + + // If we have fewer than 20 items, pad with older placeholder data + if (items.length < 20) { + const lastItem = items.length > 0 ? items[items.length - 1] : null; const lastStatus = lastItem ? lastItem.status : (status === "up" || status === "down" || status === "warning" || status === "paused") ? status as "up" | "down" | "warning" | "paused" : "paused"; - // Generate padding items with proper time spacing - const paddingItems: UptimeData[] = Array(20 - displayItems.length).fill(null).map((_, index) => { + const paddingCount = 20 - items.length; + const paddingItems: UptimeData[] = Array(paddingCount).fill(null).map((_, index) => { const baseTime = lastItem ? new Date(lastItem.timestamp).getTime() : Date.now(); - const timeOffset = (index + 1) * interval * 1000; // Respect the interval + const timeOffset = (index + 1) * interval * 1000; return { - id: `padding-${index}`, + id: `padding-${serviceId}-${index}`, serviceId: serviceId || "", timestamp: new Date(baseTime - timeOffset).toISOString(), status: lastStatus, responseTime: 0 }; }); - displayItems.push(...paddingItems); + + items.push(...paddingItems); } - return displayItems.slice(0, 20); + // Return exactly 20 items, sorted by timestamp (newest first) + return items.slice(0, 20).sort((a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); }; return { diff --git a/application/src/components/services/uptime/UptimeSummary.tsx b/application/src/components/services/uptime/UptimeSummary.tsx index b74e050..9f4eb4e 100644 --- a/application/src/components/services/uptime/UptimeSummary.tsx +++ b/application/src/components/services/uptime/UptimeSummary.tsx @@ -13,7 +13,7 @@ export const UptimeSummary = ({ uptime, interval }: UptimeSummaryProps) => { {Math.round(uptime)}% uptime - Last 20 checks ({interval}s interval) + Last 20 checks ); diff --git a/application/src/services/uptimeService.ts b/application/src/services/uptimeService.ts index 7a4d54e..5a6808c 100644 --- a/application/src/services/uptimeService.ts +++ b/application/src/services/uptimeService.ts @@ -64,6 +64,11 @@ export const uptimeService = { serviceType?: string ): Promise { try { + if (!serviceId) { + console.log('No serviceId provided to getUptimeHistory'); + return []; + } + const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`; // Check cache @@ -77,6 +82,7 @@ export const uptimeService = { const collection = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data'; console.log(`Fetching uptime history for service ${serviceId} from collection ${collection}, limit: ${limit}`); + // Build filter to get records for specific service_id let filter = `service_id='${serviceId}'`; // Add date range filtering if provided @@ -90,7 +96,7 @@ export const uptimeService = { const options = { filter: filter, - sort: '-timestamp', + sort: '-timestamp', // Sort by timestamp descending (newest first) $autoCancel: false, $cancelKey: `uptime_history_${serviceId}_${Date.now()}` }; @@ -104,27 +110,15 @@ export const uptimeService = { if (response.items.length > 0) { console.log(`Date range in results: ${response.items[response.items.length - 1].timestamp} to ${response.items[0].timestamp}`); } else { - console.log(`No records found for filter: ${filter} in collection: ${collection}`); - - // Try a fallback query without date filter to see if there's any data at all - const fallbackResponse = await pb.collection(collection).getList(1, 10, { - filter: `service_id='${serviceId}'`, - sort: '-timestamp', - $autoCancel: false - }); - - console.log(`Fallback query found ${fallbackResponse.items.length} total records for service in ${collection}`); - if (fallbackResponse.items.length > 0) { - console.log(`Latest record timestamp: ${fallbackResponse.items[0].timestamp}`); - console.log(`Oldest record timestamp: ${fallbackResponse.items[fallbackResponse.items.length - 1].timestamp}`); - } + console.log(`No records found for service_id '${serviceId}' in collection: ${collection}`); } + // Transform the response items to UptimeData format const uptimeData = response.items.map(item => ({ id: item.id, serviceId: item.service_id, timestamp: item.timestamp, - status: item.status, + status: item.status as "up" | "down" | "warning" | "paused", responseTime: item.response_time || 0, date: item.timestamp, uptime: 100 @@ -139,7 +133,7 @@ export const uptimeService = { return uptimeData; } catch (error) { - console.error("Error fetching uptime history:", error); + console.error(`Error fetching uptime history for service ${serviceId}:`, error); // Try to return cached data as fallback const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`; @@ -149,7 +143,9 @@ export const uptimeService = { return cached.data; } - throw new Error('Failed to load uptime history.'); + // Return empty array instead of throwing to prevent UI crashes + console.log(`Returning empty array for service ${serviceId} due to fetch error`); + return []; } } }; \ No newline at end of file