150 lines
4.3 KiB
TypeScript
150 lines
4.3 KiB
TypeScript
/**
|
|
* Hook for polling OAuth token alerts and showing toast notifications
|
|
*
|
|
* This hook periodically checks for new OAuth token failure alerts
|
|
* and displays toast notifications when detected.
|
|
*/
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import { billingService } from '../services/billingService';
|
|
import { UsageAlert } from '../types/billing';
|
|
import { showToastNotification } from '../utils/toastNotifications';
|
|
|
|
interface UseOAuthTokenAlertsOptions {
|
|
/**
|
|
* Polling interval in milliseconds
|
|
* @default 60000 (1 minute)
|
|
*/
|
|
interval?: number;
|
|
|
|
/**
|
|
* Whether to enable polling
|
|
* @default true
|
|
*/
|
|
enabled?: boolean;
|
|
|
|
/**
|
|
* Authenticated user ID from Clerk; polling is skipped when unavailable
|
|
*/
|
|
userId?: string;
|
|
}
|
|
|
|
/**
|
|
* Hook to poll for OAuth token alerts and show toast notifications
|
|
*
|
|
* Polls the UsageAlert API for new OAuth token alerts (oauth_token_failure, oauth_token_warning)
|
|
* and displays toast notifications when new unread alerts are detected.
|
|
*
|
|
* @param options Polling configuration options
|
|
* @returns Object with polling state and controls
|
|
*/
|
|
export function useOAuthTokenAlerts(options: UseOAuthTokenAlertsOptions = {}) {
|
|
const {
|
|
interval = 60000, // 1 minute default
|
|
enabled = true,
|
|
userId
|
|
} = options;
|
|
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
const lastAlertIdsRef = useRef<Set<number>>(new Set());
|
|
const isPollingRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
if (!userId) {
|
|
console.debug('useOAuthTokenAlerts: No user ID available, skipping polling');
|
|
return;
|
|
}
|
|
|
|
const pollAlerts = async () => {
|
|
// Prevent concurrent polls
|
|
if (isPollingRef.current) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isPollingRef.current = true;
|
|
|
|
// Fetch unread alerts only
|
|
const alerts = await billingService.getUsageAlerts(userId, true);
|
|
|
|
// Filter for OAuth token alerts
|
|
const oauthAlerts = alerts.filter(
|
|
(alert: UsageAlert) =>
|
|
alert.type === 'oauth_token_failure' ||
|
|
alert.type === 'oauth_token_warning'
|
|
);
|
|
|
|
// Find new alerts (not in our tracked set)
|
|
const newAlerts = oauthAlerts.filter(
|
|
(alert: UsageAlert) => !lastAlertIdsRef.current.has(alert.id)
|
|
);
|
|
|
|
// Show toast notifications for new alerts
|
|
for (const alert of newAlerts) {
|
|
// Map severity to notification type
|
|
const notificationType =
|
|
alert.severity === 'error' ? 'error' :
|
|
alert.severity === 'warning' ? 'warning' :
|
|
'info';
|
|
|
|
// Show toast notification
|
|
showToastNotification(alert.message, notificationType);
|
|
|
|
// Track this alert ID
|
|
lastAlertIdsRef.current.add(alert.id);
|
|
|
|
console.log(`OAuth token alert notification: ${alert.title}`, {
|
|
type: alert.type,
|
|
severity: alert.severity,
|
|
platform: extractPlatformFromTitle(alert.title)
|
|
});
|
|
}
|
|
|
|
// Update tracked alert IDs (keep only current alerts to handle deletions)
|
|
lastAlertIdsRef.current = new Set(oauthAlerts.map((a: UsageAlert) => a.id));
|
|
|
|
} catch (error) {
|
|
console.error('Error polling OAuth token alerts:', error);
|
|
// Don't show error to user - this is background polling
|
|
} finally {
|
|
isPollingRef.current = false;
|
|
}
|
|
};
|
|
|
|
// Poll immediately on mount
|
|
pollAlerts();
|
|
|
|
// Set up periodic polling
|
|
intervalRef.current = setInterval(pollAlerts, interval);
|
|
|
|
// Cleanup on unmount
|
|
return () => {
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
intervalRef.current = null;
|
|
}
|
|
};
|
|
}, [enabled, interval, userId]);
|
|
|
|
return {
|
|
isPolling: isPollingRef.current
|
|
};
|
|
}
|
|
|
|
// Note: showToastNotification is now imported from utils/toastNotifications.ts
|
|
// This ensures consistent toast notifications across the app
|
|
|
|
/**
|
|
* Extract platform name from alert title
|
|
* Used for logging/debugging
|
|
*/
|
|
function extractPlatformFromTitle(title: string): string {
|
|
const match = title.match(/^(Google Search Console|Bing Webmaster Tools|WordPress|Wix)/);
|
|
return match ? match[1] : 'Unknown';
|
|
}
|
|
|