From cff27e27b6fa4d395c0b1f978792cfb5591e4b4c Mon Sep 17 00:00:00 2001
From: andrey271192 <76248502+andrey271192@users.noreply.github.com>
Date: Sun, 21 Jun 2026 18:22:36 +0300
Subject: [PATCH] feat: import clients from amnezia configs
---
README.md | 14 +++
docs/panel-guide.md | 3 +
package-lock.json | 4 +-
package.json | 2 +-
public/app.js | 35 ++++++++
public/index.html | 27 ++++++
public/styles.css | 14 ++-
server.js | 211 +++++++++++++++++++++++++++++++++++++++++++-
8 files changed, 305 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index d3fd6f7..aba16c0 100644
--- a/README.md
+++ b/README.md
@@ -152,6 +152,20 @@ curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia_web-PRO/main/s
Если на хосте уже запущено **несколько** контейнеров с именами вида **`amnezia-awg*`**, а в панели по-прежнему один инстанс — перезапустите установщик из GitHub. Он автоматически создаст **`AWG_PROFILES`** и сохранит файл **`/root/amnezia-admin.awg-profiles.json`**. Ручной JSON нужен только при нестандартных путях внутри контейнеров.
+### Импорт клиента из Amnezia backup / `.conf`
+
+В панели есть блок **«Импорт клиента»**. Он нужен, когда клиент уже создан в приложении Amnezia, но в веб-панели его нет или у строки нет `last_config`.
+
+Что поддерживается:
+
+- вставка готового клиентского **`.conf`** с секциями `[Interface]` и `[Peer]`;
+- вставка JSON backup, если внутри него есть строка с таким `.conf`;
+- восстановление записи в **`clientsTable`** без дублирования peer, если peer уже есть в `awg0.conf`;
+- добавление peer в текущий инстанс, если его ещё нет на сервере;
+- сохранение исходного `.conf` в `userData.last_config`, чтобы дальше работали экспорт и управление из веба.
+
+Перед импортом выберите нужный **«Инстанс»** вверху панели. Если backup приложения зашифрован или не содержит текстовый `.conf`, сначала экспортируйте клиентский конфиг из приложения Amnezia и вставьте его в форму импорта.
+
### Telegram MTProto‑прокси
**Не связано с AmneziaWG.** Отдельный контейнер образа **[telegrammessenger/proxy](https://hub.docker.com/r/telegrammessenger/proxy)** на том же хосте. После входа в панель: установка, перезапуск, удаление, выбор порта на хосте, просмотр логов и ссылки **`tg://proxy`** (хост из **`MTPRO_PUBLIC_HOST`**, **`CLIENT_CONFIG_ENDPOINT`** или из URL страницы панели).
diff --git a/docs/panel-guide.md b/docs/panel-guide.md
index 829e9b6..854bb25 100644
--- a/docs/panel-guide.md
+++ b/docs/panel-guide.md
@@ -11,6 +11,7 @@
| **Cloudflare WARP** | *Необязательно.* Вывод части клиентов в интернет через интерфейс `warp` в контейнере AWG. Если WARP не ставили — статус **«Не установлен»** нормален; панель и VPN без этого работают. Установка и удаление — скрипт `scripts/warp-amnezia.sh` на хосте (`install` / **`uninstall`**), подробности в основном [README](../README.md). |
| **Telegram MTProto‑прокси** | *Необязательно*, не часть AWG. Docker-контейнер официального образа **telegrammessenger/proxy**; установка из панели, ссылка **`tg://proxy`**, переменные **`MTPRO_*`** — см. основной [README](../README.md). |
| **Новый клиент под каскад** | Клиент в конфиге смотрит на **промежуточный узел** по Endpoint; peer и ключи создаются **на текущем VPS** и отдаются в `.conf`. На промежуточном сервере заранее нужен проброс порта DNAT UDP до WG этого VPS ([**kaskad_web_vpn**](https://github.com/andrey271192/kaskad_web_vpn) или свой проброс); в блоке см. текст и команду установки каскад-панели. |
+| **Импорт клиента** | Перенос готового клиента из `.conf` или JSON backup Amnezia, если backup содержит текстовый конфиг. Панель восстанавливает строку в `clientsTable`, сохраняет `last_config` и добавляет peer только если его ещё нет в серверном конфиге. |
| **Пользователи** | Вкл/выкл peer, удаление, переименование, даты отключения; экспорт `.conf` если в записи есть `userData.last_config`. |
## Переключатель «Инстанс» не отображается
@@ -61,6 +62,7 @@
- **Старые строки без `last_config`** — полный `.conf` с сервера собрать нельзя (нет приватного ключа). Используйте приложение Amnezia или блок **«Новый клиент под каскад»** (новый ключ на сервере).
- **Экспорт по кнопкам** — только если в `clientsTable` есть **`userData.last_config`** с полем `config` или `client_priv_key`.
+- **Импорт из приложения Amnezia** — откройте блок **«Импорт клиента»**, выберите нужный **«Инстанс»**, вставьте клиентский `.conf` или JSON backup, где внутри есть такой `.conf`, и нажмите импорт. Если peer уже есть в `awg0.conf`, панель не создаёт дубль, а только восстанавливает/обновляет строку в `clientsTable`.
### Экспорт: имя файла и прямая ссылка
@@ -89,6 +91,7 @@
| POST | `/api/clients/disconnect-date` | Даты отключения / расписание |
| GET/POST | `/api/clients/export-config` | Скачать `.conf`; GET — прямая ссылка (сессия); опционально `?token=…` если задан `EXPORT_CONFIG_SECRET` |
| POST | `/api/clients/create-cascade` | `{ "endpointHost", "endpointPort?", "tunnelIp?", "clientName?", "profileId?" }` — новый peer и файл `.conf` |
+| POST | `/api/clients/import-config` | `{ "configText", "clientName?", "profileId?" }` — импорт клиента из `.conf` или JSON backup с вложенным `.conf` |
| POST | `/api/warp/host-setup` | Установка/удаление WARP на хосте по SSH: `{ "rootPassword", "cmd": "install" \| "uninstall" }` (как синхронизация времени; каталог скрипта — `WARP_SSH_INSTALL_DIR`) |
| POST | `/api/warp/start` | Поднять WARP |
| POST | `/api/warp/stop` | Остановить WARP |
diff --git a/package-lock.json b/package-lock.json
index b02d931..05f726c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "amnezia-admin",
- "version": "1.2.30",
+ "version": "1.2.31",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "amnezia-admin",
- "version": "1.2.30",
+ "version": "1.2.31",
"license": "MIT",
"dependencies": {
"express": "^4.21.2"
diff --git a/package.json b/package.json
index c1eafec..b6f7893 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amnezia-admin",
- "version": "1.2.30",
+ "version": "1.2.31",
"private": false,
"description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)",
"license": "MIT",
diff --git a/public/app.js b/public/app.js
index a8df635..591abcf 100644
--- a/public/app.js
+++ b/public/app.js
@@ -658,6 +658,11 @@ if (directForm) {
directForm.addEventListener("submit", (ev) => void downloadDirectConf(ev));
}
+const importForm = document.querySelector("#import-form");
+if (importForm) {
+ importForm.addEventListener("submit", (ev) => void importClientConfig(ev));
+}
+
const instanceForm = document.querySelector("#instance-form");
if (instanceForm) {
instanceForm.addEventListener("submit", (ev) => void createInstance(ev));
@@ -1424,6 +1429,36 @@ async function downloadDirectConf(ev) {
}
}
+async function importClientConfig(ev) {
+ ev.preventDefault();
+ const nameEl = document.querySelector("#import-name");
+ const configEl = document.querySelector("#import-config");
+ const configText = configEl?.value.trim() || "";
+ if (!configText) {
+ setStatus("Вставьте .conf или JSON backup для импорта.", true);
+ return;
+ }
+ const body = { configText };
+ const nm = nameEl?.value.trim();
+ if (nm) body.clientName = nm;
+ const pid = currentProfileIdValue();
+ if (pid) body.profileId = pid;
+ try {
+ setStatus("Импортирую клиента в текущий инстанс…", false);
+ const data = await api("/api/clients/import-config", {
+ method: "POST",
+ body: JSON.stringify(body),
+ });
+ const count = Array.isArray(data.imported) ? data.imported.length : 0;
+ if (configEl) configEl.value = "";
+ if (nameEl) nameEl.value = "";
+ setStatus(`Импортировано клиентов: ${count}. Таблица обновлена.`, false);
+ await loadClients();
+ } catch (e) {
+ setStatus(String(e.message || e), true);
+ }
+}
+
const VARIANT_META = {
awg2: { icon: "✨", title: "AmneziaWG 2.0", badge: "NEW" },
awg: { icon: "🔮", title: "AmneziaWG", badge: "" },
diff --git a/public/index.html b/public/index.html
index d0900cb..fcccf0f 100644
--- a/public/index.html
+++ b/public/index.html
@@ -240,6 +240,33 @@
+
+
+
+
+ Импорт клиента
+ Перенос из готового .conf или backup Amnezia, где внутри есть конфиг
+
+
+
+
+
+ Вставьте содержимое клиентского .conf или JSON backup. Панель добавит peer в текущий инстанс,
+ восстановит запись в clientsTable и сохранит исходный конфиг для дальнейшего экспорта.
+