feat: import clients from amnezia configs

This commit is contained in:
andrey271192
2026-06-21 18:22:36 +03:00
parent 8ba2a266e5
commit cff27e27b6
8 changed files with 305 additions and 5 deletions

View File

@@ -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: "" },

View File

@@ -240,6 +240,33 @@
</div>
</details>
<details class="panel-fold cascade-panel" id="import-panel">
<summary class="fold-summary">
<span class="fold-arrow" aria-hidden="true"></span>
<span class="fold-titles">
<span class="fold-h">Импорт клиента</span>
<span class="muted fold-meta">Перенос из готового .conf или backup Amnezia, где внутри есть конфиг</span>
</span>
</summary>
<div class="panel-fold-body">
<div class="muted cascade-guide">
<p class="cascade-intro">
Вставьте содержимое клиентского <code class="inline">.conf</code> или JSON backup. Панель добавит peer в текущий инстанс,
восстановит запись в <code class="inline">clientsTable</code> и сохранит исходный конфиг для дальнейшего экспорта.
</p>
</div>
<form id="import-form" class="cascade-form import-form">
<label for="import-name">Имя клиента / префикс</label>
<input id="import-name" type="text" placeholder="Например iPhone из backup" autocomplete="off">
<label for="import-config">.conf или JSON backup</label>
<textarea id="import-config" rows="10" spellcheck="false" placeholder="[Interface]&#10;PrivateKey = ...&#10;Address = 10.8.1.10/32&#10;&#10;[Peer]&#10;PublicKey = ..."></textarea>
<button type="submit" class="btn primary">Импортировать в текущий инстанс</button>
</form>
</div>
</details>
<details class="panel-fold cascade-panel" id="cascade-panel" open>
<summary class="fold-summary">
<span class="fold-arrow" aria-hidden="true"></span>

View File

@@ -844,10 +844,22 @@ tr:last-child td {
color: var(--muted);
}
.cascade-form input {
.cascade-form input,
.cascade-form textarea {
width: 100%;
}
.cascade-form textarea {
resize: vertical;
min-height: 12rem;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
line-height: 1.45;
}
.import-form {
max-width: 46rem;
}
.raw {
margin-top: 1.25rem;
color: var(--muted);