Fix: Uptime history timestamp and service ID
This commit is contained in:
@@ -14,100 +14,102 @@ interface UseUptimeDataProps {
|
|||||||
export const useUptimeData = ({ serviceId, serviceType, status, interval }: UseUptimeDataProps) => {
|
export const useUptimeData = ({ serviceId, serviceType, status, interval }: UseUptimeDataProps) => {
|
||||||
const [historyItems, setHistoryItems] = useState<UptimeData[]>([]);
|
const [historyItems, setHistoryItems] = useState<UptimeData[]>([]);
|
||||||
|
|
||||||
// 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({
|
const { data: uptimeData, isLoading, error, isFetching, refetch } = useQuery({
|
||||||
queryKey: ['uptimeHistory', serviceId, serviceType],
|
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,
|
enabled: !!serviceId,
|
||||||
refetchInterval: 30000, // Refresh every 30 seconds
|
refetchInterval: 30000,
|
||||||
staleTime: 15000, // Consider data fresh for 15 seconds
|
staleTime: 15000,
|
||||||
placeholderData: (previousData) => previousData, // Show previous data while refetching
|
placeholderData: (previousData) => previousData,
|
||||||
retry: 3, // Retry failed requests three times
|
retry: 3,
|
||||||
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10000), // Exponential backoff with max 10s
|
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10000),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter uptime data to respect the service interval
|
// Filter and process uptime data
|
||||||
const filterUptimeDataByInterval = (data: UptimeData[], intervalSeconds: number): UptimeData[] => {
|
const processUptimeData = (data: UptimeData[], intervalSeconds: number): UptimeData[] => {
|
||||||
if (!data || data.length === 0) return [];
|
if (!data || data.length === 0) return [];
|
||||||
|
|
||||||
|
console.log(`Processing ${data.length} uptime records for service ${serviceId}`);
|
||||||
|
|
||||||
// Sort data by timestamp (newest first)
|
// Sort data by timestamp (newest first)
|
||||||
const sortedData = [...data].sort((a, b) =>
|
const sortedData = [...data].sort((a, b) =>
|
||||||
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
const filtered: UptimeData[] = [];
|
// Take the most recent 20 records to ensure we have enough data
|
||||||
let lastIncludedTime: number | null = null;
|
const recentData = sortedData.slice(0, 20);
|
||||||
const intervalMs = intervalSeconds * 1000; // Convert to milliseconds
|
|
||||||
|
|
||||||
// Include the most recent record first
|
console.log(`Using ${recentData.length} most recent records`);
|
||||||
if (sortedData.length > 0) {
|
|
||||||
filtered.push(sortedData[0]);
|
|
||||||
lastIncludedTime = new Date(sortedData[0].timestamp).getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter subsequent records to maintain proper interval spacing
|
return recentData;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update history items when data changes
|
// Update history items when data changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uptimeData && uptimeData.length > 0) {
|
if (uptimeData && uptimeData.length > 0) {
|
||||||
// Filter data based on the service interval
|
console.log(`Received ${uptimeData.length} uptime records for service ${serviceId}`);
|
||||||
const filteredData = filterUptimeDataByInterval(uptimeData, interval);
|
const processedData = processUptimeData(uptimeData, interval);
|
||||||
setHistoryItems(filteredData);
|
setHistoryItems(processedData);
|
||||||
} else if (status === "paused" || (uptimeData && uptimeData.length === 0)) {
|
} else if (!serviceId || (uptimeData && uptimeData.length === 0)) {
|
||||||
// For paused services with no history, or empty history data, show all as paused
|
// 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")
|
const statusValue = (status === "up" || status === "down" || status === "warning" || status === "paused")
|
||||||
? status
|
? status
|
||||||
: "paused"; // Default to paused if not a valid status
|
: "paused";
|
||||||
|
|
||||||
const placeholderHistory: UptimeData[] = Array(20).fill(null).map((_, index) => ({
|
const placeholderHistory: UptimeData[] = Array(20).fill(null).map((_, index) => ({
|
||||||
id: `placeholder-${index}`,
|
id: `placeholder-${serviceId}-${index}`,
|
||||||
serviceId: serviceId || "",
|
serviceId: serviceId || "",
|
||||||
timestamp: new Date(Date.now() - (index * interval * 1000)).toISOString(),
|
timestamp: new Date(Date.now() - (index * interval * 1000)).toISOString(),
|
||||||
status: statusValue as "up" | "down" | "warning" | "paused",
|
status: statusValue as "up" | "down" | "warning" | "paused",
|
||||||
responseTime: 0
|
responseTime: 0
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setHistoryItems(placeholderHistory);
|
setHistoryItems(placeholderHistory);
|
||||||
}
|
}
|
||||||
}, [uptimeData, serviceId, status, interval]);
|
}, [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 getDisplayItems = (): UptimeData[] => {
|
||||||
const displayItems = [...historyItems];
|
const items = [...historyItems];
|
||||||
if (displayItems.length < 20) {
|
|
||||||
const lastItem = displayItems.length > 0 ? displayItems[displayItems.length - 1] : null;
|
// 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 :
|
const lastStatus = lastItem ? lastItem.status :
|
||||||
(status === "up" || status === "down" || status === "warning" || status === "paused") ?
|
(status === "up" || status === "down" || status === "warning" || status === "paused") ?
|
||||||
status as "up" | "down" | "warning" | "paused" : "paused";
|
status as "up" | "down" | "warning" | "paused" : "paused";
|
||||||
|
|
||||||
// Generate padding items with proper time spacing
|
const paddingCount = 20 - items.length;
|
||||||
const paddingItems: UptimeData[] = Array(20 - displayItems.length).fill(null).map((_, index) => {
|
const paddingItems: UptimeData[] = Array(paddingCount).fill(null).map((_, index) => {
|
||||||
const baseTime = lastItem ? new Date(lastItem.timestamp).getTime() : Date.now();
|
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 {
|
return {
|
||||||
id: `padding-${index}`,
|
id: `padding-${serviceId}-${index}`,
|
||||||
serviceId: serviceId || "",
|
serviceId: serviceId || "",
|
||||||
timestamp: new Date(baseTime - timeOffset).toISOString(),
|
timestamp: new Date(baseTime - timeOffset).toISOString(),
|
||||||
status: lastStatus,
|
status: lastStatus,
|
||||||
responseTime: 0
|
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 {
|
return {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const UptimeSummary = ({ uptime, interval }: UptimeSummaryProps) => {
|
|||||||
{Math.round(uptime)}% uptime
|
{Math.round(uptime)}% uptime
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
Last 20 checks ({interval}s interval)
|
Last 20 checks
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ export const uptimeService = {
|
|||||||
serviceType?: string
|
serviceType?: string
|
||||||
): Promise<UptimeData[]> {
|
): Promise<UptimeData[]> {
|
||||||
try {
|
try {
|
||||||
|
if (!serviceId) {
|
||||||
|
console.log('No serviceId provided to getUptimeHistory');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`;
|
const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`;
|
||||||
|
|
||||||
// Check cache
|
// Check cache
|
||||||
@@ -77,6 +82,7 @@ export const uptimeService = {
|
|||||||
const collection = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data';
|
const collection = serviceType ? getCollectionForServiceType(serviceType) : 'uptime_data';
|
||||||
console.log(`Fetching uptime history for service ${serviceId} from collection ${collection}, limit: ${limit}`);
|
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}'`;
|
let filter = `service_id='${serviceId}'`;
|
||||||
|
|
||||||
// Add date range filtering if provided
|
// Add date range filtering if provided
|
||||||
@@ -90,7 +96,7 @@ export const uptimeService = {
|
|||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
filter: filter,
|
filter: filter,
|
||||||
sort: '-timestamp',
|
sort: '-timestamp', // Sort by timestamp descending (newest first)
|
||||||
$autoCancel: false,
|
$autoCancel: false,
|
||||||
$cancelKey: `uptime_history_${serviceId}_${Date.now()}`
|
$cancelKey: `uptime_history_${serviceId}_${Date.now()}`
|
||||||
};
|
};
|
||||||
@@ -104,27 +110,15 @@ export const uptimeService = {
|
|||||||
if (response.items.length > 0) {
|
if (response.items.length > 0) {
|
||||||
console.log(`Date range in results: ${response.items[response.items.length - 1].timestamp} to ${response.items[0].timestamp}`);
|
console.log(`Date range in results: ${response.items[response.items.length - 1].timestamp} to ${response.items[0].timestamp}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`No records found for filter: ${filter} in collection: ${collection}`);
|
console.log(`No records found for service_id '${serviceId}' 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}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform the response items to UptimeData format
|
||||||
const uptimeData = response.items.map(item => ({
|
const uptimeData = response.items.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
serviceId: item.service_id,
|
serviceId: item.service_id,
|
||||||
timestamp: item.timestamp,
|
timestamp: item.timestamp,
|
||||||
status: item.status,
|
status: item.status as "up" | "down" | "warning" | "paused",
|
||||||
responseTime: item.response_time || 0,
|
responseTime: item.response_time || 0,
|
||||||
date: item.timestamp,
|
date: item.timestamp,
|
||||||
uptime: 100
|
uptime: 100
|
||||||
@@ -139,7 +133,7 @@ export const uptimeService = {
|
|||||||
|
|
||||||
return uptimeData;
|
return uptimeData;
|
||||||
} catch (error) {
|
} 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
|
// Try to return cached data as fallback
|
||||||
const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`;
|
const cacheKey = `uptime_${serviceId}_${limit}_${startDate?.toISOString() || ''}_${endDate?.toISOString() || ''}_${serviceType || 'default'}`;
|
||||||
@@ -149,7 +143,9 @@ export const uptimeService = {
|
|||||||
return cached.data;
|
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 [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user