Skip to content

Commit e6603eb

Browse files
committed
Proxy Checker, headless, and aggregators
- headless checks/screenshots with pooled workers + cleanup - aggregator parsing and error handling improvements - proxy UI/i18n/export updates
1 parent c18752c commit e6603eb

21 files changed

Lines changed: 1554 additions & 115 deletions

README.es.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ xattr -dr com.apple.quarantine "/Applications/API Key Health Checker.app"
5656
- Exportar informes a CSV/JSON (enmascarado o completo)
5757
- Idiomas de la UI: Inglés (por defecto), Ruso, Español
5858

59+
## Proxy Checker
60+
61+
- Tipos: HTTP, HTTPS, SOCKS4, SOCKS5; el esquema en la línea (`http://`, `https://`, `socks4://`, `socks5://`) fija el tipo.
62+
- Fuentes: lista manual, importación de archivo y URLs de agregadores (una por línea; se permiten comentarios con `#`).
63+
- Formatos: `ip:port` o `user:pass@ip:port`; normalización y deduplicación.
64+
- Modos: validez o acceso a URL con URL objetivo configurable.
65+
- Búsqueda HTML: uno o varios textos (OR). Sin headless lee solo los primeros N KB.
66+
- Navegador headless: carga completa con JS; capturas opcionales (carpeta, máx. archivos, autoeliminar, incluir fallidas).
67+
- Headless y capturas están disponibles solo en modo URL con búsqueda HTML activada.
68+
- Controles: el límite de velocidad es un umbral suave; el timeout es un corte duro por solicitud. Además reintentos, concurrencia, RPS máx, retraso aleatorio + jitter, máx. proxies por ejecución.
69+
- Resultados: cada proxy se comprueba por cada tipo seleccionado; logs/exportes incluyen tipo, modo y URL; resumen con listas por tipo y copiar/mover a la lista.
70+
- Resumen: separación por tipo y botón "Mover a la lista de proxies" para filtrar rápido y luego revalidar en modo más estricto (primero validez, luego URL objetivo).
71+
- Agregadores preinstalados en `src/renderer/lib/proxyAggregators` (son ejemplos; algunos pueden estar caídos o bloqueados por región).
72+
5973
## Seguridad y privacidad
6074

6175
- Las claves se envían solo al endpoint API seleccionado

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ xattr -dr com.apple.quarantine "/Applications/API Key Health Checker.app"
5454
- Export reports to CSV/JSON (masked or full)
5555
- UI languages: English (default), Russian, Spanish
5656

57+
## Proxy Checker
58+
59+
- Types: HTTP, HTTPS, SOCKS4, SOCKS5; per-line scheme (`http://`, `https://`, `socks4://`, `socks5://`) forces the type.
60+
- Inputs: manual list, file import, and aggregator URLs (one per line; `#` comments allowed).
61+
- Formats: `ip:port` or `user:pass@ip:port`; auto-normalize and dedupe.
62+
- Check modes: validity or URL access with a configurable target URL.
63+
- HTML search: one or multiple texts (OR). Non-headless reads only the first N KB.
64+
- Headless browser: full page load with JS; optional screenshots (folder, max files, auto-delete, include failed).
65+
- Headless + screenshots are available only in URL mode with HTML search enabled.
66+
- Controls: speed limit is a soft latency threshold; timeout is a hard per-request cutoff. Plus retries, concurrency, max RPS, random delay + jitter, max proxies per run.
67+
- Results: each proxy is checked per selected type; logs/exports include proxy type, check mode, target URL; summary includes per-type lists with copy/move-to-list.
68+
- Summary workflow: split by proxy type, plus "Move to Proxy list" to re-check valid proxies under stricter settings (first run validity, then target URL).
69+
- Bundled aggregator presets live in `src/renderer/lib/proxyAggregators` (examples; some URLs may be dead or region-blocked).
70+
5771
## Security and privacy
5872

5973
- Keys are sent only to the selected API endpoint

README.ru.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ xattr -dr com.apple.quarantine "/Applications/API Key Health Checker.app"
5656
- Экспорт отчетов в CSV/JSON (маскированный или полный)
5757
- Языки UI: English (по умолчанию), Русский, Español
5858

59+
## Проверка прокси
60+
61+
- Типы: HTTP, HTTPS, SOCKS4, SOCKS5; схема в строке (`http://`, `https://`, `socks4://`, `socks5://`) фиксирует тип.
62+
- Источники: ручной ввод, импорт файла и URL агрегаторов (по одному в строке; поддерживаются `#` комментарии).
63+
- Форматы: `ip:port` или `user:pass@ip:port`; автонормализация и дедупликация.
64+
- Режимы: валидность или доступ к URL с настраиваемым целевым URL.
65+
- Поиск в HTML: один или несколько текстов (ИЛИ). Без headless читаются только первые N КБ.
66+
- Headless браузер: полная загрузка страницы с JS; скриншоты по желанию (папка, лимит, автоудаление, сохранять невалидные).
67+
- Headless и скриншоты доступны только в режиме URL и при включённом поиске в HTML.
68+
- Контроль: speed limit — мягкий порог задержки, timeout — жёсткий стоп на запрос. Плюс ретраи, параллельность, лимит RPS, задержка + джиттер, лимит прокси за запуск.
69+
- Результаты: каждый прокси проверяется по каждому выбранному типу; в логах/экспорте есть тип, режим, URL; в сводке — списки по типам с копированием/переносом.
70+
- Сводка: разделение по типам и кнопка "Переместить в Proxy list", чтобы быстро отсечь валидные и затем перепроверить их в более жестком режиме (сначала доступность, потом целевой сайт).
71+
- Предустановленные агрегаторы лежат в `src/renderer/lib/proxyAggregators` (это примеры; ссылки могут быть недоступны из-за региональных блокировок или потому что источники умерли).
72+
5973
## Безопасность и приватность
6074

6175
- Ключи отправляются только в выбранный API

src/main/engine/processManager.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import type {
1010
import { ProcessRun } from "./processRun";
1111
import { RateLimiter } from "./rateLimiter";
1212
import { HistoryStore } from "../history";
13+
import { registerHeadlessProcess, unregisterHeadlessProcess } from "../services/proxy";
14+
15+
const MAX_LATENCY_SAMPLES = 2000;
1316

1417
export interface StartProcessPayload {
1518
name: string;
@@ -28,6 +31,8 @@ interface StatsTracker {
2831
network: number;
2932
unknown: number;
3033
latencies: number[];
34+
latencyCount: number;
35+
latencyTotal: number;
3136
}
3237

3338
function emptyStats(total: number): StatsTracker {
@@ -39,12 +44,23 @@ function emptyStats(total: number): StatsTracker {
3944
rateLimited: 0,
4045
network: 0,
4146
unknown: 0,
42-
latencies: []
47+
latencies: [],
48+
latencyCount: 0,
49+
latencyTotal: 0
4350
};
4451
}
4552

4653
function updateStats(stats: StatsTracker, status: CheckStatus, latencyMs: number) {
47-
stats.latencies.push(latencyMs);
54+
stats.latencyCount += 1;
55+
stats.latencyTotal += latencyMs;
56+
if (stats.latencies.length < MAX_LATENCY_SAMPLES) {
57+
stats.latencies.push(latencyMs);
58+
} else {
59+
const replaceIndex = Math.floor(Math.random() * stats.latencyCount);
60+
if (replaceIndex < MAX_LATENCY_SAMPLES) {
61+
stats.latencies[replaceIndex] = latencyMs;
62+
}
63+
}
4864
if (status === "OK") {
4965
stats.success += 1;
5066
} else if (status === "INVALID") {
@@ -61,8 +77,9 @@ function updateStats(stats: StatsTracker, status: CheckStatus, latencyMs: number
6177
}
6278

6379
function finalizeSummary(stats: StatsTracker): ReportSummary {
64-
const totalLatency = stats.latencies.reduce((sum, val) => sum + val, 0);
65-
const avgLatencyMs = stats.latencies.length ? Math.round(totalLatency / stats.latencies.length) : 0;
80+
const avgLatencyMs = stats.latencyCount
81+
? Math.round(stats.latencyTotal / stats.latencyCount)
82+
: 0;
6683
const sorted = [...stats.latencies].sort((a, b) => a - b);
6784
const mid = Math.floor(sorted.length / 2);
6885
const medianLatencyMs =
@@ -126,6 +143,11 @@ export class ProcessManager {
126143
});
127144
this.stats.set(processId, emptyStats(payload.keys.length));
128145

146+
if (payload.serviceId === "proxy" && payload.settings.proxy?.headlessBrowser) {
147+
const poolSize = payload.settings.proxy.headlessPoolSize ?? 0;
148+
registerHeadlessProcess(processId, poolSize);
149+
}
150+
129151
const run = new ProcessRun({
130152
id: processId,
131153
name: payload.name,
@@ -167,6 +189,10 @@ export class ProcessManager {
167189
summary
168190
});
169191
}
192+
const serviceId = meta?.serviceId ?? payload.serviceId;
193+
if (serviceId === "proxy") {
194+
unregisterHeadlessProcess(event.processId);
195+
}
170196
this.sendEvent("process-completed", event);
171197
this.runs.delete(event.processId);
172198
this.stats.delete(event.processId);

src/main/main.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,27 @@ function registerIpcHandlers() {
121121
}
122122
});
123123

124+
ipcMain.handle("select-directory", async (_event, payload: { title?: string }) => {
125+
const options = {
126+
title: payload.title || "Select folder",
127+
properties: ["openDirectory", "createDirectory"]
128+
} satisfies Electron.OpenDialogOptions;
129+
130+
const parentWindow = BrowserWindow.getFocusedWindow() ?? mainWindow;
131+
if (parentWindow && !parentWindow.isDestroyed()) {
132+
parentWindow.focus();
133+
}
134+
const { canceled, filePaths } = parentWindow
135+
? await dialog.showOpenDialog(parentWindow, options)
136+
: await dialog.showOpenDialog(options);
137+
138+
if (canceled || filePaths.length === 0) {
139+
return { canceled: true };
140+
}
141+
142+
return { canceled: false, path: filePaths[0] };
143+
});
144+
124145
ipcMain.handle("start-check", (_event, payload) => {
125146
return requireManager().startProcess(payload);
126147
});

src/main/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { ExportFormat, ReportPayload } from "../shared/types";
44
contextBridge.exposeInMainWorld("api", {
55
getAppVersion: () => ipcRenderer.invoke("get-app-version"),
66
openKeyFile: (payload: { encoding: string }) => ipcRenderer.invoke("open-key-file", payload),
7+
selectDirectory: (payload: { title?: string }) =>
8+
ipcRenderer.invoke("select-directory", payload),
79
fetchProxyAggregators: (payload: {
810
urls: string[];
911
timeoutMs?: number;

0 commit comments

Comments
 (0)