mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 22:16:41 +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
232 lines
5.8 KiB
TypeScript
232 lines
5.8 KiB
TypeScript
/**
|
|
* Version Cache Manager
|
|
* Handles reading/writing version cache to avoid excessive GitHub API calls.
|
|
*/
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { getBinDir } from '../config-generator';
|
|
import {
|
|
VersionCache,
|
|
VERSION_CACHE_DURATION_MS,
|
|
VERSION_PIN_FILE,
|
|
VersionListCache,
|
|
} from './types';
|
|
import { DEFAULT_BACKEND } from '../platform-detector';
|
|
import type { CLIProxyBackend } from '../types';
|
|
|
|
/**
|
|
* Get path to version cache file (backend-specific)
|
|
*/
|
|
export function getVersionCachePath(backend: CLIProxyBackend = DEFAULT_BACKEND): string {
|
|
return path.join(getBinDir(), backend, '.version-cache.json');
|
|
}
|
|
|
|
/**
|
|
* Get path to version pin file (backend-specific)
|
|
*/
|
|
export function getVersionPinPath(backend: CLIProxyBackend = DEFAULT_BACKEND): string {
|
|
return path.join(getBinDir(), backend, VERSION_PIN_FILE);
|
|
}
|
|
|
|
/**
|
|
* Read version cache if still valid (backend-specific)
|
|
*/
|
|
export function readVersionCache(backend: CLIProxyBackend = DEFAULT_BACKEND): VersionCache | null {
|
|
const cachePath = getVersionCachePath(backend);
|
|
if (!fs.existsSync(cachePath)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const content = fs.readFileSync(cachePath, 'utf8');
|
|
const cache: VersionCache = JSON.parse(content);
|
|
|
|
// Check if cache is still valid
|
|
if (Date.now() - cache.checkedAt < VERSION_CACHE_DURATION_MS) {
|
|
return cache;
|
|
}
|
|
|
|
// Cache expired
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write version to cache (backend-specific)
|
|
*/
|
|
export function writeVersionCache(
|
|
version: string,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): void {
|
|
const cachePath = getVersionCachePath(backend);
|
|
const cache: VersionCache = {
|
|
latestVersion: version,
|
|
checkedAt: Date.now(),
|
|
};
|
|
|
|
try {
|
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
fs.writeFileSync(cachePath, JSON.stringify(cache), 'utf8');
|
|
} catch {
|
|
// Silent fail - caching is optional
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read installed version from .version file
|
|
*/
|
|
export function readInstalledVersion(binPath: string, fallbackVersion: string): string {
|
|
const versionFile = path.join(binPath, '.version');
|
|
if (fs.existsSync(versionFile)) {
|
|
try {
|
|
return fs.readFileSync(versionFile, 'utf8').trim();
|
|
} catch {
|
|
return fallbackVersion;
|
|
}
|
|
}
|
|
return fallbackVersion;
|
|
}
|
|
|
|
/**
|
|
* Write installed version to .version file
|
|
*/
|
|
export function writeInstalledVersion(binPath: string, version: string): void {
|
|
const versionFile = path.join(binPath, '.version');
|
|
try {
|
|
fs.writeFileSync(versionFile, version, 'utf8');
|
|
} catch {
|
|
// Silent fail - not critical
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get pinned version if one exists (backend-specific)
|
|
*/
|
|
export function getPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): string | null {
|
|
const pinPath = getVersionPinPath(backend);
|
|
if (!fs.existsSync(pinPath)) {
|
|
return null;
|
|
}
|
|
try {
|
|
return fs.readFileSync(pinPath, 'utf8').trim();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save pinned version to persist user's explicit choice (backend-specific)
|
|
*/
|
|
export function savePinnedVersion(
|
|
version: string,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): void {
|
|
const pinPath = getVersionPinPath(backend);
|
|
try {
|
|
fs.mkdirSync(path.dirname(pinPath), { recursive: true });
|
|
fs.writeFileSync(pinPath, version, 'utf8');
|
|
} catch {
|
|
// Silent fail - not critical
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear pinned version (unpin) - backend-specific
|
|
*/
|
|
export function clearPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): void {
|
|
const pinPath = getVersionPinPath(backend);
|
|
if (fs.existsSync(pinPath)) {
|
|
try {
|
|
fs.unlinkSync(pinPath);
|
|
} catch {
|
|
// Silent fail
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a version is currently pinned (backend-specific)
|
|
*/
|
|
export function isVersionPinned(backend: CLIProxyBackend = DEFAULT_BACKEND): boolean {
|
|
return getPinnedVersion(backend) !== null;
|
|
}
|
|
|
|
/**
|
|
* Migrate old shared version pin to backend-specific location.
|
|
* Called once on first run after update.
|
|
*/
|
|
export function migrateVersionPin(backend: CLIProxyBackend): void {
|
|
const oldPinPath = path.join(getBinDir(), VERSION_PIN_FILE);
|
|
if (!fs.existsSync(oldPinPath)) return;
|
|
|
|
try {
|
|
const oldVersion = fs.readFileSync(oldPinPath, 'utf8').trim();
|
|
if (!oldVersion) return;
|
|
|
|
// Save to new backend-specific location
|
|
savePinnedVersion(oldVersion, backend);
|
|
|
|
// Delete old shared file
|
|
fs.unlinkSync(oldPinPath);
|
|
} catch {
|
|
// Silent fail - not critical
|
|
}
|
|
}
|
|
|
|
// ==================== Version List Cache ====================
|
|
|
|
const VERSION_LIST_CACHE_FILE = '.version-list-cache.json';
|
|
|
|
/**
|
|
* Get path to version list cache file (backend-specific)
|
|
*/
|
|
export function getVersionListCachePath(backend: CLIProxyBackend = DEFAULT_BACKEND): string {
|
|
return path.join(getBinDir(), backend, VERSION_LIST_CACHE_FILE);
|
|
}
|
|
|
|
/**
|
|
* Read version list cache if still valid (backend-specific)
|
|
*/
|
|
export function readVersionListCache(
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): VersionListCache | null {
|
|
const cachePath = getVersionListCachePath(backend);
|
|
if (!fs.existsSync(cachePath)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const content = fs.readFileSync(cachePath, 'utf8');
|
|
const cache: VersionListCache = JSON.parse(content);
|
|
|
|
// Check if cache is still valid (1 hour)
|
|
if (Date.now() - cache.checkedAt < VERSION_CACHE_DURATION_MS) {
|
|
return cache;
|
|
}
|
|
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write version list to cache (backend-specific)
|
|
*/
|
|
export function writeVersionListCache(
|
|
cache: VersionListCache,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): void {
|
|
const cachePath = getVersionListCachePath(backend);
|
|
|
|
try {
|
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
fs.writeFileSync(cachePath, JSON.stringify(cache), 'utf8');
|
|
} catch {
|
|
// Silent fail - caching is optional
|
|
}
|
|
}
|