| import { |
| useInfiniteQuery, |
| useMutation, |
| useQuery, |
| useQueryClient, |
| } from "@tanstack/react-query"; |
| import * as React from "react"; |
| import type { JobStatus } from "@/core/types"; |
| import { api } from "./api"; |
|
|
| |
| export type WorkbenchConfig = Awaited<ReturnType<typeof api.getConfig>>; |
|
|
| |
| export const queryKeys = { |
| config: ["config"] as const, |
| overview: ["overview"] as const, |
| queueNames: ["queue-names"] as const, |
| queues: ["queues"] as const, |
| queue: (name: string) => ["queue", name] as const, |
| jobs: (queueName: string, status?: JobStatus, sort?: string) => |
| ["jobs", queueName, status, sort] as const, |
| jobsAll: (queueName: string) => ["jobs", queueName] as const, |
| job: (queueName: string, jobId: string) => ["job", queueName, jobId] as const, |
| runs: ( |
| sort?: string, |
| filters?: { |
| status?: JobStatus; |
| tags?: Record<string, string>; |
| text?: string; |
| timeRange?: { start: number; end: number }; |
| }, |
| ) => ["runs", sort, filters] as const, |
| runsAll: ["runs"] as const, |
| schedulers: { |
| repeatable: (sort?: string) => ["schedulers", "repeatable", sort] as const, |
| delayed: (sort?: string) => ["schedulers", "delayed", sort] as const, |
| all: ["schedulers"] as const, |
| }, |
| search: (query: string) => ["search", query] as const, |
| tagValues: (field: string) => ["tagValues", field] as const, |
| metrics: ["metrics"] as const, |
| activity: ["activity"] as const, |
| flows: ["flows"] as const, |
| flow: (queueName: string, jobId: string) => |
| ["flow", queueName, jobId] as const, |
| }; |
|
|
| |
| |
| |
| export function useConfig() { |
| return useQuery({ |
| queryKey: queryKeys.config, |
| queryFn: () => api.getConfig(), |
| staleTime: Number.POSITIVE_INFINITY, |
| }); |
| } |
|
|
| |
| |
| |
| export function useOverview() { |
| return useQuery({ |
| queryKey: queryKeys.overview, |
| queryFn: ({ signal }) => api.getOverview(signal), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useCounts() { |
| return useQuery({ |
| queryKey: ["counts"], |
| queryFn: ({ signal }) => api.getCounts(signal), |
| refetchInterval: 2000, |
| staleTime: 1000, |
| }); |
| } |
|
|
| |
| |
| |
| |
| export function useQueueNames() { |
| return useQuery({ |
| queryKey: queryKeys.queueNames, |
| queryFn: ({ signal }) => api.getQueueNames(signal), |
| staleTime: 60000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useQueues() { |
| return useQuery({ |
| queryKey: queryKeys.queues, |
| queryFn: ({ signal }) => api.getQueues(signal), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| |
| export function useQueueInfo(queueName: string) { |
| const { data: queues } = useQueues(); |
| return React.useMemo( |
| () => queues?.find((q) => q.name === queueName), |
| [queues, queueName], |
| ); |
| } |
|
|
| |
| |
| |
| export function useMetrics() { |
| return useQuery({ |
| queryKey: queryKeys.metrics, |
| queryFn: ({ signal }) => api.getMetrics(signal), |
| refetchInterval: 30000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useActivityStats() { |
| return useQuery({ |
| queryKey: queryKeys.activity, |
| queryFn: ({ signal }) => api.getActivityStats(signal), |
| refetchInterval: 30000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useJobs(queueName: string, status?: JobStatus, sort?: string) { |
| return useInfiniteQuery({ |
| queryKey: queryKeys.jobs(queueName, status, sort), |
| queryFn: ({ pageParam }) => |
| api.getJobs(queueName, { status, limit: 50, cursor: pageParam, sort }), |
| getNextPageParam: (lastPage) => lastPage.cursor, |
| initialPageParam: undefined as string | undefined, |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useJob(queueName: string, jobId: string) { |
| return useQuery({ |
| queryKey: queryKeys.job(queueName, jobId), |
| queryFn: () => api.getJob(queueName, jobId), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| |
| export function useRuns( |
| sort?: string, |
| filters?: { |
| status?: JobStatus; |
| tags?: Record<string, string>; |
| text?: string; |
| timeRange?: { start: number; end: number }; |
| }, |
| ) { |
| return useInfiniteQuery({ |
| queryKey: queryKeys.runs(sort, filters), |
| queryFn: ({ pageParam, signal }) => |
| api.getRuns({ limit: 30, cursor: pageParam, sort, ...filters }, signal), |
| getNextPageParam: (lastPage) => lastPage.cursor, |
| initialPageParam: undefined as string | undefined, |
| refetchInterval: 5000, |
| staleTime: 3000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useRepeatableSchedulers(sort?: string) { |
| return useQuery({ |
| queryKey: queryKeys.schedulers.repeatable(sort), |
| queryFn: () => api.getRepeatableSchedulers(sort), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useDelayedSchedulers(sort?: string) { |
| return useQuery({ |
| queryKey: queryKeys.schedulers.delayed(sort), |
| queryFn: () => api.getDelayedSchedulers(sort), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useSearch(query: string) { |
| return useQuery({ |
| queryKey: queryKeys.search(query), |
| queryFn: () => api.search(query), |
| enabled: query.trim().length > 0, |
| staleTime: 1000 * 30, |
| }); |
| } |
|
|
| |
| |
| |
| export function useTagValues(field: string, enabled = true) { |
| return useQuery({ |
| queryKey: queryKeys.tagValues(field), |
| queryFn: () => api.getTagValues(field), |
| enabled: enabled && field.length > 0, |
| staleTime: 1000 * 60, |
| }); |
| } |
|
|
| |
|
|
| |
| |
| |
| export function useRefresh() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: () => api.refresh(), |
| onSuccess: () => { |
| |
| queryClient.invalidateQueries(); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useRetryJob() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: ({ queueName, jobId }: { queueName: string; jobId: string }) => |
| api.retryJob(queueName, jobId), |
| onSuccess: (_, { queueName, jobId }) => { |
| queryClient.invalidateQueries({ |
| queryKey: queryKeys.job(queueName, jobId), |
| }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.jobsAll(queueName) }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useRemoveJob() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: ({ queueName, jobId }: { queueName: string; jobId: string }) => |
| api.removeJob(queueName, jobId), |
| onSuccess: (_, { queueName }) => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.jobsAll(queueName) }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function usePromoteJob() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: ({ queueName, jobId }: { queueName: string; jobId: string }) => |
| api.promoteJob(queueName, jobId), |
| onSuccess: (_, { queueName, jobId }) => { |
| queryClient.invalidateQueries({ |
| queryKey: queryKeys.job(queueName, jobId), |
| }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.jobsAll(queueName) }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.schedulers.all }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useTestJob() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: (params: { |
| queueName: string; |
| name: string; |
| data: unknown; |
| delay?: number; |
| }) => api.testJob(params), |
| onSuccess: (_, { queueName }) => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.jobsAll(queueName) }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useCleanQueue() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: ({ |
| queueName, |
| status, |
| }: { |
| queueName: string; |
| status: JobStatus; |
| }) => api.cleanQueue(queueName, status), |
| onSuccess: (_, { queueName }) => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.jobsAll(queueName) }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
|
|
| type BulkJobParams = { jobs: { queueName: string; jobId: string }[] }; |
| type BulkResult = { success: number; failed: number }; |
|
|
| |
| |
| |
| export function useBulkRetry() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation<BulkResult, Error, BulkJobParams>({ |
| mutationFn: ({ jobs }) => api.bulkRetry(jobs), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useBulkDelete() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation<BulkResult, Error, BulkJobParams>({ |
| mutationFn: ({ jobs }) => api.bulkDelete(jobs), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useBulkPromote() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation<BulkResult, Error, BulkJobParams>({ |
| mutationFn: ({ jobs }) => api.bulkPromote(jobs), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.schedulers.all }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| export function usePauseQueue() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: (queueName: string) => api.pauseQueue(queueName), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
| export function useResumeQueue() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: (queueName: string) => api.resumeQueue(queueName), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| export function useFlows() { |
| return useQuery({ |
| queryKey: queryKeys.flows, |
| queryFn: ({ signal }) => api.getFlows(undefined, signal), |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useFlow(queueName: string, jobId: string) { |
| return useQuery({ |
| queryKey: queryKeys.flow(queueName, jobId), |
| queryFn: () => api.getFlow(queueName, jobId), |
| enabled: !!queueName && !!jobId, |
| retry: false, |
| refetchInterval: 5000, |
| }); |
| } |
|
|
| |
| |
| |
| export function useCreateFlow() { |
| const queryClient = useQueryClient(); |
|
|
| return useMutation({ |
| mutationFn: (request: Parameters<typeof api.createFlow>[0]) => |
| api.createFlow(request), |
| onSuccess: () => { |
| queryClient.invalidateQueries({ queryKey: queryKeys.flows }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.runsAll }); |
| queryClient.invalidateQueries({ queryKey: queryKeys.queues }); |
| }, |
| }); |
| } |
|
|