Release 3.9.0+3: subscription-URL routing, emoji flags, Remnawave provisioning.
Route credential-less http(s) URLs pasted into the add-link field to subscription import (fixes remaining 'proxy URI missing username'). Extend geoflag with RU country names and city hints; live Remnawave names already carrying emoji flags are kept as-is. Add admin provisioning via configs/remnawave-api.json (GET by-username / POST users, 50 GB MONTH plan) and the «Выдать доступ» UI panel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1026,6 +1026,15 @@
|
||||
<button class="action primary" id="rwSyncBtn" type="button" style="flex:1">Синхронизировать</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="remna-box" id="provisionBox" hidden>
|
||||
<div class="remna-title">Выдать доступ</div>
|
||||
<p class="remna-hint">Создаёт пользователя в панели Remnawave (тариф из configs/remnawave-api.json, по умолчанию 50 ГБ / 30 дней) и сразу импортирует его конфиги.</p>
|
||||
<div>
|
||||
<label class="field" for="provUser">Пользователь</label>
|
||||
<input id="provUser" type="text" placeholder="username / @telegram / email" spellcheck="false" autocomplete="off" />
|
||||
</div>
|
||||
<button class="action primary" id="provBtn" type="button">Выдать доступ</button>
|
||||
</div>
|
||||
<button class="action secondary" id="saveBtn" type="button">Сохранить профиль</button>
|
||||
</div>
|
||||
</details>
|
||||
@@ -1158,7 +1167,7 @@
|
||||
const methods = [
|
||||
"getState","connect","disconnect","connectProfile","saveProfile","createProfile",
|
||||
"selectProfile","deleteProfile","installCore","openURL","pingServers","pingBest",
|
||||
"checkUpdate","applyUpdate","saveHy2","importSubscription","saveRemnawave","importRemnawave","quit"
|
||||
"checkUpdate","applyUpdate","saveHy2","importSubscription","saveRemnawave","importRemnawave","provisionUser","quit"
|
||||
];
|
||||
if (typeof window.getState === "function") return;
|
||||
window.__navisHttp = true;
|
||||
@@ -1266,6 +1275,9 @@
|
||||
const rwCaddy = $("rwCaddy");
|
||||
const rwSaveBtn = $("rwSaveBtn");
|
||||
const rwSyncBtn = $("rwSyncBtn");
|
||||
const provisionBox = $("provisionBox");
|
||||
const provUser = $("provUser");
|
||||
const provBtn = $("provBtn");
|
||||
const hero = $("hero");
|
||||
const protoChip = $("protoChip");
|
||||
const heroHint = $("heroHint");
|
||||
@@ -1618,6 +1630,9 @@
|
||||
if (rwCaddy) rwCaddy.disabled = busy;
|
||||
if (rwSaveBtn) rwSaveBtn.disabled = busy;
|
||||
if (rwSyncBtn) rwSyncBtn.disabled = busy;
|
||||
if (provisionBox) provisionBox.hidden = !state.provision_ready;
|
||||
if (provUser) provUser.disabled = busy;
|
||||
if (provBtn) provBtn.disabled = busy;
|
||||
btn.disabled = busy;
|
||||
|
||||
rememberPings(state.pings || []);
|
||||
@@ -1696,7 +1711,7 @@
|
||||
}
|
||||
|
||||
function paintButtonsLocked(locked) {
|
||||
[btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, updateBtn, skipUpdateBtn, subBtn, subUrl, rwBase, rwShort, rwToken, rwCaddy, rwSaveBtn, rwSyncBtn].forEach((b) => {
|
||||
[btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, updateBtn, skipUpdateBtn, subBtn, subUrl, rwBase, rwShort, rwToken, rwCaddy, rwSaveBtn, rwSyncBtn, provUser, provBtn].forEach((b) => {
|
||||
if (b) b.disabled = locked;
|
||||
});
|
||||
}
|
||||
@@ -1869,6 +1884,35 @@
|
||||
}));
|
||||
}
|
||||
|
||||
if (provBtn) {
|
||||
provBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
const u = (provUser.value || "").trim();
|
||||
if (!u) {
|
||||
setMeta("Укажите имя пользователя (username / @telegram / email)", "err");
|
||||
return;
|
||||
}
|
||||
setMeta("Выдача доступа через панель…");
|
||||
const r = await provisionUser(u);
|
||||
formHydrated = false;
|
||||
dirty = false;
|
||||
let msg = (r.created ? "Создан пользователь " : "Найден пользователь ") + r.username;
|
||||
if (r.traffic_limit_bytes > 0) msg += " · " + fmtTraffic(r.traffic_limit_bytes);
|
||||
if (r.expire_at) {
|
||||
try { msg += " · до " + new Date(r.expire_at * 1000).toLocaleDateString("ru-RU"); } catch (_) {}
|
||||
}
|
||||
msg += " · импортировано серверов: " + (r.imported || 0);
|
||||
setMeta(msg, "ok");
|
||||
} catch (e) { setMeta(String(e), "err"); }
|
||||
}));
|
||||
provUser.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
provBtn.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pingBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
setMeta("Пинг серверов…");
|
||||
|
||||
Reference in New Issue
Block a user