'use client'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import type { ProcessInfo, SystemInfo, LogTailResult } from '@/lib/supervisor/types'; // Query Keys export const supervisorKeys = { all: ['supervisor'] as const, system: () => [...supervisorKeys.all, 'system'] as const, processes: () => [...supervisorKeys.all, 'processes'] as const, process: (name: string) => [...supervisorKeys.processes(), name] as const, logs: (name: string, type: 'stdout' | 'stderr') => [...supervisorKeys.process(name), 'logs', type] as const, }; // API Client Functions async function fetchSystemInfo(): Promise { const response = await fetch('/api/supervisor/system'); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to fetch system info'); } return response.json(); } async function fetchProcesses(): Promise { const response = await fetch('/api/supervisor/processes'); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to fetch processes'); } return response.json(); } async function fetchProcessInfo(name: string): Promise { const response = await fetch(`/api/supervisor/processes/${encodeURIComponent(name)}`); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to fetch process info'); } return response.json(); } async function fetchProcessLogs( name: string, type: 'stdout' | 'stderr', offset: number = -4096, length: number = 4096 ): Promise { const response = await fetch( `/api/supervisor/processes/${encodeURIComponent(name)}/logs/${type}?offset=${offset}&length=${length}` ); if (!response.ok) { const error = await response.json(); throw new Error(error.error || `Failed to fetch ${type} logs`); } return response.json(); } async function startProcess(name: string, wait: boolean = true): Promise<{ success: boolean; message: string }> { const response = await fetch(`/api/supervisor/processes/${encodeURIComponent(name)}/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ wait }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to start process'); } return response.json(); } async function stopProcess(name: string, wait: boolean = true): Promise<{ success: boolean; message: string }> { const response = await fetch(`/api/supervisor/processes/${encodeURIComponent(name)}/stop`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ wait }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to stop process'); } return response.json(); } async function restartProcess(name: string): Promise<{ success: boolean; message: string }> { const response = await fetch(`/api/supervisor/processes/${encodeURIComponent(name)}/restart`, { method: 'POST', }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to restart process'); } return response.json(); } // Custom Hooks export function useSystemInfo() { return useQuery({ queryKey: supervisorKeys.system(), queryFn: fetchSystemInfo, refetchInterval: 5000, // Refetch every 5 seconds }); } export function useProcesses(options?: { refetchInterval?: number }) { return useQuery({ queryKey: supervisorKeys.processes(), queryFn: fetchProcesses, refetchInterval: options?.refetchInterval ?? 3000, // Default 3 seconds }); } export function useProcessInfo(name: string, enabled: boolean = true) { return useQuery({ queryKey: supervisorKeys.process(name), queryFn: () => fetchProcessInfo(name), enabled, refetchInterval: 3000, }); } export function useProcessLogs( name: string, type: 'stdout' | 'stderr', options?: { offset?: number; length?: number; enabled?: boolean; refetchInterval?: number; } ) { return useQuery({ queryKey: [...supervisorKeys.logs(name, type), options?.offset, options?.length], queryFn: () => fetchProcessLogs(name, type, options?.offset, options?.length), enabled: options?.enabled ?? true, refetchInterval: options?.refetchInterval ?? 2000, }); } export function useStartProcess() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ name, wait }: { name: string; wait?: boolean }) => startProcess(name, wait), onSuccess: (data, variables) => { toast.success(data.message); // Invalidate and refetch queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() }); queryClient.invalidateQueries({ queryKey: supervisorKeys.process(variables.name) }); }, onError: (error: Error) => { toast.error(`Failed to start process: ${error.message}`); }, }); } export function useStopProcess() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ name, wait }: { name: string; wait?: boolean }) => stopProcess(name, wait), onSuccess: (data, variables) => { toast.success(data.message); queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() }); queryClient.invalidateQueries({ queryKey: supervisorKeys.process(variables.name) }); }, onError: (error: Error) => { toast.error(`Failed to stop process: ${error.message}`); }, }); } export function useRestartProcess() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (name: string) => restartProcess(name), onSuccess: (data, name) => { toast.success(data.message); queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() }); queryClient.invalidateQueries({ queryKey: supervisorKeys.process(name) }); }, onError: (error: Error) => { toast.error(`Failed to restart process: ${error.message}`); }, }); }