mirror of
https://github.com/andrey271192/amnezia_web.git
synced 2026-09-20 14:41:59 +00:00
fix(community): mount real host data dir into helper
Helper used DATA_DIR (in-container path) for bind-mount source. Daemon created phantom /data on host, log + staging went there instead of /opt/amnezia-admin-data. Resolve HOST_DATA_DIR via env or docker inspect; place staging under DATA_DIR so single mount exposes both log and install.sh to helper. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amnezia-admin",
|
"name": "amnezia-admin",
|
||||||
"version": "1.2.9",
|
"version": "1.2.10",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "amnezia_web — базовая панель AmneziaWG (FREE: просмотр и удаление клиентов; полная версия — PRO).",
|
"description": "amnezia_web — базовая панель AmneziaWG (FREE: просмотр и удаление клиентов; полная версия — PRO).",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ docker run -d --name "${CONTAINER_NAME}" --restart unless-stopped \
|
|||||||
-p "${HOST_PORT}:3980" \
|
-p "${HOST_PORT}:3980" \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-v "${DATA_DIR}:/data" \
|
-v "${DATA_DIR}:/data" \
|
||||||
|
-e "HOST_DATA_DIR=${DATA_DIR}" \
|
||||||
"${RUN_ENV[@]}" \
|
"${RUN_ENV[@]}" \
|
||||||
amnezia-admin:latest
|
amnezia-admin:latest
|
||||||
|
|
||||||
|
|||||||
58
server.js
58
server.js
@@ -1,5 +1,5 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { spawn } from "child_process";
|
import { spawn, execFileSync } from "child_process";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
@@ -147,6 +147,42 @@ const DATA_DIR = process.env.DATA_DIR || "/data";
|
|||||||
const PW_FILE = path.join(DATA_DIR, "password.hash");
|
const PW_FILE = path.join(DATA_DIR, "password.hash");
|
||||||
const SECRET_FILE = path.join(DATA_DIR, "session.secret");
|
const SECRET_FILE = path.join(DATA_DIR, "session.secret");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хостовый путь, смонтированный в контейнер панели как DATA_DIR.
|
||||||
|
* Нужен для одноразового docker-cli helper-а: контейнер панели монтирует
|
||||||
|
* `${HOST_DATA_DIR}:/mnt/data`, и оба контейнера видят один и тот же файл логов.
|
||||||
|
* Порядок поиска:
|
||||||
|
* 1) env HOST_DATA_DIR (передаётся install.sh при `docker run`);
|
||||||
|
* 2) docker inspect собственного контейнера (находим Source для Destination=DATA_DIR);
|
||||||
|
* 3) fallback /opt/amnezia-admin-data — типичный путь install.sh.
|
||||||
|
*/
|
||||||
|
function resolveHostDataDir() {
|
||||||
|
const explicit = process.env.HOST_DATA_DIR?.trim();
|
||||||
|
if (explicit) return explicit;
|
||||||
|
const selfId = (process.env.HOSTNAME || "").trim();
|
||||||
|
if (selfId) {
|
||||||
|
try {
|
||||||
|
const out = execFileSync(
|
||||||
|
"docker",
|
||||||
|
[
|
||||||
|
"inspect",
|
||||||
|
"--format",
|
||||||
|
`{{range .Mounts}}{{if eq .Destination "${DATA_DIR}"}}{{.Source}}{{end}}{{end}}`,
|
||||||
|
selfId,
|
||||||
|
],
|
||||||
|
{ stdio: ["ignore", "pipe", "ignore"], timeout: 5000 },
|
||||||
|
)
|
||||||
|
.toString()
|
||||||
|
.trim();
|
||||||
|
if (out) return out;
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "/opt/amnezia-admin-data";
|
||||||
|
}
|
||||||
|
const HOST_DATA_DIR = resolveHostDataDir();
|
||||||
|
|
||||||
const SESSION_COOKIE = "amnezia_sess";
|
const SESSION_COOKIE = "amnezia_sess";
|
||||||
const SESSION_MS = 7 * 24 * 60 * 60 * 1000;
|
const SESSION_MS = 7 * 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
@@ -1495,7 +1531,13 @@ app.post("/api/community/run-private-install", requireAuth, async (req, res) =>
|
|||||||
return res.status(400).json({ error: "Скачанный скрипт слишком большой — отказ." });
|
return res.status(400).json({ error: "Скачанный скрипт слишком большой — отказ." });
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir = fs.mkdtempSync(path.join("/tmp", "amnezia-priv-inst-"));
|
const stageRoot = path.join(DATA_DIR, ".priv-install-tmp");
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(stageRoot, { recursive: true });
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
tmpDir = fs.mkdtempSync(path.join(stageRoot, "amnezia-priv-inst-"));
|
||||||
const scriptPath = path.join(tmpDir, "install.sh");
|
const scriptPath = path.join(tmpDir, "install.sh");
|
||||||
fs.writeFileSync(scriptPath, buf);
|
fs.writeFileSync(scriptPath, buf);
|
||||||
fs.chmodSync(scriptPath, 0o700);
|
fs.chmodSync(scriptPath, 0o700);
|
||||||
@@ -1563,7 +1605,9 @@ app.post("/api/community/run-private-install", requireAuth, async (req, res) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (useDockerCliHelper) {
|
if (useDockerCliHelper) {
|
||||||
const dataAbs = path.resolve(DATA_DIR);
|
const dataHost = HOST_DATA_DIR;
|
||||||
|
const relStage = path.relative(DATA_DIR, tmpDir);
|
||||||
|
const stageInHelper = `/mnt/data/${relStage}`;
|
||||||
const prelude = COMMUNITY_SKIP_REMOVE_FREE_BEFORE_PRIVATE_PRO
|
const prelude = COMMUNITY_SKIP_REMOVE_FREE_BEFORE_PRIVATE_PRO
|
||||||
? "sleep 2"
|
? "sleep 2"
|
||||||
: [
|
: [
|
||||||
@@ -1577,11 +1621,13 @@ app.post("/api/community/run-private-install", requireAuth, async (req, res) =>
|
|||||||
exec >>"/mnt/data/community-install-last.log" 2>&1
|
exec >>"/mnt/data/community-install-last.log" 2>&1
|
||||||
set -eu
|
set -eu
|
||||||
echo "→ helper (docker cli): prelude + bash/curl..."
|
echo "→ helper (docker cli): prelude + bash/curl..."
|
||||||
|
echo " staging: ${stageInHelper} (host: ${tmpDir.replace(DATA_DIR, dataHost)})"
|
||||||
|
[ -f "${stageInHelper}/install.sh" ] || { echo "⚠ helper: нет install.sh в ${stageInHelper} (проверьте монтирование ${dataHost}:/mnt/data)"; ls -la "${stageInHelper}" 2>&1 || true; exit 125; }
|
||||||
${prelude}
|
${prelude}
|
||||||
${fetchTools ? `${fetchTools}\n` : ""}command -v bash >/dev/null 2>&1 || { echo "⚠ helper: нет bash после apk/apt — см. ошибки apk выше"; exit 126; }
|
${fetchTools ? `${fetchTools}\n` : ""}command -v bash >/dev/null 2>&1 || { echo "⚠ helper: нет bash после apk/apt — см. ошибки apk выше"; exit 126; }
|
||||||
command -v curl >/dev/null 2>&1 || { echo "⚠ helper: нет curl после apk/apt"; exit 126; }
|
command -v curl >/dev/null 2>&1 || { echo "⚠ helper: нет curl после apk/apt"; exit 126; }
|
||||||
set +e
|
set +e
|
||||||
( cd /mnt/stage && bash ./install.sh )
|
( cd ${stageInHelper} && bash ./install.sh )
|
||||||
rv=$?
|
rv=$?
|
||||||
set -e
|
set -e
|
||||||
echo ""
|
echo ""
|
||||||
@@ -1598,9 +1644,7 @@ exit "\${rv}"
|
|||||||
"-v",
|
"-v",
|
||||||
"/var/run/docker.sock:/var/run/docker.sock",
|
"/var/run/docker.sock:/var/run/docker.sock",
|
||||||
`-v`,
|
`-v`,
|
||||||
`${tmpDir}:/mnt/stage:ro`,
|
`${dataHost}:/mnt/data`,
|
||||||
`-v`,
|
|
||||||
`${dataAbs}:/mnt/data`,
|
|
||||||
"-e",
|
"-e",
|
||||||
"GITHUB_TOKEN",
|
"GITHUB_TOKEN",
|
||||||
"-e",
|
"-e",
|
||||||
|
|||||||
Reference in New Issue
Block a user