chore: initial commit - CloudSearch v0.0.2

This commit is contained in:
2026-05-15 05:50:50 +08:00
commit d83225d736
102 changed files with 37926 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
/**
* 统一代理工具 — 支持 HTTP/HTTPS/SOCKS5/SOCKS5h 协议
*
* Node 20+ 原生 fetch() 使用 undici Dispatcher但 socks-proxy-agent 不实现此接口。
* 解决方案:使用 http.Agent 接口 + http/https.request()。
*/
let HttpsProxyAgent: any;
let SocksProxyAgent: any;
try {
HttpsProxyAgent = require('https-proxy-agent').HttpsProxyAgent;
} catch {
try { HttpsProxyAgent = require('https-proxy-agent'); } catch {}
}
try {
SocksProxyAgent = require('socks-proxy-agent').SocksProxyAgent;
} catch {
try { SocksProxyAgent = require('socks-proxy-agent'); } catch {}
}
/** Create an http.Agent for the given proxy URL (works with https.request) */
function createProxyAgent(proxyUrl: string): any | null {
if (!proxyUrl || typeof proxyUrl !== 'string') return null;
const trimmed = proxyUrl.trim();
if (!trimmed) return null;
const lower = trimmed.toLowerCase();
try {
if (lower.startsWith('socks5://') || lower.startsWith('socks5h://')) {
if (!SocksProxyAgent) {
console.warn('[Proxy] socks-proxy-agent not installed');
return null;
}
return new SocksProxyAgent(trimmed);
}
if (lower.startsWith('http://') || lower.startsWith('https://')) {
if (!HttpsProxyAgent) {
console.warn('[Proxy] No HTTP proxy agent available');
return null;
}
return new HttpsProxyAgent(trimmed);
}
// Unknown scheme — try as HTTP proxy
if (HttpsProxyAgent) return new HttpsProxyAgent(trimmed);
return null;
} catch (err: any) {
console.error(`[Proxy] Failed to create proxy agent: ${err.message}`);
return null;
}
}
/**
* Fetch with proxy support.
* Uses native fetch() when no proxy, or http/https.request() with agent when proxy is set.
*/
export async function proxiedFetch(
url: string,
init?: RequestInit,
proxyUrl?: string,
): Promise<Response> {
if (!proxyUrl) return fetch(url, init);
const agent = createProxyAgent(proxyUrl);
if (!agent) return fetch(url, init);
const parsedUrl = new URL(url);
const mod = parsedUrl.protocol === 'https:' ? require('https') : require('http');
return new Promise((resolve, reject) => {
const headers: Record<string, string> = {};
if (init?.headers) {
const h = init.headers as any;
if (h instanceof Headers) {
h.forEach((v, k) => { headers[k] = v; });
} else if (typeof h === 'object') {
Object.assign(headers, h);
}
}
const options: any = {
hostname: parsedUrl.hostname,
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
path: parsedUrl.pathname + parsedUrl.search,
method: init?.method || 'GET',
headers,
agent,
};
const req = mod.request(options, (res: any) => {
const chunks: Buffer[] = [];
res.on('data', (c: Buffer) => chunks.push(c));
res.on('end', () => {
const body = Buffer.concat(chunks);
resolve(new Response(body, {
status: res.statusCode || 502,
statusText: res.statusMessage || '',
headers: new Headers(res.headers || {}),
}));
});
});
req.on('error', reject);
if (init?.signal) {
init.signal.addEventListener('abort', () => req.destroy());
}
if (init?.body) {
req.write(
typeof init.body === 'string' ? init.body :
init.body instanceof Buffer ? init.body :
init.body instanceof ArrayBuffer ? Buffer.from(init.body) :
Buffer.from(String(init.body))
);
}
req.end();
});
}
export async function testProxyConnection(
proxyUrl: string,
testUrl?: string,
): Promise<{ ok: boolean; latency: number; info: string }> {
const target = testUrl || 'https://www.baidu.com';
const start = Date.now();
try {
const res = await proxiedFetch(target, {
signal: AbortSignal.timeout(10000),
}, proxyUrl);
const latency = Date.now() - start;
return { ok: true, latency, info: `连接成功 (${res.status})` };
} catch (err: any) {
return { ok: false, latency: Date.now() - start, info: `代理连接失败: ${err.message}` };
}
}
// Legacy compat — no longer returns dispatcher, kept for type compatibility
export function createProxyDispatcher(proxyUrl: string): { agent?: any } | null {
const agent = createProxyAgent(proxyUrl);
return agent ? { agent } : null;
}