From 51d5d13c41d51d4bc5f952e4114dbf242a7356e8 Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Sat, 4 Apr 2026 15:48:15 +0300 Subject: [PATCH] feat: enhance SQL migration handling and add Docker installation instructions for remote servers --- README.md | 26 +++++++++++++++++++++++-- inc/InstallProtocolManager.php | 35 +++++++++++++++++++++++++++++++--- inc/VpnClient.php | 18 ++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a63bb07..9a6b940 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,40 @@ docker compose up -d docker compose exec web composer install # Ensure all SQL migrations are applied (safe to run repeatedly) -docker compose exec -T db sh -lc 'for f in /docker-entrypoint-initdb.d/*.sql; do mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true; done' +for f in migrations/*.sql; do + docker compose exec -T db mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true +done # Or for older Docker Compose V1 docker-compose up -d docker-compose exec web composer install -docker-compose exec -T db sh -lc 'for f in /docker-entrypoint-initdb.d/*.sql; do mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true; done' +for f in migrations/*.sql; do + docker-compose exec -T db mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true +done ``` Access: http://localhost:8082 Default login: admin@amnez.ia / admin123 +### Remote Server Prerequisite + +For protocol deployment on a clean remote host, Docker Engine must be available on that host. +If Docker is missing, install it first (Ubuntu example): + +```bash +apt-get update -y +apt-get install -y ca-certificates curl gnupg lsb-release +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg +chmod a+r /etc/apt/keyrings/docker.gpg +. /etc/os-release +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list +apt-get update -y +apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +systemctl enable --now docker +``` + ## Configuration Edit `.env`: diff --git a/inc/InstallProtocolManager.php b/inc/InstallProtocolManager.php index ca78a26..51caf99 100644 --- a/inc/InstallProtocolManager.php +++ b/inc/InstallProtocolManager.php @@ -1149,7 +1149,7 @@ class InstallProtocolManager if ($isAwg) { $detection = self::detectBuiltinAwg($server, $protocol); - if (in_array($detection['status'] ?? '', ['existing', 'partial'], true)) { + if (($detection['status'] ?? '') === 'existing') { Logger::appendInstall($serverId, 'Existing AWG installation detected, restoring instead of reinstalling'); $restoreResult = self::restoreBuiltinAwg($server, $protocol, $detection, $options); // Import existing clients into DB @@ -1177,7 +1177,7 @@ class InstallProtocolManager if ($isXray) { $xrayDetection = self::detectBuiltinXray($server, $protocol); - if (in_array($xrayDetection['status'] ?? '', ['existing', 'partial'], true)) { + if (($xrayDetection['status'] ?? '') === 'existing') { Logger::appendInstall($serverId, 'Existing X-Ray installation detected, restoring instead of reinstalling'); $restoreResult = self::restoreBuiltinXray($server, $protocol, $xrayDetection, $options); return array_merge($restoreResult, ['mode' => 'restore_existing']); @@ -1189,12 +1189,41 @@ class InstallProtocolManager if ($engine === 'builtin_awg') { $res = $server->runAwgInstall($options); Logger::appendInstall($serverId, 'Builtin AWG install finished'); + + $resolvedPort = null; + if (isset($res['vpn_port']) && (int) $res['vpn_port'] > 0) { + $resolvedPort = (int) $res['vpn_port']; + } elseif (isset($res['server_port']) && (int) $res['server_port'] > 0) { + $resolvedPort = (int) $res['server_port']; + } + + $resolvedAwgParams = $res['awg_params'] ?? null; + if (!is_array($resolvedAwgParams)) { + $candidate = []; + foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4'] as $k) { + if (array_key_exists($k, $res)) { + $candidate[$k] = $res[$k]; + } + } + if ($candidate) { + $resolvedAwgParams = $candidate; + } + } + + self::markServerActive($serverId, null, [ + 'vpn_port' => $resolvedPort, + 'server_public_key' => $res['server_public_key'] ?? null, + 'preshared_key' => $res['preshared_key'] ?? null, + 'container_name' => $res['container_name'] ?? null, + 'awg_params' => $resolvedAwgParams, + ]); + $pdo = DB::conn(); $pid = self::resolveProtocolId($protocol); if ($pid) { $config = [ 'server_host' => $server->getData()['host'] ?? null, - 'server_port' => $res['vpn_port'] ?? null, + 'server_port' => $resolvedPort, 'extras' => $res ]; $stmt2 = $pdo->prepare('INSERT INTO server_protocols (server_id, protocol_id, config_data, applied_at, created_at) VALUES (?, ?, ?, NOW(), NOW()) ON DUPLICATE KEY UPDATE config_data = VALUES(config_data), applied_at = NOW()'); diff --git a/inc/VpnClient.php b/inc/VpnClient.php index 08cf493..6361926 100644 --- a/inc/VpnClient.php +++ b/inc/VpnClient.php @@ -1459,10 +1459,19 @@ class VpnClient $awgParams = []; } + // Accept mixed-case keys from installer outputs (e.g. Jc/Jmin/Jmax) + // by duplicating them into canonical uppercase AWG keys. + foreach ($awgParams as $k => $v) { + $uk = strtoupper((string) $k); + if (in_array($uk, ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'], true) && !isset($awgParams[$uk])) { + $awgParams[$uk] = $v; + } + } + // If AWG params are missing (common after reinstall), fetch them directly from wg0.conf // to avoid falling back to template defaults that will not match the server. if (in_array($slug, ['amnezia-wg-advanced', 'awg2'], true)) { - $needKeys = ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4']; + $needKeys = ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4']; $missing = false; foreach ($needKeys as $k) { if (!isset($awgParams[$k])) { @@ -1494,6 +1503,13 @@ class VpnClient } } + if (!isset($awgParams['S3'])) { + $awgParams['S3'] = 0; + } + if (!isset($awgParams['S4'])) { + $awgParams['S4'] = 0; + } + // Still missing? Refuse to overwrite config with template defaults. foreach ($needKeys as $k) { if (!isset($awgParams[$k])) {