/** * Proxy Check Operations - Checking proxy functionality */ import { OperationContext } from '@stock-bot/di'; import { getLogger } from '@stock-bot/logger'; import type { ProxyInfo } from '@stock-bot/proxy'; import { fetch } from '@stock-bot/utils'; import { PROXY_CONFIG } from '../shared/config'; /** * Check if a proxy is working */ export async function checkProxy(proxy: ProxyInfo): Promise { const ctx = { logger: getLogger('proxy-check'), resolve: (_name: string) => { throw new Error(`Service container not available for proxy operations`); }, } as any; let success = false; ctx.logger.debug(`Checking Proxy:`, { protocol: proxy.protocol, host: proxy.host, port: proxy.port, }); try { // Test the proxy using fetch with proxy support const proxyUrl = proxy.username && proxy.password ? `${proxy.protocol}://${encodeURIComponent(proxy.username)}:${encodeURIComponent(proxy.password)}@${proxy.host}:${proxy.port}` : `${proxy.protocol}://${proxy.host}:${proxy.port}`; const response = await fetch(PROXY_CONFIG.CHECK_URL, { proxy: proxyUrl, signal: AbortSignal.timeout(PROXY_CONFIG.CHECK_TIMEOUT), logger: ctx.logger, } as any); const data = await response.text(); const isWorking = response.ok; const result: ProxyInfo = { ...proxy, isWorking, lastChecked: new Date(), }; if (isWorking && !data.includes(PROXY_CONFIG.CHECK_IP)) { success = true; await updateProxyInCache(result, true, ctx); } else { await updateProxyInCache(result, false, ctx); } if (proxy.source) { updateProxyStats(proxy.source, success, ctx); } ctx.logger.debug('Proxy check completed', { host: proxy.host, port: proxy.port, isWorking, }); return result; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const result: ProxyInfo = { ...proxy, isWorking: false, error: errorMessage, lastChecked: new Date(), }; // Update cache for failed proxy (increment total, don't update TTL) await updateProxyInCache(result, false, ctx); if (proxy.source) { updateProxyStats(proxy.source, success, ctx); } ctx.logger.debug('Proxy check failed', { host: proxy.host, port: proxy.port, error: errorMessage, }); return result; } } /** * Update proxy data in cache with working/total stats and average response time */ async function updateProxyInCache( proxy: ProxyInfo, isWorking: boolean, ctx: OperationContext ): Promise { const _cacheKey = `${PROXY_CONFIG.CACHE_KEY}:${proxy.protocol}://${proxy.host}:${proxy.port}`; try { // For now, skip cache operations without service container // TODO: Pass service container to operations const existing: ProxyInfo | null = null; // For failed proxies, only update if they already exist if (!isWorking && !existing) { ctx.logger.debug('Proxy not in cache, skipping failed update', { proxy: `${proxy.host}:${proxy.port}`, }); return; } // Calculate new average response time if we have a response time let newAverageResponseTime = existing?.averageResponseTime; if (proxy.responseTime !== undefined) { const existingAvg = existing?.averageResponseTime || 0; const existingTotal = existing?.total || 0; // Calculate weighted average: (existing_avg * existing_count + new_response) / (existing_count + 1) newAverageResponseTime = existingTotal > 0 ? (existingAvg * existingTotal + proxy.responseTime) / (existingTotal + 1) : proxy.responseTime; } // Build updated proxy data const updated = { ...existing, ...proxy, // Keep latest proxy info total: (existing?.total || 0) + 1, working: isWorking ? (existing?.working || 0) + 1 : existing?.working || 0, isWorking, lastChecked: new Date(), // Add firstSeen only for new entries ...(existing ? {} : { firstSeen: new Date() }), // Update average response time if we calculated a new one ...(newAverageResponseTime !== undefined ? { averageResponseTime: newAverageResponseTime } : {}), }; // Calculate success rate updated.successRate = updated.total > 0 ? (updated.working / updated.total) * 100 : 0; // Save to cache: reset TTL for working proxies, keep existing TTL for failed ones const _cacheOptions = isWorking ? { ttl: PROXY_CONFIG.CACHE_TTL } : undefined; // Skip cache operations without service container // TODO: Pass service container to operations ctx.logger.debug(`Updated ${isWorking ? 'working' : 'failed'} proxy in cache`, { proxy: `${proxy.host}:${proxy.port}`, working: updated.working, total: updated.total, successRate: updated.successRate.toFixed(1) + '%', avgResponseTime: updated.averageResponseTime ? `${updated.averageResponseTime.toFixed(0)}ms` : 'N/A', }); } catch (error) { ctx.logger.error('Failed to update proxy in cache', { proxy: `${proxy.host}:${proxy.port}`, error: error instanceof Error ? error.message : String(error), }); } } function updateProxyStats(sourceId: string, success: boolean, ctx: OperationContext) { // Stats are now handled by the global ProxyManager ctx.logger.debug('Proxy check result', { sourceId, success }); // TODO: Integrate with global ProxyManager stats if needed }