diff --git a/src/cliproxy/binary-manager.ts b/src/cliproxy/binary-manager.ts index be2e533a..fa037060 100644 --- a/src/cliproxy/binary-manager.ts +++ b/src/cliproxy/binary-manager.ts @@ -59,6 +59,7 @@ interface UpdateCheckResult { currentVersion: string; latestVersion: string; fromCache: boolean; + checkedAt: number; // Unix timestamp of last check } /** Default configuration */ @@ -172,19 +173,21 @@ export class BinaryManager { const currentVersion = this.getInstalledVersion(); // Try cache first - const cachedVersion = this.getCachedLatestVersion(); - if (cachedVersion) { - this.log(`Using cached version: ${cachedVersion}`); + const cache = this.getVersionCache(); + if (cache) { + this.log(`Using cached version: ${cache.latestVersion}`); return { - hasUpdate: this.isNewerVersion(cachedVersion, currentVersion), + hasUpdate: this.isNewerVersion(cache.latestVersion, currentVersion), currentVersion, - latestVersion: cachedVersion, + latestVersion: cache.latestVersion, fromCache: true, + checkedAt: cache.checkedAt, }; } // Fetch from GitHub API const latestVersion = await this.fetchLatestVersion(); + const now = Date.now(); this.cacheLatestVersion(latestVersion); return { @@ -192,6 +195,7 @@ export class BinaryManager { currentVersion, latestVersion, fromCache: false, + checkedAt: now, }; } @@ -287,9 +291,9 @@ export class BinaryManager { } /** - * Get cached latest version if still valid + * Get version cache data if still valid */ - private getCachedLatestVersion(): string | null { + private getVersionCache(): VersionCache | null { const cachePath = this.getVersionCachePath(); if (!fs.existsSync(cachePath)) { return null; @@ -301,7 +305,7 @@ export class BinaryManager { // Check if cache is still valid if (Date.now() - cache.checkedAt < VERSION_CACHE_DURATION_MS) { - return cache.latestVersion; + return cache; } // Cache expired @@ -1001,6 +1005,7 @@ export interface CliproxyUpdateCheckResult { currentVersion: string; latestVersion: string; fromCache: boolean; + checkedAt: number; // Unix timestamp of last check } /** diff --git a/ui/src/components/proxy-status-widget.tsx b/ui/src/components/proxy-status-widget.tsx index 161be85a..d3d4cd1d 100644 --- a/ui/src/components/proxy-status-widget.tsx +++ b/ui/src/components/proxy-status-widget.tsx @@ -31,6 +31,17 @@ function formatUptime(startedAt?: string): string { return `${minutes}m`; } +function formatTimeAgo(timestamp?: number): string { + if (!timestamp) return ''; + const diff = Date.now() - timestamp; + const minutes = Math.floor(diff / (1000 * 60)); + const hours = Math.floor(diff / (1000 * 60 * 60)); + + if (minutes < 1) return 'just now'; + if (minutes < 60) return `${minutes}m ago`; + return `${hours}h ago`; +} + export function ProxyStatusWidget() { const { data: status, isLoading } = useProxyStatus(); const { data: updateCheck } = useCliproxyUpdateCheck(); @@ -167,6 +178,18 @@ export function ProxyStatusWidget() { )} + + {/* Version sync indicator */} + {updateCheck?.currentVersion && ( +
+ v{updateCheck.currentVersion} + {updateCheck.checkedAt && ( + + Synced {formatTimeAgo(updateCheck.checkedAt)} + + )} +
+ )} ); } diff --git a/ui/src/lib/api-client.ts b/ui/src/lib/api-client.ts index f45837dd..73815560 100644 --- a/ui/src/lib/api-client.ts +++ b/ui/src/lib/api-client.ts @@ -196,6 +196,7 @@ export interface CliproxyUpdateCheckResult { currentVersion: string; latestVersion: string; fromCache: boolean; + checkedAt: number; // Unix timestamp of last check } // API