mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 08:18:04 +00:00
fix(cliproxy): degrade gracefully on GitHub release 403s
- fall back to stale CLIProxy version caches instead of surfacing 500s - remove duplicate dashboard update-check fetches and disable retry/focus refetch - add backend and UI regressions for the degraded-path behavior
This commit is contained in:
1 parent
4874c154cb
commit
4e15ec5387
8 files changed
+418
-86
No files matched your search
@@ -3,22 +3,15 @@
|
||||
* Fixed header with OAuth login buttons, status indicator, and refresh
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { RefreshCw, Loader2, AlertTriangle } from 'lucide-react';
|
||||
import { useCliproxyAuth } from '@/hooks/use-cliproxy';
|
||||
import { useCliproxyAuth, useCliproxyUpdateCheck } from '@/hooks/use-cliproxy';
|
||||
import { useCliproxyAuthFlow } from '@/hooks/use-cliproxy-auth-flow';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { CLIPROXY_PROVIDERS, getProviderDisplayName } from '@/lib/provider-config';
|
||||
|
||||
interface VersionInfo {
|
||||
currentVersion: string;
|
||||
isStable: boolean;
|
||||
stabilityMessage?: string;
|
||||
backendLabel?: string;
|
||||
}
|
||||
|
||||
interface LoginButtonProps {
|
||||
provider: string;
|
||||
displayName: string;
|
||||
@@ -116,25 +109,9 @@ export function CliproxyHeader({
|
||||
isRunning = true,
|
||||
}: CliproxyHeaderProps) {
|
||||
const { data: authData } = useCliproxyAuth();
|
||||
const { data: updateCheck } = useCliproxyUpdateCheck();
|
||||
const { provider: authProvider, isAuthenticating, startAuth } = useCliproxyAuthFlow();
|
||||
const lastUpdatedText = useRelativeTime(lastUpdated);
|
||||
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/cliproxy/update-check')
|
||||
.then((res) => (res.ok ? res.json() : null))
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
setVersionInfo({
|
||||
currentVersion: data.currentVersion,
|
||||
isStable: data.isStable,
|
||||
stabilityMessage: data.stabilityMessage,
|
||||
backendLabel: data.backendLabel,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {}); // Silently fail
|
||||
}, []);
|
||||
|
||||
const providers = CLIPROXY_PROVIDERS.map((id) => ({
|
||||
id,
|
||||
@@ -155,7 +132,7 @@ export function CliproxyHeader({
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">
|
||||
{versionInfo?.backendLabel ?? 'CLIProxy'}
|
||||
{updateCheck?.backendLabel ?? 'CLIProxy'}
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">CCS-level account management</p>
|
||||
</div>
|
||||
@@ -191,18 +168,18 @@ export function CliproxyHeader({
|
||||
{isRunning ? 'Running' : 'Offline'}
|
||||
</Badge>
|
||||
|
||||
{versionInfo && (
|
||||
{updateCheck && (
|
||||
<Badge
|
||||
variant={versionInfo.isStable ? 'secondary' : 'destructive'}
|
||||
variant={updateCheck.isStable ? 'secondary' : 'destructive'}
|
||||
className={cn(
|
||||
'gap-1.5',
|
||||
!versionInfo.isStable &&
|
||||
!updateCheck.isStable &&
|
||||
'bg-amber-500/20 text-amber-600 dark:text-amber-400 border-amber-500/30'
|
||||
)}
|
||||
title={versionInfo.stabilityMessage}
|
||||
title={updateCheck.stabilityMessage}
|
||||
>
|
||||
{!versionInfo.isStable && <AlertTriangle className="w-3 h-3" />}v
|
||||
{versionInfo.currentVersion}
|
||||
{!updateCheck.isStable && <AlertTriangle className="w-3 h-3" />}v
|
||||
{updateCheck.currentVersion}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
|
||||
@@ -447,7 +447,8 @@ export function useCliproxyUpdateCheck() {
|
||||
queryFn: () => api.cliproxy.updateCheck(),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes (reduced from 1 hour for faster backend switch response)
|
||||
refetchInterval: 5 * 60 * 1000, // Refresh every 5 minutes
|
||||
refetchOnWindowFocus: true, // Refetch on window focus to catch backend changes
|
||||
refetchOnWindowFocus: false, // Avoid refetch bursts for non-critical release metadata
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -492,7 +493,8 @@ export function useCliproxyVersions() {
|
||||
queryKey: ['cliproxy-versions'],
|
||||
queryFn: () => api.cliproxy.versions(),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes (reduced for faster backend switch response)
|
||||
refetchOnWindowFocus: true, // Refetch on focus to catch backend changes
|
||||
refetchOnWindowFocus: false, // Avoid repeated release lookups while browsing the dashboard
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { render, screen } from '../../../setup/test-utils';
|
||||
|
||||
const hookMocks = vi.hoisted(() => ({
|
||||
startAuth: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/hooks/use-cliproxy', () => ({
|
||||
useCliproxyAuth: () => ({
|
||||
data: {
|
||||
authStatus: [],
|
||||
},
|
||||
}),
|
||||
useCliproxyUpdateCheck: () => ({
|
||||
data: {
|
||||
backendLabel: 'CLIProxy Plus',
|
||||
currentVersion: '6.9.23-0',
|
||||
isStable: true,
|
||||
stabilityMessage: undefined,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/hooks/use-cliproxy-auth-flow', () => ({
|
||||
useCliproxyAuthFlow: () => ({
|
||||
provider: null,
|
||||
isAuthenticating: false,
|
||||
startAuth: hookMocks.startAuth,
|
||||
}),
|
||||
}));
|
||||
|
||||
import { CliproxyHeader } from '@/components/cliproxy/cliproxy-header';
|
||||
|
||||
describe('CliproxyHeader', () => {
|
||||
const fetchMock = vi.fn<typeof fetch>();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
fetchMock.mockReset();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('renders version data from the shared update query without issuing a direct fetch', () => {
|
||||
render(
|
||||
<CliproxyHeader onRefresh={vi.fn()} isRefreshing={false} lastUpdated={new Date()} isRunning />
|
||||
);
|
||||
|
||||
expect(screen.getByText('CLIProxy Plus')).toBeInTheDocument();
|
||||
expect(screen.getByText('v6.9.23-0')).toBeInTheDocument();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user