mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 14:16:21 +00:00
Root cause: fetchLatestVersion and fetchAllVersions were hardcoded to
CLIProxyAPIPlus repo, ignoring backend selection.
Changes:
- Add getGitHubApiUrls(backend) helper to types.ts
- Make fetchLatestVersion and fetchAllVersions backend-aware
- Make version list cache backend-specific (bin/{backend}/.version-list-cache.json)
- Update /api/cliproxy/versions route to use configured backend
- Allow version management even when proxy not running (ProxyStatusWidget)
- Show settings button in all states for easier access
167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
/**
|
|
* Version Checker
|
|
* Handles checking GitHub API for latest version and comparing semver.
|
|
*/
|
|
|
|
import { fetchJson } from './downloader';
|
|
import {
|
|
readVersionCache,
|
|
writeVersionCache,
|
|
readInstalledVersion,
|
|
readVersionListCache,
|
|
writeVersionListCache,
|
|
} from './version-cache';
|
|
import { UpdateCheckResult, VersionListResult, getGitHubApiUrls } from './types';
|
|
import {
|
|
CLIPROXY_MAX_STABLE_VERSION,
|
|
CLIPROXY_FAULTY_RANGE,
|
|
DEFAULT_BACKEND,
|
|
} from '../platform-detector';
|
|
import type { CLIProxyBackend } from '../types';
|
|
|
|
/**
|
|
* Compare semver versions (true if latest > current)
|
|
* Handles CLIProxyAPIPlus versioning: strips -0 suffix before comparison
|
|
*/
|
|
export function isNewerVersion(latest: string, current: string): boolean {
|
|
// Strip -0 suffix from CLIProxyAPIPlus versions (e.g., "6.6.40-0" -> "6.6.40")
|
|
const cleanLatest = latest.replace(/-\d+$/, '');
|
|
const cleanCurrent = current.replace(/-\d+$/, '');
|
|
|
|
const latestParts = cleanLatest.split('.').map((p) => parseInt(p, 10) || 0);
|
|
const currentParts = cleanCurrent.split('.').map((p) => parseInt(p, 10) || 0);
|
|
|
|
// Pad arrays to same length
|
|
while (latestParts.length < 3) latestParts.push(0);
|
|
while (currentParts.length < 3) currentParts.push(0);
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
if (latestParts[i] > currentParts[i]) return true;
|
|
if (latestParts[i] < currentParts[i]) return false;
|
|
}
|
|
|
|
return false; // Equal versions
|
|
}
|
|
|
|
/**
|
|
* Check if version is within the faulty range (v81-85)
|
|
* @returns true if version has known critical bugs
|
|
*/
|
|
export function isVersionFaulty(version: string): boolean {
|
|
const { min, max } = CLIPROXY_FAULTY_RANGE;
|
|
const atOrAboveMin = !isNewerVersion(min, version); // version >= min
|
|
const atOrBelowMax = !isNewerVersion(version, max); // version <= max
|
|
return atOrAboveMin && atOrBelowMax;
|
|
}
|
|
|
|
/**
|
|
* Fetch latest version from GitHub API
|
|
* @param verbose Enable verbose logging
|
|
* @param backend Backend to fetch version for (uses correct GitHub repo)
|
|
*/
|
|
export async function fetchLatestVersion(
|
|
verbose = false,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): Promise<string> {
|
|
const urls = getGitHubApiUrls(backend);
|
|
const response = await fetchJson(urls.latestRelease, verbose);
|
|
|
|
// Extract version from tag_name (format: "v6.5.27" or "6.5.27")
|
|
const tagName = response.tag_name as string;
|
|
if (!tagName) {
|
|
throw new Error('No tag_name in GitHub API response');
|
|
}
|
|
|
|
return tagName.replace(/^v/, '');
|
|
}
|
|
|
|
/**
|
|
* Check for updates by comparing installed version with latest release
|
|
* Uses cache to avoid hitting GitHub API on every run
|
|
* Cache is backend-specific to handle different repos for original vs plus
|
|
*/
|
|
export async function checkForUpdates(
|
|
binPath: string,
|
|
configVersion: string,
|
|
verbose = false,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): Promise<UpdateCheckResult> {
|
|
const currentVersion = readInstalledVersion(binPath, configVersion);
|
|
|
|
// Try cache first (backend-specific)
|
|
const cache = readVersionCache(backend);
|
|
if (cache) {
|
|
if (verbose) {
|
|
console.error(`[cliproxy] Using cached version: ${cache.latestVersion}`);
|
|
}
|
|
return {
|
|
hasUpdate: isNewerVersion(cache.latestVersion, currentVersion),
|
|
currentVersion,
|
|
latestVersion: cache.latestVersion,
|
|
fromCache: true,
|
|
checkedAt: cache.checkedAt,
|
|
};
|
|
}
|
|
|
|
// Fetch from GitHub API (backend-specific repo)
|
|
const latestVersion = await fetchLatestVersion(verbose, backend);
|
|
const now = Date.now();
|
|
writeVersionCache(latestVersion, backend);
|
|
|
|
return {
|
|
hasUpdate: isNewerVersion(latestVersion, currentVersion),
|
|
currentVersion,
|
|
latestVersion,
|
|
fromCache: false,
|
|
checkedAt: now,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch all available versions from GitHub releases
|
|
* Caches result for 1 hour to avoid rate limiting
|
|
* @param verbose Enable verbose logging
|
|
* @param backend Backend to fetch versions for (uses correct GitHub repo)
|
|
*/
|
|
export async function fetchAllVersions(
|
|
verbose = false,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): Promise<VersionListResult> {
|
|
// Try cache first (backend-specific)
|
|
const cache = readVersionListCache(backend);
|
|
if (cache) {
|
|
if (verbose) {
|
|
console.error(`[cliproxy] Using cached version list (${cache.versions.length} versions)`);
|
|
}
|
|
return { ...cache, fromCache: true };
|
|
}
|
|
|
|
// Fetch from GitHub API (backend-specific repo)
|
|
const urls = getGitHubApiUrls(backend);
|
|
const response = await fetchJson(urls.allReleases, verbose);
|
|
|
|
// Extract and normalize versions
|
|
const releases = response as unknown as Array<{ tag_name: string }>;
|
|
const versions = releases
|
|
.map((r) => r.tag_name.replace(/^v/, ''))
|
|
.filter((v) => /^\d+\.\d+\.\d+(-\d+)?$/.test(v)); // Valid semver only
|
|
|
|
const latest = versions[0] || '';
|
|
|
|
// Find latest stable (not newer than max stable AND not in faulty range)
|
|
const latestStable =
|
|
versions.find((v) => !isNewerVersion(v, CLIPROXY_MAX_STABLE_VERSION) && !isVersionFaulty(v)) ||
|
|
CLIPROXY_MAX_STABLE_VERSION;
|
|
|
|
const result: VersionListResult = {
|
|
versions,
|
|
latestStable,
|
|
latest,
|
|
fromCache: false,
|
|
checkedAt: Date.now(),
|
|
};
|
|
|
|
writeVersionListCache(result, backend);
|
|
return result;
|
|
}
|