mirror of
https://github.com/andrey271192/amnezia_web-PRO.git
synced 2026-09-20 14:42:00 +00:00
fix: runtime auto-discovery of AmneziaWG container
Tester hit 'No such container: amnezia-awg' after a stock install on a VPS where Amnezia created the WG container as amnezia-awg2. Install-time detection was not enough. The panel now resolves the real container at runtime: if the configured AWG_CONTAINER lacks the conf file (or does not exist), it scans running containers for one holding awg0.conf, preferring amnezia-awg* names. All docker exec paths (clients list/create/ enable/disable/delete, WARP, host-setup) and the API container field use the resolved name. Clear error lists running containers if none match. Result: install -> login -> change password -> create client works with no manual AWG_CONTAINER/AWG_PROFILES on single-instance setups.
This commit is contained in:
77
server.js
77
server.js
@@ -450,7 +450,7 @@ async function dockerRestartContainer(container) {
|
|||||||
|
|
||||||
async function warpFileExists(rt, remotePath) {
|
async function warpFileExists(rt, remotePath) {
|
||||||
try {
|
try {
|
||||||
await execDocker(["exec", rt.profile.container, "test", "-f", remotePath]);
|
await execDocker(["exec", await rt.resolveContainer(), "test", "-f", remotePath]);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
@@ -459,7 +459,7 @@ async function warpFileExists(rt, remotePath) {
|
|||||||
|
|
||||||
async function warpInterfaceUp(rt) {
|
async function warpInterfaceUp(rt) {
|
||||||
try {
|
try {
|
||||||
await execDocker(["exec", rt.profile.container, "ip", "addr", "show", "warp"]);
|
await execDocker(["exec", await rt.resolveContainer(), "ip", "addr", "show", "warp"]);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
@@ -497,7 +497,7 @@ ip route flush table 100 2>/dev/null || true
|
|||||||
exit 0
|
exit 0
|
||||||
`;
|
`;
|
||||||
try {
|
try {
|
||||||
await dockerExecStdin(rt.profile.container, sh);
|
await dockerExecStdin(await rt.resolveContainer(), sh);
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
@@ -583,7 +583,7 @@ async function warpPatchStartSh(rt, ips) {
|
|||||||
"fi",
|
"fi",
|
||||||
"",
|
"",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
await dockerExecStdin(rt.profile.container, remote);
|
await dockerExecStdin(await rt.resolveContainer(), remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function warpPersistAndRestart(rt, selectedIps) {
|
async function warpPersistAndRestart(rt, selectedIps) {
|
||||||
@@ -680,25 +680,87 @@ function peerUsesWarp(peer, selectedSet) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function listRunningContainerNames() {
|
||||||
|
try {
|
||||||
|
const { stdout } = await execDocker(["ps", "--format", "{{.Names}}"]);
|
||||||
|
return stdout.split("\n").map((x) => x.trim()).filter(Boolean);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function containerHasFile(name, filePath) {
|
||||||
|
try {
|
||||||
|
await execDocker(["exec", name, "test", "-f", filePath]);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a running container that actually holds the AmneziaWG config file.
|
||||||
|
// Lets the panel work even if AWG_CONTAINER points at the wrong name
|
||||||
|
// (e.g. configured "amnezia-awg" but Amnezia created "amnezia-awg2").
|
||||||
|
async function discoverAwgContainer(confPath, preferredName) {
|
||||||
|
const names = await listRunningContainerNames();
|
||||||
|
const score = (n) => {
|
||||||
|
if (preferredName && n === preferredName) return 0;
|
||||||
|
if (/^amnezia-?awg/i.test(n)) return 1;
|
||||||
|
if (/^amnezia/i.test(n)) return 2;
|
||||||
|
if (/awg|wireguard|wg/i.test(n)) return 3;
|
||||||
|
return 4;
|
||||||
|
};
|
||||||
|
const ordered = [...names].sort((a, b) => score(a) - score(b));
|
||||||
|
for (const n of ordered) {
|
||||||
|
if (await containerHasFile(n, confPath)) return n;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function createRuntime(profile) {
|
function createRuntime(profile) {
|
||||||
const container = profile.container;
|
let resolvedContainer = null;
|
||||||
const confPath = profile.confPath;
|
const confPath = profile.confPath;
|
||||||
const clientsPath = profile.clientsPath;
|
const clientsPath = profile.clientsPath;
|
||||||
const iface = profile.iface;
|
const iface = profile.iface;
|
||||||
const wgBinary = profile.wgBinary;
|
const wgBinary = profile.wgBinary;
|
||||||
const pskPath = profile.pskPath;
|
const pskPath = profile.pskPath;
|
||||||
|
|
||||||
|
async function resolveContainer() {
|
||||||
|
if (resolvedContainer) return resolvedContainer;
|
||||||
|
if (profile.container && (await containerHasFile(profile.container, confPath))) {
|
||||||
|
resolvedContainer = profile.container;
|
||||||
|
return resolvedContainer;
|
||||||
|
}
|
||||||
|
const found = await discoverAwgContainer(confPath, profile.container);
|
||||||
|
if (found) {
|
||||||
|
resolvedContainer = found;
|
||||||
|
if (found !== profile.container) {
|
||||||
|
console.log(
|
||||||
|
`\u2192 AWG container авто: «${found}» (профиль «${profile.label || profile.id}» указывал «${profile.container}»)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resolvedContainer;
|
||||||
|
}
|
||||||
|
const running = (await listRunningContainerNames()).join(", ") || "(нет запущенных)";
|
||||||
|
throw new Error(
|
||||||
|
`Контейнер AmneziaWG не найден. Профиль указывает «${profile.container}», но контейнера с таким именем нет и ни в одном запущенном нет файла ${confPath}. Запущенные контейнеры: ${running}. Проверьте, что инстанс Amnezia запущен, или задайте AWG_CONTAINER/AWG_PROFILES.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function dockerExec(cmd) {
|
async function dockerExec(cmd) {
|
||||||
|
const container = await resolveContainer();
|
||||||
const { stdout, stderr } = await execDocker(["exec", container, "sh", "-c", cmd]);
|
const { stdout, stderr } = await execDocker(["exec", container, "sh", "-c", cmd]);
|
||||||
return stdout + stderr;
|
return stdout + stderr;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dockerReadFile(remotePath) {
|
async function dockerReadFile(remotePath) {
|
||||||
|
const container = await resolveContainer();
|
||||||
const { stdout } = await execDocker(["exec", container, "cat", remotePath]);
|
const { stdout } = await execDocker(["exec", container, "cat", remotePath]);
|
||||||
return stdout;
|
return stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dockerWriteFile(remotePath, content) {
|
async function dockerWriteFile(remotePath, content) {
|
||||||
|
const container = await resolveContainer();
|
||||||
await execDocker(
|
await execDocker(
|
||||||
[
|
[
|
||||||
"exec",
|
"exec",
|
||||||
@@ -749,6 +811,7 @@ function createRuntime(profile) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
profile,
|
profile,
|
||||||
|
resolveContainer,
|
||||||
dockerExec,
|
dockerExec,
|
||||||
dockerReadFile,
|
dockerReadFile,
|
||||||
dockerWriteFile,
|
dockerWriteFile,
|
||||||
@@ -1785,7 +1848,7 @@ app.post("/api/warp/host-setup", requireAuth, requireProTier, async (req, res) =
|
|||||||
return res.status(400).json({ error: "Ожидается cmd: install или uninstall" });
|
return res.status(400).json({ error: "Ожидается cmd: install или uninstall" });
|
||||||
}
|
}
|
||||||
const rt = runtimeForRequest(req);
|
const rt = runtimeForRequest(req);
|
||||||
const container = String(rt.profile.container || "").trim();
|
const container = String((await rt.resolveContainer()) || "").trim();
|
||||||
if (!/^[a-zA-Z0-9_.-]+$/.test(container)) {
|
if (!/^[a-zA-Z0-9_.-]+$/.test(container)) {
|
||||||
return res.status(400).json({ error: "Некорректное имя контейнера в профиле AWG" });
|
return res.status(400).json({ error: "Некорректное имя контейнера в профиле AWG" });
|
||||||
}
|
}
|
||||||
@@ -1941,7 +2004,7 @@ app.get("/api/clients", requireAuth, async (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
profileId: rt.profile.id,
|
profileId: rt.profile.id,
|
||||||
profileLabel: rt.profile.label,
|
profileLabel: rt.profile.label,
|
||||||
container: rt.profile.container,
|
container: (await rt.resolveContainer().catch(() => rt.profile.container)),
|
||||||
protocol: "AmneziaWG",
|
protocol: "AmneziaWG",
|
||||||
peerCount: conf.peers.length,
|
peerCount: conf.peers.length,
|
||||||
clients: rows,
|
clients: rows,
|
||||||
|
|||||||
Reference in New Issue
Block a user