mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 21:02:12 +03:00
Merged into release/v3.8.45. Reds pré-existentes classificados: dast-smoke (infra), check:file-size (drift de baseline de god-files congelados), e 3 testes de contagem agentSkills stale (43→44 já corrigidos no tip pelo #6186 — somem no squash sobre o tip). Núcleo do fix #6246 (proxy IP-leak + over-deactivation).
78 lines
3.2 KiB
TypeScript
78 lines
3.2 KiB
TypeScript
/**
|
|
* Pure, network-free decision for the proxy health scheduler (#6246).
|
|
*
|
|
* Separated from the sweep so the status/removal policy can be unit-tested
|
|
* exhaustively without any I/O. The sweep classifies each probe into a tri-state
|
|
* {@link ProxyProbeOutcome} and applies the returned {@link ProxyHealthDecision}.
|
|
*
|
|
* Policy (agreed for #6246):
|
|
* A — downgrade only after `removeAfter` CONSECUTIVE conclusive failures.
|
|
* B — an `inconclusive` probe (our own timeout/abort, or the probe TARGET
|
|
* erroring) never penalizes: it neither counts nor changes status.
|
|
* C — by DEFAULT (auto-remove off) the health check NEVER mutates a proxy's
|
|
* status. It only counts failures for logging. A proxy is downgraded to
|
|
* `inactive` (and removed) only when the operator opts in via
|
|
* PROXY_AUTO_REMOVE=true. This mirrors how accounts are only auto-disabled
|
|
* when the operator allows it — the operator owns their (often paid) proxies.
|
|
*/
|
|
|
|
export type ProxyProbeOutcome = "ok" | "fail" | "inconclusive";
|
|
|
|
export interface ProxyHealthDecisionInput {
|
|
/** Tri-state result of the reachability probe for this proxy. */
|
|
outcome: ProxyProbeOutcome;
|
|
/** Consecutive failure count recorded BEFORE this probe. */
|
|
priorFailures: number;
|
|
/** PROXY_AUTO_REMOVE === "true" — operator opted into status management. */
|
|
autoRemove: boolean;
|
|
/** Consecutive conclusive failures required before a downgrade/removal. */
|
|
removeAfter: number;
|
|
}
|
|
|
|
export interface ProxyHealthDecision {
|
|
/** New consecutive-failure count to persist for this proxy. */
|
|
failures: number;
|
|
/** Whether to drop this proxy from the consecutive-failure map. */
|
|
clearFailures: boolean;
|
|
/** Status to write, or `null` to leave the operator-controlled status untouched. */
|
|
setStatus: "active" | "inactive" | null;
|
|
/** Whether to auto-remove the proxy (only ever true when autoRemove is on). */
|
|
remove: boolean;
|
|
}
|
|
|
|
export function decideProxyHealthAction(input: ProxyHealthDecisionInput): ProxyHealthDecision {
|
|
const { outcome, priorFailures, autoRemove, removeAfter } = input;
|
|
const threshold = Number.isFinite(removeAfter) && removeAfter > 0 ? removeAfter : 3;
|
|
|
|
// B: inconclusive probes are neutral — do not touch count or status.
|
|
if (outcome === "inconclusive") {
|
|
return { failures: priorFailures, clearFailures: false, setStatus: null, remove: false };
|
|
}
|
|
|
|
// Success: reset the streak. Only (re)assert "active" when the operator has
|
|
// opted into status management; otherwise never touch the user's status (C).
|
|
if (outcome === "ok") {
|
|
return {
|
|
failures: 0,
|
|
clearFailures: true,
|
|
setStatus: autoRemove ? "active" : null,
|
|
remove: false,
|
|
};
|
|
}
|
|
|
|
// Conclusive failure.
|
|
const failures = priorFailures + 1;
|
|
|
|
// C: default mode only counts/logs — never downgrades.
|
|
if (!autoRemove) {
|
|
return { failures, clearFailures: false, setStatus: null, remove: false };
|
|
}
|
|
|
|
// A: downgrade + remove only once the consecutive threshold is reached.
|
|
if (failures >= threshold) {
|
|
return { failures, clearFailures: false, setStatus: "inactive", remove: true };
|
|
}
|
|
|
|
return { failures, clearFailures: false, setStatus: null, remove: false };
|
|
}
|