144 lines
4.6 KiB
TypeScript
144 lines
4.6 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
import { useAuth } from '@clerk/clerk-react';
|
|
import { showToastNotification } from '../utils/toastNotifications';
|
|
import { getTasksNeedingIntervention, TaskNeedingIntervention } from '../api/schedulerDashboard';
|
|
import { isBackendCooldownActive, logBackendCooldownSkipOnce } from '../api/client';
|
|
import { isPodcastOnlyDemoMode } from '../utils/demoMode';
|
|
|
|
/**
|
|
* Hook to poll for tasks needing intervention and show toast notifications
|
|
*/
|
|
export function useSchedulerTaskAlerts(options: {
|
|
enabled?: boolean;
|
|
interval?: number;
|
|
} = {}) {
|
|
const { enabled = true, interval = 60000 } = options;
|
|
const { userId, getToken } = useAuth();
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
const lastAlertTimeRef = useRef<number>(0);
|
|
const isPollingRef = useRef(false);
|
|
const shownTaskIdsRef = useRef<Set<number>>(new Set());
|
|
|
|
useEffect(() => {
|
|
// Skip scheduler alerts in podcast-only mode (endpoint not available)
|
|
if (isPodcastOnlyDemoMode()) {
|
|
return;
|
|
}
|
|
|
|
if (!enabled || !userId) {
|
|
return;
|
|
}
|
|
|
|
const pollAlerts = async () => {
|
|
if (isPollingRef.current) {
|
|
return;
|
|
}
|
|
|
|
if (isBackendCooldownActive()) {
|
|
logBackendCooldownSkipOnce('useSchedulerTaskAlerts');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isPollingRef.current = true;
|
|
|
|
// Fetch tasks needing intervention
|
|
const tasks: TaskNeedingIntervention[] = await getTasksNeedingIntervention(userId);
|
|
|
|
// Show toast only for critical failures (API limits) - other failures are shown in dedicated section
|
|
for (const task of tasks) {
|
|
// Only show alert once per task
|
|
if (shownTaskIdsRef.current.has(task.task_id)) {
|
|
continue;
|
|
}
|
|
|
|
// Only show toast for critical failures (API limits) and if failure is very recent (within last 10 minutes)
|
|
const failureReason = task.failure_pattern?.failure_reason || 'unknown';
|
|
if (task.last_failure && failureReason === 'api_limit') {
|
|
const failureTime = new Date(task.last_failure).getTime();
|
|
const tenMinutesAgo = Date.now() - 10 * 60 * 1000;
|
|
|
|
if (failureTime > tenMinutesAgo) {
|
|
showTaskAlert(task);
|
|
shownTaskIdsRef.current.add(task.task_id);
|
|
lastAlertTimeRef.current = Math.max(
|
|
lastAlertTimeRef.current,
|
|
failureTime
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error polling scheduler task alerts:', error);
|
|
// Don't show error to user - this is background polling
|
|
} finally {
|
|
isPollingRef.current = false;
|
|
}
|
|
};
|
|
|
|
// Poll immediately
|
|
pollAlerts();
|
|
|
|
// Set up periodic polling
|
|
intervalRef.current = setInterval(pollAlerts, interval);
|
|
|
|
return () => {
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
}
|
|
};
|
|
}, [enabled, userId, interval]);
|
|
|
|
return {
|
|
isPolling: isPollingRef.current
|
|
};
|
|
}
|
|
|
|
function showTaskAlert(task: {
|
|
task_id: number;
|
|
task_type: string;
|
|
platform?: string;
|
|
website_url?: string;
|
|
failure_pattern: {
|
|
consecutive_failures: number;
|
|
failure_reason: string;
|
|
};
|
|
}) {
|
|
const failureReason = task.failure_pattern?.failure_reason || 'unknown';
|
|
const consecutiveFailures = task.failure_pattern?.consecutive_failures || 0;
|
|
|
|
let message = '';
|
|
let type: 'error' | 'warning' = 'error';
|
|
|
|
if (failureReason === 'api_limit') {
|
|
message = `Task "${getTaskDisplayName(task)}" has failed ${consecutiveFailures} times due to API limits. Manual intervention required.`;
|
|
type = 'error';
|
|
} else if (failureReason === 'auth_error') {
|
|
message = `Task "${getTaskDisplayName(task)}" has failed ${consecutiveFailures} times due to authentication issues. Please check your credentials.`;
|
|
type = 'warning';
|
|
} else {
|
|
message = `Task "${getTaskDisplayName(task)}" has failed ${consecutiveFailures} times and needs manual intervention.`;
|
|
type = 'error';
|
|
}
|
|
|
|
// Use existing toast function
|
|
showToastNotification(message, type);
|
|
}
|
|
|
|
function getTaskDisplayName(task: {
|
|
task_type: string;
|
|
platform?: string;
|
|
website_url?: string;
|
|
}): string {
|
|
if (task.task_type === 'oauth_token_monitoring') {
|
|
return `OAuth ${task.platform?.toUpperCase() || 'Unknown'}`;
|
|
} else if (task.task_type === 'website_analysis') {
|
|
const url = task.website_url || 'Unknown';
|
|
return `Website Analysis (${url.length > 30 ? url.substring(0, 30) + '...' : url})`;
|
|
} else if (task.task_type.includes('_insights')) {
|
|
return `${task.platform?.toUpperCase() || 'Unknown'} Insights`;
|
|
}
|
|
return task.task_type;
|
|
}
|
|
|