Files
OmniRoute/src/lib/oneproxySync.ts
2026-05-01 08:36:07 -03:00

183 lines
4.4 KiB
TypeScript

import { upsertOneproxyProxy } from "./db/oneproxy";
import { getSettings } from "./db/settings";
type OneProxyApiResponse = {
total: number;
count: number;
offset: number;
limit: number;
proxies: Array<{
id: number;
url: string;
protocol: string;
ip: string;
port: number;
country_code: string;
country_name: string;
latency_ms: number;
anonymity: string;
proxy_type: string;
can_access_google: boolean;
quality_score: number;
is_working: boolean;
validation_status: string;
last_validated: string;
}>;
};
const DEFAULT_API_URL = "https://1proxy-api.aitradepulse.com";
const DEFAULT_MAX_PROXIES = 500;
const DEFAULT_MIN_QUALITY = 50;
const DEFAULT_PAGE_SIZE = 100;
let lastSyncSuccess = false;
let lastSyncError: string | null = null;
let lastSyncAt: string | null = null;
let lastSyncCount = 0;
let consecutiveFailures = 0;
const MAX_CONSECUTIVE_FAILURES = 5;
export async function syncOneproxyProxies(options?: {
apiUrl?: string;
maxProxies?: number;
minQuality?: number;
}): Promise<{
success: boolean;
added: number;
updated: number;
failed: number;
total: number;
error: string | null;
}> {
const settings = await getSettings();
const enabled = process.env.ONEPROXY_ENABLED !== "false";
if (!enabled) {
return {
success: false,
added: 0,
updated: 0,
failed: 0,
total: 0,
error: "1proxy integration disabled",
};
}
const apiUrl = options?.apiUrl || process.env.ONEPROXY_API_URL || DEFAULT_API_URL;
const maxProxies =
(options?.maxProxies ?? parseInt(process.env.ONEPROXY_MAX_PROXIES || "", 10)) ||
DEFAULT_MAX_PROXIES;
const minQuality =
(options?.minQuality ?? parseInt(process.env.ONEPROXY_MIN_QUALITY_THRESHOLD || "", 10)) ||
DEFAULT_MIN_QUALITY;
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
return {
success: false,
added: 0,
updated: 0,
failed: 0,
total: 0,
error: `Circuit breaker: ${consecutiveFailures} consecutive failures. Reset required.`,
};
}
let added = 0;
let updated = 0;
let failed = 0;
let offset = 0;
try {
while (true) {
const url = `${apiUrl}/api/v1/proxies/advanced?limit=${DEFAULT_PAGE_SIZE}&offset=${offset}&min_quality=${minQuality}&is_working=true`;
const response = await fetch(url, {
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
throw new Error(`1proxy API returned ${response.status}`);
}
const data = (await response.json()) as OneProxyApiResponse;
if (!data.proxies || data.proxies.length === 0) break;
for (const proxy of data.proxies) {
if (added + updated >= maxProxies) break;
try {
const result = await upsertOneproxyProxy({
ip: proxy.ip,
port: proxy.port,
protocol: proxy.protocol,
countryCode: proxy.country_code,
anonymity: proxy.anonymity,
qualityScore: proxy.quality_score,
latencyMs: proxy.latency_ms,
googleAccess: proxy.can_access_google,
lastValidated: proxy.last_validated,
});
if (result.action === "created") added++;
else updated++;
} catch {
failed++;
}
}
if (added + updated >= maxProxies) break;
if (data.proxies.length < DEFAULT_PAGE_SIZE) break;
offset += DEFAULT_PAGE_SIZE;
}
consecutiveFailures = 0;
lastSyncSuccess = true;
lastSyncError = null;
lastSyncAt = new Date().toISOString();
lastSyncCount = added + updated;
return {
success: true,
added,
updated,
failed,
total: added + updated,
error: null,
};
} catch (err) {
consecutiveFailures++;
lastSyncSuccess = false;
lastSyncError = err instanceof Error ? err.message : String(err);
lastSyncAt = new Date().toISOString();
return {
success: false,
added: 0,
updated: 0,
failed: 0,
total: 0,
error: lastSyncError,
};
}
}
export function getOneproxySyncStatus(): {
lastSyncSuccess: boolean;
lastSyncError: string | null;
lastSyncAt: string | null;
lastSyncCount: number;
consecutiveFailures: number;
} {
return {
lastSyncSuccess,
lastSyncError,
lastSyncAt,
lastSyncCount,
consecutiveFailures,
};
}
export function resetOneproxyCircuitBreaker(): void {
consecutiveFailures = 0;
lastSyncError = null;
}