feat(proxy): proxy_logs telemetry — per-attempt provider/ban/latency tracking
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

One row per proxied upstream attempt (pcat call/capture/validate, emex http)
written fire-and-forget by the new ProxyTelemetryService (buffered, capped,
errors swallowed — telemetry can never hurt the request path).

- banned = upstream 403/429 (IP-block signal), distinct from auth/data errors
- sticky legs (pcat capture, emex floxy) carry a session_key; pcat capture
  also resolves the actual residential exit IP via a parallel ipify probe
  through the same sticky session → concrete banned-IP tracking
- rotating legs log provider + outcome (ban *rate* instead of per-IP)
- 30-day retention piggybacked on the query-cleanup job

Feeds the Süper Panel /analytics/proxy page (provider grading + banned IPs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 17:27:48 +03:00
parent 9f47d15312
commit 45f7e3f5a4
12 changed files with 373 additions and 9 deletions

View File

@@ -617,3 +617,41 @@ export const emexCategoryTranslations = pgTable(
},
(table) => [uniqueIndex("emex_translations_original_name_idx").on(table.originalName)],
);
// ─── Proxy Logs ──────────────────────────────────────
// One row per proxied upstream attempt (pcat call/capture/validate, emex http).
// Written fire-and-forget by ProxyTelemetryService so the Süper Panel can grade
// proxy providers (success/ban/latency per service) and track banned exit IPs.
// `banned` = HTTP 403/429 from the upstream (IP-level block signal); `session_key`
// identifies a sticky Floxy session or a DataImpulse port so bans are attributable
// even when the exact exit IP is unknown (rotating call legs).
export const proxyLogs = pgTable(
"proxy_logs",
{
id: uuid("id").primaryKey().defaultRandom(),
// Which integration leg made the request: pcat_call | pcat_capture |
// pcat_validate | emex_http
service: varchar("service", { length: 30 }).notNull(),
// floxy | dataimpulse | none (direct)
provider: varchar("provider", { length: 20 }).notNull(),
// Sticky Floxy session id ("s-ab12cd34") or DataImpulse port ("di:10042");
// null on per-request rotating legs (exit IP changes every call).
sessionKey: varchar("session_key", { length: 60 }),
// Resolved residential exit IP (sticky sessions only — one cheap ipify probe
// per session). Null when rotation makes the IP unknowable.
exitIp: varchar("exit_ip", { length: 45 }),
targetHost: varchar("target_host", { length: 120 }),
statusCode: integer("status_code"),
// Transport failure class when no HTTP answer arrived:
// timeout | connect | reset | dns | aborted | transport
errorKind: varchar("error_kind", { length: 30 }),
success: boolean("success").default(false).notNull(),
banned: boolean("banned").default(false).notNull(),
durationMs: integer("duration_ms"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(table) => [
index("proxy_logs_created_at_idx").on(table.createdAt),
index("proxy_logs_banned_created_at_idx").on(table.banned, table.createdAt),
],
);