Check the proxy at startup instead of failing on first use
A proxy pointed at 127.0.0.1 from inside a container reaches the container itself, and the only sign was a connection error buried in the first search. Startup now probes the proxy over TCP and says what is wrong, naming host.docker.internal when loopback was configured. The README also covers the follow-up trap: even that address fails when the proxy listens on loopback only, so it shows how to check the bind address and what to change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ea19a74c12
commit
380f7bcf31
@@ -242,8 +242,21 @@ YTDLP_PROXY=socks5h://127.0.0.1:1080
|
||||
Два подводных камня:
|
||||
|
||||
- **`127.0.0.1` внутри контейнера — это сам контейнер.** Если Psiphon или ваш SOCKS запущен на
|
||||
хосте, укажите `socks5h://host.docker.internal:1080` и добавьте в `compose.yml`
|
||||
к `extra_hosts` строку `"host.docker.internal:host-gateway"`.
|
||||
хосте, укажите `socks5h://host.docker.internal:1080` (эта запись в `extra_hosts` уже есть).
|
||||
Бот проверяет прокси при старте и пишет в лог понятную причину, если тот недоступен.
|
||||
- **Прокси должен слушать интерфейс, видимый контейнеру.** Даже с `host.docker.internal`
|
||||
соединение упрётся в `ECONNREFUSED`, если клиент забинден только на loopback. Проверить:
|
||||
|
||||
```bash
|
||||
ss -lntp | grep 1080
|
||||
```
|
||||
|
||||
Если там `127.0.0.1:1080`, разрешите прослушивание всех интерфейсов (в конфиге
|
||||
psiphon-tunnel-core за это отвечает `ListenInterface`) и закройте порт снаружи файрволом.
|
||||
Альтернатива без правки прокси — добавить сервису `network_mode: host`, тогда `127.0.0.1:1080`
|
||||
работает как есть; но при этом перестают действовать `ports` и `extra_hosts`, то есть
|
||||
`STOAT_DOMAIN` снова начнёт резолвиться публично, а панель нужно будет привязать к
|
||||
`HOST=127.0.0.1`.
|
||||
- **ffmpeg не умеет SOCKS.** Поэтому при заданном прокси аудио всегда идёт через yt-dlp, а не
|
||||
напрямую из CDN — трафик не утекает мимо прокси. Побочный эффект: перемотка становится
|
||||
медленнее, так как позиция отыгрывается декодированием, а не HTTP-запросом с диапазоном.
|
||||
|
||||
+6
-2
@@ -3,7 +3,7 @@ import { startBot } from "./bot/index.js";
|
||||
import { config } from "./config.js";
|
||||
import { MusicManager } from "./core/manager.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { checkCookies, checkYtDlp, describeProxy } from "./sources/index.js";
|
||||
import { checkCookies, checkProxy, checkYtDlp, describeProxy } from "./sources/index.js";
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const ytdlpVersion = await checkYtDlp();
|
||||
@@ -23,7 +23,11 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
const proxy = describeProxy();
|
||||
if (proxy) logger.info({ proxy }, "routing yt-dlp through a proxy");
|
||||
if (proxy) {
|
||||
const problem = await checkProxy();
|
||||
if (problem) logger.error({ proxy }, `proxy is configured but unreachable: ${problem}`);
|
||||
else logger.info({ proxy }, "routing yt-dlp through a proxy");
|
||||
}
|
||||
|
||||
const cookies = await checkCookies();
|
||||
if (cookies === "ok") {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as direct from "./direct.js";
|
||||
import * as local from "./local.js";
|
||||
import * as ytdlp from "./ytdlp.js";
|
||||
|
||||
export { checkAvailable as checkYtDlp, checkCookies, describeProxy } from "./ytdlp.js";
|
||||
export { checkAvailable as checkYtDlp, checkCookies, checkProxy, describeProxy } from "./ytdlp.js";
|
||||
export { isEnabled as isLocalLibraryEnabled, listFiles as listLocalFiles } from "./local.js";
|
||||
|
||||
const YOUTUBE_HOSTS = ["youtube.com", "youtu.be", "music.youtube.com", "m.youtube.com"];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { connect } from "node:net";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { constants } from "node:fs";
|
||||
import { access } from "node:fs/promises";
|
||||
@@ -52,6 +53,40 @@ export function describeProxy(): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A quick TCP probe of the proxy. The usual mistake is pointing a container at
|
||||
* 127.0.0.1, which is the container itself — better to say so at startup than to
|
||||
* let every search fail with a connection error.
|
||||
*/
|
||||
export function checkProxy(timeoutMs = 4000): Promise<string | null> {
|
||||
if (!config.YTDLP_PROXY) return Promise.resolve(null);
|
||||
let target: URL;
|
||||
try {
|
||||
target = new URL(config.YTDLP_PROXY);
|
||||
} catch {
|
||||
return Promise.resolve("YTDLP_PROXY не является корректным URL");
|
||||
}
|
||||
const port = Number(target.port) || (target.protocol === "https:" ? 443 : 1080);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const socket = connect({ host: target.hostname, port });
|
||||
const done = (result: string | null) => {
|
||||
socket.destroy();
|
||||
resolve(result);
|
||||
};
|
||||
socket.setTimeout(timeoutMs);
|
||||
socket.once("connect", () => done(null));
|
||||
socket.once("timeout", () => done(`прокси ${target.hostname}:${port} не отвечает`));
|
||||
socket.once("error", (err: NodeJS.ErrnoException) => {
|
||||
const hint =
|
||||
target.hostname === "127.0.0.1" || target.hostname === "localhost"
|
||||
? " — внутри контейнера это сам контейнер; используйте host.docker.internal"
|
||||
: "";
|
||||
done(`прокси ${target.hostname}:${port} недоступен (${err.code ?? err.message})${hint}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export type CookieStatus = "ok" | "read-only" | "missing";
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user