The /api/me call races with TokenSync — fires before the token provider is set. Removed the hard window.location redirect on 401 from the API client. React Query retries with backoff instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
204 lines
6.3 KiB
TypeScript
204 lines
6.3 KiB
TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { api } from './client';
|
|
import type {
|
|
TenantResponse, EnvironmentResponse, AppResponse,
|
|
DeploymentResponse, LicenseResponse, AgentStatusResponse,
|
|
ObservabilityStatusResponse, LogEntry, MeResponse,
|
|
} from '../types/api';
|
|
|
|
// Tenant
|
|
export function useTenant(tenantId: string) {
|
|
return useQuery({
|
|
queryKey: ['tenant', tenantId],
|
|
queryFn: () => api.get<TenantResponse>(`/tenants/${tenantId}`),
|
|
enabled: !!tenantId,
|
|
});
|
|
}
|
|
|
|
// License
|
|
export function useLicense(tenantId: string) {
|
|
return useQuery({
|
|
queryKey: ['license', tenantId],
|
|
queryFn: () => api.get<LicenseResponse>(`/tenants/${tenantId}/license`),
|
|
enabled: !!tenantId,
|
|
});
|
|
}
|
|
|
|
// Environments
|
|
export function useEnvironments(tenantId: string) {
|
|
return useQuery({
|
|
queryKey: ['environments', tenantId],
|
|
queryFn: () => api.get<EnvironmentResponse[]>(`/tenants/${tenantId}/environments`),
|
|
enabled: !!tenantId,
|
|
});
|
|
}
|
|
|
|
export function useCreateEnvironment(tenantId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (data: { slug: string; displayName: string }) =>
|
|
api.post<EnvironmentResponse>(`/tenants/${tenantId}/environments`, data),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['environments', tenantId] }),
|
|
});
|
|
}
|
|
|
|
export function useUpdateEnvironment(tenantId: string, envId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (data: { displayName: string }) =>
|
|
api.patch<EnvironmentResponse>(`/tenants/${tenantId}/environments/${envId}`, data),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['environments', tenantId] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteEnvironment(tenantId: string, envId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: () => api.delete(`/tenants/${tenantId}/environments/${envId}`),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['environments', tenantId] }),
|
|
});
|
|
}
|
|
|
|
// Apps
|
|
export function useApps(environmentId: string) {
|
|
return useQuery({
|
|
queryKey: ['apps', environmentId],
|
|
queryFn: () => api.get<AppResponse[]>(`/environments/${environmentId}/apps`),
|
|
enabled: !!environmentId,
|
|
});
|
|
}
|
|
|
|
export function useApp(environmentId: string, appId: string) {
|
|
return useQuery({
|
|
queryKey: ['app', appId],
|
|
queryFn: () => api.get<AppResponse>(`/environments/${environmentId}/apps/${appId}`),
|
|
enabled: !!appId,
|
|
});
|
|
}
|
|
|
|
export function useCreateApp(environmentId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (formData: FormData) =>
|
|
api.post<AppResponse>(`/environments/${environmentId}/apps`, formData),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['apps', environmentId] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteApp(environmentId: string, appId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: () => api.delete(`/environments/${environmentId}/apps/${appId}`),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['apps', environmentId] }),
|
|
});
|
|
}
|
|
|
|
export function useUpdateRouting(environmentId: string, appId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (data: { exposedPort: number | null }) =>
|
|
api.patch<AppResponse>(`/environments/${environmentId}/apps/${appId}/routing`, data),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['app', appId] }),
|
|
});
|
|
}
|
|
|
|
// Deployments
|
|
export function useDeploy(appId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: () => api.post<DeploymentResponse>(`/apps/${appId}/deploy`),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['deployments', appId] }),
|
|
});
|
|
}
|
|
|
|
export function useDeployments(appId: string) {
|
|
return useQuery({
|
|
queryKey: ['deployments', appId],
|
|
queryFn: () => api.get<DeploymentResponse[]>(`/apps/${appId}/deployments`),
|
|
enabled: !!appId,
|
|
});
|
|
}
|
|
|
|
export function useDeployment(appId: string, deploymentId: string) {
|
|
return useQuery({
|
|
queryKey: ['deployment', deploymentId],
|
|
queryFn: () => api.get<DeploymentResponse>(`/apps/${appId}/deployments/${deploymentId}`),
|
|
enabled: !!deploymentId,
|
|
refetchInterval: (query) => {
|
|
const status = query.state.data?.observedStatus;
|
|
return status === 'BUILDING' || status === 'STARTING' ? 3000 : false;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useStop(appId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: () => api.post<DeploymentResponse>(`/apps/${appId}/stop`),
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ['deployments', appId] });
|
|
qc.invalidateQueries({ queryKey: ['app'] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRestart(appId: string) {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: () => api.post<DeploymentResponse>(`/apps/${appId}/restart`),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ['deployments', appId] }),
|
|
});
|
|
}
|
|
|
|
// Observability
|
|
export function useAgentStatus(appId: string) {
|
|
return useQuery({
|
|
queryKey: ['agent-status', appId],
|
|
queryFn: () => api.get<AgentStatusResponse>(`/apps/${appId}/agent-status`),
|
|
enabled: !!appId,
|
|
refetchInterval: 15_000,
|
|
});
|
|
}
|
|
|
|
export function useObservabilityStatus(appId: string) {
|
|
return useQuery({
|
|
queryKey: ['observability-status', appId],
|
|
queryFn: () => api.get<ObservabilityStatusResponse>(`/apps/${appId}/observability-status`),
|
|
enabled: !!appId,
|
|
refetchInterval: 30_000,
|
|
});
|
|
}
|
|
|
|
export function useLogs(appId: string, params?: { since?: string; limit?: number; stream?: string }) {
|
|
return useQuery({
|
|
queryKey: ['logs', appId, params],
|
|
queryFn: () => {
|
|
const qs = new URLSearchParams();
|
|
if (params?.since) qs.set('since', params.since);
|
|
if (params?.limit) qs.set('limit', String(params.limit));
|
|
if (params?.stream) qs.set('stream', params.stream);
|
|
const query = qs.toString();
|
|
return api.get<LogEntry[]>(`/apps/${appId}/logs${query ? `?${query}` : ''}`);
|
|
},
|
|
enabled: !!appId,
|
|
});
|
|
}
|
|
|
|
// Platform
|
|
export function useMe() {
|
|
return useQuery({
|
|
queryKey: ['me'],
|
|
queryFn: () => api.get<MeResponse>('/me'),
|
|
staleTime: 60_000,
|
|
retry: 3,
|
|
retryDelay: 1000,
|
|
});
|
|
}
|
|
|
|
export function useAllTenants() {
|
|
return useQuery({
|
|
queryKey: ['tenants', 'all'],
|
|
queryFn: () => api.get<TenantResponse[]>('/tenants'),
|
|
});
|
|
}
|