mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 04:42:10 +03:00
294 lines
8.7 KiB
TypeScript
294 lines
8.7 KiB
TypeScript
import { randomUUID } from "crypto";
|
|
import { getDbInstance } from "./core";
|
|
import { backupDbFile } from "./backup";
|
|
|
|
type JsonRecord = Record<string, unknown>;
|
|
|
|
export interface OneproxyProxyRecord {
|
|
id: string;
|
|
name: string;
|
|
type: string;
|
|
host: string;
|
|
port: number;
|
|
region: string | null;
|
|
notes: string | null;
|
|
status: string;
|
|
source: string;
|
|
qualityScore: number | null;
|
|
latencyMs: number | null;
|
|
anonymity: string | null;
|
|
googleAccess: boolean;
|
|
lastValidated: string | null;
|
|
countryCode: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface OneproxyStats {
|
|
total: number;
|
|
active: number;
|
|
avgQuality: number | null;
|
|
lastValidated: string | null;
|
|
byProtocol: Array<{ protocol: string; count: number }>;
|
|
byCountry: Array<{ countryCode: string; count: number }>;
|
|
}
|
|
|
|
interface OneproxyUpsertInput {
|
|
ip: string;
|
|
port: number;
|
|
protocol: string;
|
|
country?: string | null;
|
|
countryCode?: string | null;
|
|
anonymity?: string | null;
|
|
qualityScore?: number | null;
|
|
latencyMs?: number | null;
|
|
googleAccess?: boolean;
|
|
lastValidated?: string | null;
|
|
}
|
|
|
|
function toRecord(value: unknown): JsonRecord {
|
|
return value && typeof value === "object" ? (value as JsonRecord) : {};
|
|
}
|
|
|
|
function mapProxyRow(row: unknown): OneproxyProxyRecord {
|
|
const r = toRecord(row);
|
|
return {
|
|
id: typeof r.id === "string" ? r.id : "",
|
|
name: typeof r.name === "string" ? r.name : "",
|
|
type: typeof r.type === "string" ? r.type : "http",
|
|
host: typeof r.host === "string" ? r.host : "",
|
|
port: Number(r.port) || 0,
|
|
region: typeof r.region === "string" ? r.region : null,
|
|
notes: typeof r.notes === "string" ? r.notes : null,
|
|
status: typeof r.status === "string" ? r.status : "active",
|
|
source: typeof r.source === "string" ? r.source : "oneproxy",
|
|
qualityScore: typeof r.quality_score === "number" ? r.quality_score : null,
|
|
latencyMs: typeof r.latency_ms === "number" ? r.latency_ms : null,
|
|
anonymity: typeof r.anonymity === "string" ? r.anonymity : null,
|
|
googleAccess: r.google_access === 1 || r.google_access === true,
|
|
lastValidated: typeof r.last_validated === "string" ? r.last_validated : null,
|
|
countryCode: typeof r.country_code === "string" ? r.country_code : null,
|
|
createdAt: typeof r.created_at === "string" ? r.created_at : "",
|
|
updatedAt: typeof r.updated_at === "string" ? r.updated_at : "",
|
|
};
|
|
}
|
|
|
|
function mapStatsRow(row: unknown) {
|
|
const r = toRecord(row);
|
|
return {
|
|
total: Number(r.total) || 0,
|
|
active: Number(r.active) || 0,
|
|
avgQuality:
|
|
r.avg_quality !== null && r.avg_quality !== undefined
|
|
? Math.round(Number(r.avg_quality) * 100) / 100
|
|
: null,
|
|
lastValidated: typeof r.last_validated === "string" ? r.last_validated : null,
|
|
};
|
|
}
|
|
|
|
export async function listOneproxyProxies(options?: {
|
|
protocol?: string;
|
|
countryCode?: string;
|
|
minQuality?: number;
|
|
limit?: number;
|
|
}): Promise<OneproxyProxyRecord[]> {
|
|
const db = getDbInstance();
|
|
|
|
let sql = "SELECT * FROM proxy_registry WHERE source = 'oneproxy' AND status = 'active'";
|
|
const params: unknown[] = [];
|
|
|
|
if (options?.protocol) {
|
|
sql += " AND type = ?";
|
|
params.push(options.protocol);
|
|
}
|
|
if (options?.countryCode) {
|
|
sql += " AND country_code = ?";
|
|
params.push(options.countryCode);
|
|
}
|
|
if (options?.minQuality != null) {
|
|
sql += " AND quality_score >= ?";
|
|
params.push(options.minQuality);
|
|
}
|
|
|
|
sql += " ORDER BY quality_score DESC, last_validated DESC";
|
|
|
|
if (options?.limit) {
|
|
sql += " LIMIT ?";
|
|
params.push(options.limit);
|
|
}
|
|
|
|
const rows = db.prepare(sql).all(...params);
|
|
return rows.map(mapProxyRow);
|
|
}
|
|
|
|
export async function getOneproxyStats(): Promise<OneproxyStats> {
|
|
const db = getDbInstance();
|
|
|
|
const statsRow = db
|
|
.prepare(
|
|
`SELECT
|
|
COUNT(*) as total,
|
|
SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active,
|
|
AVG(quality_score) as avg_quality,
|
|
MAX(last_validated) as last_validated
|
|
FROM proxy_registry WHERE source = 'oneproxy'`
|
|
)
|
|
.get();
|
|
|
|
const stats = mapStatsRow(statsRow);
|
|
|
|
const byProtocol = db
|
|
.prepare(
|
|
"SELECT type as protocol, COUNT(*) as count FROM proxy_registry WHERE source = 'oneproxy' GROUP BY type ORDER BY count DESC"
|
|
)
|
|
.all() as Array<JsonRecord>;
|
|
|
|
const byCountry = db
|
|
.prepare(
|
|
"SELECT country_code as countryCode, COUNT(*) as count FROM proxy_registry WHERE source = 'oneproxy' AND country_code IS NOT NULL GROUP BY country_code ORDER BY count DESC LIMIT 20"
|
|
)
|
|
.all() as Array<JsonRecord>;
|
|
|
|
return {
|
|
...stats,
|
|
byProtocol: byProtocol.map((r) => ({
|
|
protocol: String(r.protocol || "unknown"),
|
|
count: Number(r.count) || 0,
|
|
})),
|
|
byCountry: byCountry.map((r) => ({
|
|
countryCode: String(r.countryCode || "unknown"),
|
|
count: Number(r.count) || 0,
|
|
})),
|
|
};
|
|
}
|
|
|
|
export async function upsertOneproxyProxy(
|
|
input: OneproxyUpsertInput
|
|
): Promise<{ proxy: OneproxyProxyRecord | null; action: "created" | "updated" }> {
|
|
const db = getDbInstance();
|
|
const now = new Date().toISOString();
|
|
|
|
const name = `${input.protocol?.toUpperCase() || "HTTP"} - ${input.countryCode || "Unknown"} - ${input.ip}`;
|
|
|
|
const existing = db
|
|
.prepare("SELECT id FROM proxy_registry WHERE host = ? AND port = ? AND source = 'oneproxy'")
|
|
.get(input.ip, input.port) as { id?: string } | undefined;
|
|
|
|
if (existing?.id) {
|
|
db.prepare(
|
|
`UPDATE proxy_registry
|
|
SET status = ?, quality_score = ?, latency_ms = ?, anonymity = ?,
|
|
google_access = ?, last_validated = ?, country_code = ?, updated_at = ?
|
|
WHERE id = ?`
|
|
).run(
|
|
"active",
|
|
input.qualityScore ?? null,
|
|
input.latencyMs ?? null,
|
|
input.anonymity ?? null,
|
|
input.googleAccess ? 1 : 0,
|
|
input.lastValidated ?? now,
|
|
input.countryCode ?? null,
|
|
now,
|
|
existing.id
|
|
);
|
|
backupDbFile("pre-write");
|
|
const proxy = await getOneproxyProxyById(existing.id);
|
|
return { proxy, action: "updated" };
|
|
}
|
|
|
|
const id = randomUUID();
|
|
db.prepare(
|
|
`INSERT INTO proxy_registry
|
|
(id, name, type, host, port, region, notes, status, source,
|
|
quality_score, latency_ms, anonymity, google_access, last_validated, country_code,
|
|
created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
).run(
|
|
id,
|
|
name,
|
|
input.protocol || "http",
|
|
input.ip,
|
|
input.port,
|
|
input.countryCode ?? null,
|
|
null,
|
|
"active",
|
|
"oneproxy",
|
|
input.qualityScore ?? null,
|
|
input.latencyMs ?? null,
|
|
input.anonymity ?? null,
|
|
input.googleAccess ? 1 : 0,
|
|
input.lastValidated ?? now,
|
|
input.countryCode ?? null,
|
|
now,
|
|
now
|
|
);
|
|
backupDbFile("pre-write");
|
|
const proxy = await getOneproxyProxyById(id);
|
|
return { proxy, action: "created" };
|
|
}
|
|
|
|
export async function getOneproxyProxyById(id: string): Promise<OneproxyProxyRecord | null> {
|
|
const db = getDbInstance();
|
|
const row = db
|
|
.prepare("SELECT * FROM proxy_registry WHERE id = ? AND source = 'oneproxy'")
|
|
.get(id);
|
|
if (!row) return null;
|
|
return mapProxyRow(row);
|
|
}
|
|
|
|
export async function deleteOneproxyProxy(id: string): Promise<boolean> {
|
|
const db = getDbInstance();
|
|
const result = db
|
|
.prepare("DELETE FROM proxy_registry WHERE id = ? AND source = 'oneproxy'")
|
|
.run(id);
|
|
backupDbFile("pre-write");
|
|
return result.changes > 0;
|
|
}
|
|
|
|
export async function clearAllOneproxyProxies(): Promise<number> {
|
|
const db = getDbInstance();
|
|
const result = db.prepare("DELETE FROM proxy_registry WHERE source = 'oneproxy'").run();
|
|
backupDbFile("pre-write");
|
|
return result.changes;
|
|
}
|
|
|
|
export async function getOneproxyProxyForRotation(options?: {
|
|
strategy?: "random" | "quality" | "sequential";
|
|
}): Promise<OneproxyProxyRecord | null> {
|
|
const db = getDbInstance();
|
|
const strategy = options?.strategy || "quality";
|
|
|
|
let sql = "SELECT * FROM proxy_registry WHERE source = 'oneproxy' AND status = 'active'";
|
|
|
|
switch (strategy) {
|
|
case "quality":
|
|
sql += " ORDER BY quality_score DESC, latency_ms ASC LIMIT 1";
|
|
break;
|
|
case "random":
|
|
sql += " ORDER BY RANDOM() LIMIT 1";
|
|
break;
|
|
case "sequential":
|
|
sql += " ORDER BY last_validated ASC LIMIT 1";
|
|
break;
|
|
}
|
|
|
|
const row = db.prepare(sql).get();
|
|
if (!row) return null;
|
|
return mapProxyRow(row);
|
|
}
|
|
|
|
export async function markOneproxyProxyFailed(host: string, port: number): Promise<boolean> {
|
|
const db = getDbInstance();
|
|
const result = db
|
|
.prepare(
|
|
`UPDATE proxy_registry
|
|
SET quality_score = MAX(0, COALESCE(quality_score, 50) - 10),
|
|
status = CASE WHEN COALESCE(quality_score, 50) <= 10 THEN 'inactive' ELSE status END,
|
|
updated_at = datetime('now')
|
|
WHERE host = ? AND port = ? AND source = 'oneproxy'`
|
|
)
|
|
.run(host, port);
|
|
backupDbFile("pre-write");
|
|
return result.changes > 0;
|
|
}
|