mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
fix: 移除未使用的类型导出,清理代码
This commit is contained in:
@@ -292,6 +292,14 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up health check service state for deleted provider
|
||||||
|
try {
|
||||||
|
const { getHealthCheckService } = await import('../services/health-check-service.js');
|
||||||
|
getHealthCheckService().cleanupProvider(providerId);
|
||||||
|
} catch (cleanupErr) {
|
||||||
|
console.warn('[Provider Delete] Failed to cleanup health check state:', cleanupErr);
|
||||||
|
}
|
||||||
|
|
||||||
broadcastToClients({
|
broadcastToClients({
|
||||||
type: 'LITELLM_PROVIDER_DELETED',
|
type: 'LITELLM_PROVIDER_DELETED',
|
||||||
payload: { providerId, timestamp: new Date().toISOString() }
|
payload: { providerId, timestamp: new Date().toISOString() }
|
||||||
|
|||||||
@@ -640,6 +640,12 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
|
|||||||
try {
|
try {
|
||||||
const healthCheckService = getHealthCheckService();
|
const healthCheckService = getHealthCheckService();
|
||||||
healthCheckService.startAllHealthChecks(initialPath);
|
healthCheckService.startAllHealthChecks(initialPath);
|
||||||
|
|
||||||
|
// Graceful shutdown: stop health checks when server closes
|
||||||
|
server.on('close', () => {
|
||||||
|
console.log('[Server] Shutting down health check service...');
|
||||||
|
healthCheckService.stopAllHealthChecks();
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[Server] Failed to start health check service:', err);
|
console.warn('[Server] Failed to start health check service:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,51 @@
|
|||||||
|
|
||||||
import type { ProviderType } from '../../types/litellm-api-config.js';
|
import type { ProviderType } from '../../types/litellm-api-config.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed URL patterns for API base URLs (SSRF protection)
|
||||||
|
* Only allows HTTPS URLs to known API providers or custom domains
|
||||||
|
*/
|
||||||
|
const BLOCKED_HOSTS = [
|
||||||
|
/^localhost$/i,
|
||||||
|
/^127\.\d+\.\d+\.\d+$/,
|
||||||
|
/^10\.\d+\.\d+\.\d+$/,
|
||||||
|
/^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/,
|
||||||
|
/^192\.168\.\d+\.\d+$/,
|
||||||
|
/^0\.0\.0\.0$/,
|
||||||
|
/^::1$/,
|
||||||
|
/^\[::1\]$/,
|
||||||
|
/^metadata\.google\.internal$/i,
|
||||||
|
/^169\.254\.\d+\.\d+$/, // AWS metadata service
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate API base URL to prevent SSRF attacks
|
||||||
|
* @param url - The URL to validate
|
||||||
|
* @returns Object with valid flag and optional error message
|
||||||
|
*/
|
||||||
|
export function validateApiBaseUrl(url: string): { valid: boolean; error?: string } {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
|
||||||
|
// Must be HTTPS (except for localhost in development)
|
||||||
|
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
||||||
|
return { valid: false, error: 'URL must use HTTPS protocol' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check against blocked hosts
|
||||||
|
const hostname = parsed.hostname.toLowerCase();
|
||||||
|
for (const pattern of BLOCKED_HOSTS) {
|
||||||
|
if (pattern.test(hostname)) {
|
||||||
|
return { valid: false, error: 'URL points to internal/private network address' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true };
|
||||||
|
} catch {
|
||||||
|
return { valid: false, error: 'Invalid URL format' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result of an API key connection test
|
* Result of an API key connection test
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +89,12 @@ export async function testApiKeyConnection(
|
|||||||
apiKey: string,
|
apiKey: string,
|
||||||
timeout: number = 10000
|
timeout: number = 10000
|
||||||
): Promise<TestResult> {
|
): Promise<TestResult> {
|
||||||
|
// Validate URL to prevent SSRF
|
||||||
|
const urlValidation = validateApiBaseUrl(apiBase);
|
||||||
|
if (!urlValidation.valid) {
|
||||||
|
return { valid: false, error: urlValidation.error };
|
||||||
|
}
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|||||||
@@ -330,6 +330,32 @@ export class HealthCheckService {
|
|||||||
getMonitoredProviders(): string[] {
|
getMonitoredProviders(): string[] {
|
||||||
return Array.from(this.timers.keys());
|
return Array.from(this.timers.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up all state for a deleted provider
|
||||||
|
* Call this when a provider is deleted to prevent memory leaks
|
||||||
|
* @param providerId - The provider ID to clean up
|
||||||
|
*/
|
||||||
|
cleanupProvider(providerId: string): void {
|
||||||
|
// Stop health check timer
|
||||||
|
this.stopHealthCheck(providerId);
|
||||||
|
|
||||||
|
// Remove all key states for this provider
|
||||||
|
const keysToRemove: string[] = [];
|
||||||
|
for (const key of this.keyStates.keys()) {
|
||||||
|
if (key.startsWith(`${providerId}:`)) {
|
||||||
|
keysToRemove.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of keysToRemove) {
|
||||||
|
this.keyStates.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysToRemove.length > 0) {
|
||||||
|
console.log(`[HealthCheck] Cleaned up ${keysToRemove.length} key state(s) for deleted provider ${providerId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user