Release 4.0.0+1: rebrand Navis → EvilFox; centralized Remnawave auto-provisioning (panel credentials baked into binary); remove Remnawave API UI; Windows 4.0.0.1.
Keep dist/navis-release/ path and ship Navis.exe as a same-hash alias of EvilFox.exe so 3.x clients can still update from the Windows branch feed.
This commit is contained in:
+11
-139
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Navis</title>
|
||||
<title>EvilFox</title>
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
@@ -993,8 +993,8 @@
|
||||
<path d="M22.5 8.2l3.2-1.1-1.1 3.2-5.6 5.6 1.6 1.6 5.6-5.6 3.2-1.1-1.1 3.2" fill="#b8f0d8" opacity=".9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="brand">Navis</h1>
|
||||
<span class="badge-ver" id="badgeVer">3.10.0</span>
|
||||
<h1 class="brand">EvilFox</h1>
|
||||
<span class="badge-ver" id="badgeVer">4.0.0</span>
|
||||
</div>
|
||||
|
||||
<button class="nav-btn active" type="button" data-page="home">
|
||||
@@ -1020,7 +1020,7 @@
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="side-foot">
|
||||
<span class="ver" id="verLabel">Navis</span>
|
||||
<span class="ver" id="verLabel">EvilFox</span>
|
||||
<button type="button" class="theme-toggle" id="themeToggle" title="Тема" aria-label="Переключить тему">
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M20.5 14.2A8.2 8.2 0 0 1 9.8 3.5 8.5 8.5 0 1 0 20.5 14.2z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
|
||||
@@ -1150,17 +1150,6 @@
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Выдача доступа: скрыта, когда аккаунт уже привязан
|
||||
(создание второго конфига отключено — платная подписка появится позже) -->
|
||||
<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>
|
||||
</section>
|
||||
|
||||
<!-- ============ Настройки ============ -->
|
||||
@@ -1173,7 +1162,7 @@
|
||||
<button type="button" id="modeProxyBtn">Режим прокси</button>
|
||||
<button type="button" id="modeVpnBtn">Режим VPN</button>
|
||||
</div>
|
||||
<p class="remna-hint" id="modeHint">Прокси — трафик идёт через локальный прокси (и системный прокси Windows). VPN — весь трафик, включая игры и приложения, через TUN-адаптер; нужны права администратора и wintun.dll рядом с Navis.exe. Меняется в отключённом состоянии.</p>
|
||||
<p class="remna-hint" id="modeHint">Прокси — трафик идёт через локальный прокси (и системный прокси Windows). VPN — весь трафик, включая игры и приложения, через TUN-адаптер; нужны права администратора и wintun.dll рядом с EvilFox.exe. Меняется в отключённом состоянии.</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -1260,33 +1249,6 @@
|
||||
<label class="check"><input id="hy2Lazy" type="checkbox" /> Lazy connect</label>
|
||||
</details>
|
||||
|
||||
<details class="panel" id="rwPanel">
|
||||
<summary>Remnawave API</summary>
|
||||
<div class="remna-box" style="margin-top:8px;border:0;padding:0;background:transparent">
|
||||
<p class="remna-hint">Панель → short UUID пользователя. Токен API — опционально (для /api/subscriptions/… и /users/…).</p>
|
||||
<div>
|
||||
<label class="field" for="rwBase">URL панели</label>
|
||||
<input id="rwBase" type="text" placeholder="https://panel.example.com" spellcheck="false" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="field" for="rwShort">Short UUID</label>
|
||||
<input id="rwShort" type="text" placeholder="abc12xyz" spellcheck="false" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="field" for="rwToken">API token</label>
|
||||
<input id="rwToken" type="password" placeholder="Bearer из панели → API Tokens" spellcheck="false" autocomplete="off" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="field" for="rwCaddy">Caddy X-Api-Key</label>
|
||||
<input id="rwCaddy" type="password" placeholder="если стоит auth у Caddy" spellcheck="false" autocomplete="off" />
|
||||
</div>
|
||||
<div class="sub-bar" style="margin-top:8px">
|
||||
<button class="action secondary" id="rwSaveBtn" type="button" style="flex:1">Сохранить</button>
|
||||
<button class="action primary" id="rwSyncBtn" type="button" style="flex:1">Синхронизировать</button>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="panel" id="toolsBox">
|
||||
<summary>Сервис · cores, обновления</summary>
|
||||
<div class="tools">
|
||||
@@ -1317,9 +1279,9 @@
|
||||
<path d="M22.5 8.2l3.2-1.1-1.1 3.2-5.6 5.6 1.6 1.6 5.6-5.6 3.2-1.1-1.1 3.2" fill="#b8f0d8" opacity=".9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Navis</h2>
|
||||
<h2>EvilFox</h2>
|
||||
<p id="aboutVer">версия…</p>
|
||||
<p style="margin-top:6px">VPN/прокси-клиент: Remnawave · Naive · Hysteria 2 · AWG · Xray</p>
|
||||
<p style="margin-top:6px">VPN/прокси-клиент: Naive · Hysteria 2 · AWG · Xray</p>
|
||||
</div>
|
||||
<div class="tools" style="margin-top:0">
|
||||
<button class="action secondary" id="aboutUpdBtn" type="button">Проверить обновление</button>
|
||||
@@ -1498,15 +1460,6 @@
|
||||
}
|
||||
const subUrl = $("subUrl");
|
||||
const subBtn = $("subBtn");
|
||||
const rwBase = $("rwBase");
|
||||
const rwShort = $("rwShort");
|
||||
const rwToken = $("rwToken");
|
||||
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");
|
||||
@@ -1572,7 +1525,7 @@
|
||||
|
||||
function markDirty() { dirty = true; }
|
||||
[
|
||||
proxy, nameInput, sysproxy, subUrl, rwBase, rwShort, rwToken, rwCaddy,
|
||||
proxy, nameInput, sysproxy, subUrl,
|
||||
$("hy2Congestion"), $("hy2Bbr"), $("hy2Up"), $("hy2Down"),
|
||||
$("hy2Obfs"), $("hy2ObfsPass"), $("hy2Sni"), $("hy2Pin"), $("hy2Hop"),
|
||||
$("hy2Insecure"), $("hy2Fast"), $("hy2Lazy")
|
||||
@@ -1681,7 +1634,7 @@
|
||||
if (!list.length) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "server-empty";
|
||||
empty.textContent = "Нет серверов — добавьте профиль или подписку";
|
||||
empty.textContent = "Нет серверов — подождите автовыдачу или добавьте подписку";
|
||||
serverList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
@@ -1876,7 +1829,7 @@
|
||||
}
|
||||
|
||||
function renderUpdate(u, version) {
|
||||
const label = "Navis v" + (version || "?");
|
||||
const label = "EvilFox v" + (version || "?");
|
||||
verLabel.textContent = label;
|
||||
if (aboutVer) aboutVer.textContent = "Версия " + (version || "?");
|
||||
if (badgeVer && version) {
|
||||
@@ -1941,16 +1894,6 @@
|
||||
updateBtn.disabled = busy;
|
||||
subBtn.disabled = busy;
|
||||
subUrl.disabled = busy;
|
||||
if (rwBase) rwBase.disabled = busy;
|
||||
if (rwShort) rwShort.disabled = busy;
|
||||
if (rwToken) rwToken.disabled = busy;
|
||||
if (rwCaddy) rwCaddy.disabled = busy;
|
||||
if (rwSaveBtn) rwSaveBtn.disabled = busy;
|
||||
if (rwSyncBtn) rwSyncBtn.disabled = busy;
|
||||
// Выдать доступ — только пока аккаунт НЕ привязан (второй не создаём).
|
||||
if (provisionBox) provisionBox.hidden = !state.provision_ready || !!state.account;
|
||||
if (provUser) provUser.disabled = busy;
|
||||
if (provBtn) provBtn.disabled = busy;
|
||||
if (renewBtn) renewBtn.disabled = busy || !state.provision_ready;
|
||||
if (modeProxyBtn) modeProxyBtn.disabled = busy || connected;
|
||||
if (modeVpnBtn) modeVpnBtn.disabled = busy || connected;
|
||||
@@ -1965,12 +1908,6 @@
|
||||
if (typeof state.subscription_url === "string" && !dirty) {
|
||||
subUrl.value = state.subscription_url;
|
||||
}
|
||||
if (state.remnawave && !dirty) {
|
||||
rwBase.value = state.remnawave.base_url || "";
|
||||
rwShort.value = state.remnawave.short_uuid || "";
|
||||
if (typeof state.remnawave.token === "string") rwToken.value = state.remnawave.token;
|
||||
if (typeof state.remnawave.caddy_token === "string") rwCaddy.value = state.remnawave.caddy_token;
|
||||
}
|
||||
|
||||
if (syncForm || (!formHydrated && !dirty)) {
|
||||
const active = (state.profiles || []).find((p) => p.name === profile.value) || {};
|
||||
@@ -2038,8 +1975,7 @@
|
||||
|
||||
function paintButtonsLocked(locked) {
|
||||
[btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, aboutUpdBtn,
|
||||
updateBtn, skipUpdateBtn, subBtn, subUrl, rwBase, rwShort, rwToken, rwCaddy, rwSaveBtn, rwSyncBtn,
|
||||
provUser, provBtn, renewBtn, modeProxyBtn, modeVpnBtn, logsRefreshBtn].forEach((b) => {
|
||||
updateBtn, skipUpdateBtn, subBtn, subUrl, renewBtn, modeProxyBtn, modeVpnBtn, logsRefreshBtn].forEach((b) => {
|
||||
if (b) b.disabled = locked;
|
||||
});
|
||||
}
|
||||
@@ -2166,15 +2102,6 @@
|
||||
return "Импортировано: " + r;
|
||||
}
|
||||
|
||||
function readRemnawave() {
|
||||
return {
|
||||
base_url: (rwBase && rwBase.value || "").trim(),
|
||||
short_uuid: (rwShort && rwShort.value || "").trim(),
|
||||
token: (rwToken && rwToken.value || "").trim(),
|
||||
caddy_token: (rwCaddy && rwCaddy.value || "").trim()
|
||||
};
|
||||
}
|
||||
|
||||
subBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
await runImportSubscription();
|
||||
@@ -2187,61 +2114,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
if (rwSaveBtn) {
|
||||
rwSaveBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
await saveRemnawave(readRemnawave());
|
||||
setMeta("Настройки Remnawave сохранены", "ok");
|
||||
} catch (e) { setMeta(String(e), "err"); }
|
||||
}));
|
||||
}
|
||||
if (rwSyncBtn) {
|
||||
rwSyncBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
const s = readRemnawave();
|
||||
if (!s.base_url || !s.short_uuid) {
|
||||
setMeta("Укажите URL панели и short UUID", "err");
|
||||
return;
|
||||
}
|
||||
setMeta("Remnawave: загрузка конфигов…");
|
||||
const r = await importRemnawave(s);
|
||||
formHydrated = false;
|
||||
dirty = false;
|
||||
setMeta("Remnawave: " + importSummary(r).toLowerCase() + " · пинг…", "ok");
|
||||
await runBest(!!autoBest.checked);
|
||||
} catch (e) { setMeta(String(e), "err"); }
|
||||
}));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (renewBtn) {
|
||||
renewBtn.addEventListener("click", () => withBusy(async () => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user