3x-ui/frontend/src/pages/xray/XrayPage.vue

362 lines
12 KiB
Vue
Raw Normal View History

<script setup>
import { ref } from 'vue';
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
import { useI18n } from 'vue-i18n';
import { Modal, message } from 'ant-design-vue';
import {
SettingOutlined,
SwapOutlined,
UploadOutlined,
ClusterOutlined,
DatabaseOutlined,
CodeOutlined,
QuestionCircleOutlined,
} from '@ant-design/icons-vue';
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
import { useMediaQuery } from '@/composables/useMediaQuery.js';
import AppSidebar from '@/components/AppSidebar.vue';
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
import BasicsTab from './BasicsTab.vue';
import RoutingTab from './RoutingTab.vue';
feat(frontend): Phase 6-iv — xray Outbounds tab + outbound modal Replaces the Outbounds tab placeholder with a full table + add/edit flow. The 1.3k-line legacy outbound modal is condensed to a tabbed modal with structured Basics fields (tag/protocol/sendThrough/domain strategy) and JSON tabs for the protocol-specific settings + stream trees — same approach the Inbound modal uses, and a power user can still edit the same trees via the page-level Advanced (JSON) tab. - useXraySetting.js: adds fetchOutboundsTraffic + resetOutboundsTraffic + testOutbound. Test states are tracked per outbound index so the row's Test button can show loading + the Test-result column can render the response delay / status / error. - OutboundsTab.vue: full table (action / identity / address / traffic / test result / test) plus a card-list mobile variant with the same row dropdown (set-first / edit / move up/down / reset traffic / delete). outboundAddresses() reproduces the legacy findOutboundAddress logic so each protocol's host:port list is rendered consistently. Add/edit go through OutboundFormModal, delete goes through Modal.confirm, reset traffic posts to /panel/xray/resetOutboundsTraffic with the row's tag (or "-alltags-" from the toolbar). - OutboundFormModal.vue: tag/protocol/sendThrough/domainStrategy on the Basics tab; settings + streamSettings as raw JSON on their respective tabs. Tag-collision check happens client-side before emitting; malformed JSON aborts the save with a message.error. - XrayPage.vue: imports OutboundsTab and wires the test action to the composable's testOutbound helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:27:40 +00:00
import OutboundsTab from './OutboundsTab.vue';
feat(frontend): Phase 6-v — xray Balancers tab + DNS placeholder Brings Balancers to full parity with the legacy panel and adds a DNS tab placeholder that exposes the full dns/fakedns trees as JSON so users can edit them without falling through to Advanced. - BalancerFormModal.vue: tag (with duplicate-tag warning across other balancers), strategy (random/roundRobin/leastLoad/leastPing), selector tag-mode multi-select sourced from existing outbound tags + free-form additions, fallback. Disable-on-invalid is driven by the duplicateTag + emptySelector computed flags. - BalancersTab.vue: empty state with a single "Add balancer" CTA; populated state shows the legacy 4-column table (action / tag / strategy / selector / fallback) with per-row edit + delete in a dropdown. On submit the wire shape preserves the `strategy: { type }` nesting only when the strategy is non-default, matching the legacy emit. Tag renames also chase across routing.rules.balancerTag references so existing rules don't dangle. - DnsTab.vue: master enable switch + raw JSON for `dns` and `fakedns`. Legacy had a dedicated server-by-server editor + a fakedns row editor; both are big enough to deserve their own commits, and the JSON path supports every field today. WARP / NordVPN provisioning modals still toast as "coming soon" — those are third-party API integrations worth their own commits. The xray page now has structured editors for Basics / Routing / Outbounds / Balancers and JSON editors for DNS / Advanced — every xray tab the legacy panel offered is functional. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:30:48 +00:00
import BalancersTab from './BalancersTab.vue';
import DnsTab from './DnsTab.vue';
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals Replaces the toast stubs on the Basics tab and Outbounds toolbar with the legacy WARP + NordVPN provisioning flows. Both modals now stage their wireguard outbounds back into templateSettings.outbounds through the same event channels OutboundsTab uses, so the existing add / reset / delete / refresh-traffic surface keeps working. - WarpModal.vue: empty state shows a single Create button that generates a wireguard keypair locally (Wireguard.generateKeypair) and posts it to /panel/xray/warp/reg; populated state surfaces the access_token / device_id / license_key / private_key, lets the user upgrade to WARP+ via /panel/xray/warp/license, refreshes the account info from /panel/xray/warp/config (plan / quota / usage in human-readable bytes), and stages a wireguard outbound with the WARP-specific reserved-byte encoding pulled from client_id. Add / Reset / Delete go through events the parent routes back to templateSettings.outbounds. - NordModal.vue: dual-tab login (NordVPN access token → /panel/xray/nord/reg, or paste a NordLynx private key → /panel/xray/nord/setKey). Once authenticated, country / city / server selectors fetch from /panel/xray/nord/{countries,servers}, servers sort by load ascending, the lowest-load server in the current city auto-selects. Reset emits oldTag/newTag so the parent renames matching routing rules in place; logout emits a remove-routing-rules event with prefix `nord-` to purge any dangling references. - XrayPage.vue: holds warpOpen / nordOpen flags, ensures the outbounds array exists before mutating it, and wires the modal events (add-outbound / reset-outbound / remove-outbound / remove-routing-rules) to in-place edits of templateSettings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
import WarpModal from './WarpModal.vue';
import NordModal from './NordModal.vue';
import { useXraySetting } from './useXraySetting.js';
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
const { t } = useI18n();
const {
fetched,
spinning,
saveDisabled,
fetchError,
xraySetting,
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
templateSettings,
outboundTestUrl,
inboundTags,
clientReverseTags,
restartResult,
feat(frontend): Phase 6-iv — xray Outbounds tab + outbound modal Replaces the Outbounds tab placeholder with a full table + add/edit flow. The 1.3k-line legacy outbound modal is condensed to a tabbed modal with structured Basics fields (tag/protocol/sendThrough/domain strategy) and JSON tabs for the protocol-specific settings + stream trees — same approach the Inbound modal uses, and a power user can still edit the same trees via the page-level Advanced (JSON) tab. - useXraySetting.js: adds fetchOutboundsTraffic + resetOutboundsTraffic + testOutbound. Test states are tracked per outbound index so the row's Test button can show loading + the Test-result column can render the response delay / status / error. - OutboundsTab.vue: full table (action / identity / address / traffic / test result / test) plus a card-list mobile variant with the same row dropdown (set-first / edit / move up/down / reset traffic / delete). outboundAddresses() reproduces the legacy findOutboundAddress logic so each protocol's host:port list is rendered consistently. Add/edit go through OutboundFormModal, delete goes through Modal.confirm, reset traffic posts to /panel/xray/resetOutboundsTraffic with the row's tag (or "-alltags-" from the toolbar). - OutboundFormModal.vue: tag/protocol/sendThrough/domainStrategy on the Basics tab; settings + streamSettings as raw JSON on their respective tabs. Tag-collision check happens client-side before emitting; malformed JSON aborts the save with a message.error. - XrayPage.vue: imports OutboundsTab and wires the test action to the composable's testOutbound helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:27:40 +00:00
outboundsTraffic,
outboundTestStates,
fetchAll,
feat(frontend): Phase 6-iv — xray Outbounds tab + outbound modal Replaces the Outbounds tab placeholder with a full table + add/edit flow. The 1.3k-line legacy outbound modal is condensed to a tabbed modal with structured Basics fields (tag/protocol/sendThrough/domain strategy) and JSON tabs for the protocol-specific settings + stream trees — same approach the Inbound modal uses, and a power user can still edit the same trees via the page-level Advanced (JSON) tab. - useXraySetting.js: adds fetchOutboundsTraffic + resetOutboundsTraffic + testOutbound. Test states are tracked per outbound index so the row's Test button can show loading + the Test-result column can render the response delay / status / error. - OutboundsTab.vue: full table (action / identity / address / traffic / test result / test) plus a card-list mobile variant with the same row dropdown (set-first / edit / move up/down / reset traffic / delete). outboundAddresses() reproduces the legacy findOutboundAddress logic so each protocol's host:port list is rendered consistently. Add/edit go through OutboundFormModal, delete goes through Modal.confirm, reset traffic posts to /panel/xray/resetOutboundsTraffic with the row's tag (or "-alltags-" from the toolbar). - OutboundFormModal.vue: tag/protocol/sendThrough/domainStrategy on the Basics tab; settings + streamSettings as raw JSON on their respective tabs. Tag-collision check happens client-side before emitting; malformed JSON aborts the save with a message.error. - XrayPage.vue: imports OutboundsTab and wires the test action to the composable's testOutbound helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:27:40 +00:00
fetchOutboundsTraffic,
resetOutboundsTraffic,
testOutbound,
saveAll,
restartXray,
} = useXraySetting();
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
feat(frontend): Phase 6-iv — xray Outbounds tab + outbound modal Replaces the Outbounds tab placeholder with a full table + add/edit flow. The 1.3k-line legacy outbound modal is condensed to a tabbed modal with structured Basics fields (tag/protocol/sendThrough/domain strategy) and JSON tabs for the protocol-specific settings + stream trees — same approach the Inbound modal uses, and a power user can still edit the same trees via the page-level Advanced (JSON) tab. - useXraySetting.js: adds fetchOutboundsTraffic + resetOutboundsTraffic + testOutbound. Test states are tracked per outbound index so the row's Test button can show loading + the Test-result column can render the response delay / status / error. - OutboundsTab.vue: full table (action / identity / address / traffic / test result / test) plus a card-list mobile variant with the same row dropdown (set-first / edit / move up/down / reset traffic / delete). outboundAddresses() reproduces the legacy findOutboundAddress logic so each protocol's host:port list is rendered consistently. Add/edit go through OutboundFormModal, delete goes through Modal.confirm, reset traffic posts to /panel/xray/resetOutboundsTraffic with the row's tag (or "-alltags-" from the toolbar). - OutboundFormModal.vue: tag/protocol/sendThrough/domainStrategy on the Basics tab; settings + streamSettings as raw JSON on their respective tabs. Tag-collision check happens client-side before emitting; malformed JSON aborts the save with a message.error. - XrayPage.vue: imports OutboundsTab and wires the test action to the composable's testOutbound helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:27:40 +00:00
async function onTestOutbound(idx) {
const outbound = templateSettings.value?.outbounds?.[idx];
if (outbound) await testOutbound(idx, outbound);
}
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
// `WarpExist` / `NordExist` derive from the parsed templateSettings —
// the Basics tab gates its WARP / NordVPN domain selectors on whether
// the matching outbound is provisioned, falling back to a "configure"
// button that today just toasts (the modals land in 6-v).
const warpExist = computed(
() => !!templateSettings.value?.outbounds?.find((o) => o?.tag === 'warp'),
);
const nordExist = computed(
() => !!templateSettings.value?.outbounds?.find((o) => o?.tag?.startsWith?.('nord-')),
);
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals Replaces the toast stubs on the Basics tab and Outbounds toolbar with the legacy WARP + NordVPN provisioning flows. Both modals now stage their wireguard outbounds back into templateSettings.outbounds through the same event channels OutboundsTab uses, so the existing add / reset / delete / refresh-traffic surface keeps working. - WarpModal.vue: empty state shows a single Create button that generates a wireguard keypair locally (Wireguard.generateKeypair) and posts it to /panel/xray/warp/reg; populated state surfaces the access_token / device_id / license_key / private_key, lets the user upgrade to WARP+ via /panel/xray/warp/license, refreshes the account info from /panel/xray/warp/config (plan / quota / usage in human-readable bytes), and stages a wireguard outbound with the WARP-specific reserved-byte encoding pulled from client_id. Add / Reset / Delete go through events the parent routes back to templateSettings.outbounds. - NordModal.vue: dual-tab login (NordVPN access token → /panel/xray/nord/reg, or paste a NordLynx private key → /panel/xray/nord/setKey). Once authenticated, country / city / server selectors fetch from /panel/xray/nord/{countries,servers}, servers sort by load ascending, the lowest-load server in the current city auto-selects. Reset emits oldTag/newTag so the parent renames matching routing rules in place; logout emits a remove-routing-rules event with prefix `nord-` to purge any dangling references. - XrayPage.vue: holds warpOpen / nordOpen flags, ensures the outbounds array exists before mutating it, and wires the modal events (add-outbound / reset-outbound / remove-outbound / remove-routing-rules) to in-place edits of templateSettings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
// === WARP / NordVPN provisioning modals ============================
const warpOpen = ref(false);
const nordOpen = ref(false);
function showWarp() { warpOpen.value = true; }
function showNord() { nordOpen.value = true; }
function ensureOutbounds() {
if (!templateSettings.value) return null;
if (!Array.isArray(templateSettings.value.outbounds)) {
templateSettings.value.outbounds = [];
}
return templateSettings.value.outbounds;
}
function onAddOutbound(outbound) {
const list = ensureOutbounds();
if (list) list.push(outbound);
}
function onResetOutbound({ index, outbound, oldTag, newTag }) {
const list = ensureOutbounds();
if (!list || index < 0) return;
list[index] = outbound;
// Tag rename across routing rules — preserves Nord's
// server-switch flow without dangling references.
if (oldTag && newTag && oldTag !== newTag) {
const rules = templateSettings.value?.routing?.rules || [];
for (const r of rules) {
if (r?.outboundTag === oldTag) r.outboundTag = newTag;
}
}
}
function onRemoveOutboundByTag(tag) {
const list = ensureOutbounds();
if (!list) return;
const idx = list.findIndex((o) => o?.tag === tag);
if (idx >= 0) list.splice(idx, 1);
}
function onRemoveOutboundByIndex(index) {
const list = ensureOutbounds();
if (list && index >= 0) list.splice(index, 1);
}
function onRemoveRoutingRules({ prefix }) {
const rules = templateSettings.value?.routing?.rules;
if (!Array.isArray(rules)) return;
templateSettings.value.routing.rules = rules.filter(
(r) => !r?.outboundTag?.startsWith?.(prefix),
);
}
// `message` is used by some of the in-progress UX flows (kept around
// because future provisioning errors will surface through it).
void message;
const { isMobile } = useMediaQuery();
const basePath = window.__X_UI_BASE_PATH__ || '';
const requestUri = window.location.pathname;
// See SettingsPage scrollTarget — wrap so `document` is in scope.
function scrollTarget() {
return document.getElementById('content-layout');
}
function confirmRestart() {
Modal.confirm({
title: 'Restart xray?',
content: 'Reloads the xray service with the saved configuration.',
okText: 'Restart',
cancelText: 'Cancel',
onOk: () => restartXray(),
});
}
</script>
<template>
<a-config-provider :theme="antdThemeConfig">
<a-layout
class="xray-page"
:class="{ 'is-dark': themeState.isDark, 'is-ultra': themeState.isUltra }"
>
<AppSidebar :base-path="basePath" :request-uri="requestUri" />
<a-layout class="content-shell">
<a-layout-content id="content-layout" class="content-area">
<a-spin :spinning="spinning || !fetched" :delay="200" tip="Loading…" size="large">
<div v-if="!fetched" class="loading-spacer" />
<a-result
v-else-if="fetchError"
status="error"
:title="t('somethingWentWrong')"
:sub-title="fetchError"
>
<template #extra>
<a-button type="primary" @click="fetchAll">{{ t('check') }}</a-button>
</template>
</a-result>
<template v-else>
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]">
<!-- Save / Restart bar -->
<a-col :span="24">
<a-card hoverable>
<a-row class="header-row">
<a-col :xs="24" :sm="14" class="header-actions">
<a-space direction="horizontal">
<a-button type="primary" :disabled="saveDisabled" @click="saveAll">
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
{{ t('pages.xray.save') }}
</a-button>
<a-button type="primary" danger :disabled="!saveDisabled" @click="confirmRestart">
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
{{ t('pages.xray.restart') }}
</a-button>
<a-popover v-if="restartResult" placement="rightTop">
<template #title>Xray restart output</template>
<template #content>
<pre class="restart-result">{{ restartResult }}</pre>
</template>
<QuestionCircleOutlined class="restart-icon" />
</a-popover>
</a-space>
</a-col>
<a-col :xs="24" :sm="10" class="header-info">
<a-back-top :target="scrollTarget" :visibility-height="200" />
<a-alert
type="warning"
show-icon
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
:message="t('pages.settings.infoDesc')"
/>
</a-col>
</a-row>
</a-card>
</a-col>
<!-- Tabs -->
<a-col :span="24">
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
<a-tabs default-active-key="tpl-basic">
<a-tab-pane key="tpl-basic" class="tab-pane">
<template #tab>
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
<SettingOutlined /> <span>{{ t('pages.xray.basicTemplate') }}</span>
</template>
feat(frontend): Phase 6-ii — xray Basics tab structured editor Replaces the placeholder on the Basics tab with a structured form for the most-touched fields of the xray template — outbound + routing strategy, log levels, traffic stat counters, and the "basic routing" shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4 forced, WARP / NordVPN routing). - useXraySetting.js: hoists a parsed `templateSettings` reactive alongside the JSON string, with two cooperating watches that keep them in sync. Editing structured fields stringifies into xraySetting for the dirty-poll + Advanced JSON tab; editing the JSON re-parses into templateSettings only when valid, so structured tabs stay readable mid-edit. - BasicsTab.vue: collapse panels mirror the legacy partial — General, Statistics, Logs, Basic routing. Every input is a computed v-model reading/writing into templateSettings; the routing-rule shortcuts funnel through ruleGetter/ruleSetter which match the legacy templateRuleGetter/templateRuleSetter behavior (replace-first, drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters also call syncOutbound() to provision/prune the matching outbound. - XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist` from the parsed templateSettings. WARP/NordVPN provisioning modals are still placeholders that toast — those land in 6-v with the routing/outbound editors. Default tab flips back to Basics so users land on the structured editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
<BasicsTab
:template-settings="templateSettings"
:outbound-test-url="outboundTestUrl"
:warp-exist="warpExist"
:nord-exist="nordExist"
@update:outbound-test-url="(v) => (outboundTestUrl = v)"
@show-warp="showWarp"
@show-nord="showNord"
/>
</a-tab-pane>
<a-tab-pane key="tpl-routing" class="tab-pane">
<template #tab>
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
<SwapOutlined /> <span>{{ t('pages.xray.Routings') }}</span>
</template>
<RoutingTab
:template-settings="templateSettings"
:inbound-tags="inboundTags"
:client-reverse-tags="clientReverseTags"
:is-mobile="isMobile"
/>
</a-tab-pane>
<a-tab-pane key="tpl-outbound" class="tab-pane">
<template #tab>
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
<UploadOutlined /> <span>{{ t('pages.xray.Outbounds') }}</span>
</template>
feat(frontend): Phase 6-iv — xray Outbounds tab + outbound modal Replaces the Outbounds tab placeholder with a full table + add/edit flow. The 1.3k-line legacy outbound modal is condensed to a tabbed modal with structured Basics fields (tag/protocol/sendThrough/domain strategy) and JSON tabs for the protocol-specific settings + stream trees — same approach the Inbound modal uses, and a power user can still edit the same trees via the page-level Advanced (JSON) tab. - useXraySetting.js: adds fetchOutboundsTraffic + resetOutboundsTraffic + testOutbound. Test states are tracked per outbound index so the row's Test button can show loading + the Test-result column can render the response delay / status / error. - OutboundsTab.vue: full table (action / identity / address / traffic / test result / test) plus a card-list mobile variant with the same row dropdown (set-first / edit / move up/down / reset traffic / delete). outboundAddresses() reproduces the legacy findOutboundAddress logic so each protocol's host:port list is rendered consistently. Add/edit go through OutboundFormModal, delete goes through Modal.confirm, reset traffic posts to /panel/xray/resetOutboundsTraffic with the row's tag (or "-alltags-" from the toolbar). - OutboundFormModal.vue: tag/protocol/sendThrough/domainStrategy on the Basics tab; settings + streamSettings as raw JSON on their respective tabs. Tag-collision check happens client-side before emitting; malformed JSON aborts the save with a message.error. - XrayPage.vue: imports OutboundsTab and wires the test action to the composable's testOutbound helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:27:40 +00:00
<OutboundsTab
:template-settings="templateSettings"
:outbounds-traffic="outboundsTraffic"
:outbound-test-states="outboundTestStates"
:is-mobile="isMobile"
@refresh-traffic="fetchOutboundsTraffic"
@reset-traffic="resetOutboundsTraffic"
@test="onTestOutbound"
@show-warp="showWarp"
@show-nord="showNord"
/>
</a-tab-pane>
<a-tab-pane key="tpl-balancer" class="tab-pane">
<template #tab>
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
<ClusterOutlined /> <span>{{ t('pages.xray.Balancers') }}</span>
</template>
feat(frontend): Phase 6-v — xray Balancers tab + DNS placeholder Brings Balancers to full parity with the legacy panel and adds a DNS tab placeholder that exposes the full dns/fakedns trees as JSON so users can edit them without falling through to Advanced. - BalancerFormModal.vue: tag (with duplicate-tag warning across other balancers), strategy (random/roundRobin/leastLoad/leastPing), selector tag-mode multi-select sourced from existing outbound tags + free-form additions, fallback. Disable-on-invalid is driven by the duplicateTag + emptySelector computed flags. - BalancersTab.vue: empty state with a single "Add balancer" CTA; populated state shows the legacy 4-column table (action / tag / strategy / selector / fallback) with per-row edit + delete in a dropdown. On submit the wire shape preserves the `strategy: { type }` nesting only when the strategy is non-default, matching the legacy emit. Tag renames also chase across routing.rules.balancerTag references so existing rules don't dangle. - DnsTab.vue: master enable switch + raw JSON for `dns` and `fakedns`. Legacy had a dedicated server-by-server editor + a fakedns row editor; both are big enough to deserve their own commits, and the JSON path supports every field today. WARP / NordVPN provisioning modals still toast as "coming soon" — those are third-party API integrations worth their own commits. The xray page now has structured editors for Basics / Routing / Outbounds / Balancers and JSON editors for DNS / Advanced — every xray tab the legacy panel offered is functional. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:30:48 +00:00
<BalancersTab :template-settings="templateSettings" />
</a-tab-pane>
<a-tab-pane key="tpl-dns" class="tab-pane">
<template #tab>
<DatabaseOutlined /> <span>DNS</span>
</template>
feat(frontend): Phase 6-v — xray Balancers tab + DNS placeholder Brings Balancers to full parity with the legacy panel and adds a DNS tab placeholder that exposes the full dns/fakedns trees as JSON so users can edit them without falling through to Advanced. - BalancerFormModal.vue: tag (with duplicate-tag warning across other balancers), strategy (random/roundRobin/leastLoad/leastPing), selector tag-mode multi-select sourced from existing outbound tags + free-form additions, fallback. Disable-on-invalid is driven by the duplicateTag + emptySelector computed flags. - BalancersTab.vue: empty state with a single "Add balancer" CTA; populated state shows the legacy 4-column table (action / tag / strategy / selector / fallback) with per-row edit + delete in a dropdown. On submit the wire shape preserves the `strategy: { type }` nesting only when the strategy is non-default, matching the legacy emit. Tag renames also chase across routing.rules.balancerTag references so existing rules don't dangle. - DnsTab.vue: master enable switch + raw JSON for `dns` and `fakedns`. Legacy had a dedicated server-by-server editor + a fakedns row editor; both are big enough to deserve their own commits, and the JSON path supports every field today. WARP / NordVPN provisioning modals still toast as "coming soon" — those are third-party API integrations worth their own commits. The xray page now has structured editors for Basics / Routing / Outbounds / Balancers and JSON editors for DNS / Advanced — every xray tab the legacy panel offered is functional. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:30:48 +00:00
<DnsTab :template-settings="templateSettings" />
</a-tab-pane>
<a-tab-pane key="tpl-advanced" class="tab-pane">
<template #tab>
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards Replaces hardcoded English with t() calls in the components every user sees on every page load. The translations themselves come from the existing TOML files via the sync script — no new strings, no new locale keys. Per component: - AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings / xray / logout). Computed so the sidebar re-renders when the cookie-driven locale flips on reload. - IndexPage.vue: Quick actions card title + Logs / Backup / Up-to- date / Update buttons. - StatusCard.vue: CPU / Memory / Swap / Storage labels + logical-processors / frequency tooltips. - XrayStatusCard.vue: card title + error popover header + Stop / Restart / Switch xray action labels (kept the v-prefix version string as-is — it's content, not a label). - SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons + unsaved-changes warning. - XrayPage.vue: 6 tab titles + Save / Restart-xray buttons + unsaved-changes warning. - InboundsPage.vue: 5 summary-stat card titles. - InboundList.vue: 10 column titles (computed for live locale), Add inbound / General actions buttons + every dropdown menu item, search placeholder, filter radio labels, popover titles (disabled / depleted / depleting / online), traffic + info popover row labels. Total: ~75 strings localised across 8 files. The remaining English labels live in the per-tab settings forms, the form modals (Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and the per-row table cell helpers — all incremental work that doesn't touch infrastructure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
<CodeOutlined /> <span>{{ t('pages.xray.advancedTemplate') }}</span>
</template>
<a-form layout="vertical">
<a-form-item label="xraySetting (full JSON)">
<a-textarea
v-model:value="xraySetting"
:auto-size="{ minRows: 18, maxRows: 40 }"
spellcheck="false"
class="json-editor"
/>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
</a-col>
</a-row>
</template>
</a-spin>
</a-layout-content>
</a-layout>
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals Replaces the toast stubs on the Basics tab and Outbounds toolbar with the legacy WARP + NordVPN provisioning flows. Both modals now stage their wireguard outbounds back into templateSettings.outbounds through the same event channels OutboundsTab uses, so the existing add / reset / delete / refresh-traffic surface keeps working. - WarpModal.vue: empty state shows a single Create button that generates a wireguard keypair locally (Wireguard.generateKeypair) and posts it to /panel/xray/warp/reg; populated state surfaces the access_token / device_id / license_key / private_key, lets the user upgrade to WARP+ via /panel/xray/warp/license, refreshes the account info from /panel/xray/warp/config (plan / quota / usage in human-readable bytes), and stages a wireguard outbound with the WARP-specific reserved-byte encoding pulled from client_id. Add / Reset / Delete go through events the parent routes back to templateSettings.outbounds. - NordModal.vue: dual-tab login (NordVPN access token → /panel/xray/nord/reg, or paste a NordLynx private key → /panel/xray/nord/setKey). Once authenticated, country / city / server selectors fetch from /panel/xray/nord/{countries,servers}, servers sort by load ascending, the lowest-load server in the current city auto-selects. Reset emits oldTag/newTag so the parent renames matching routing rules in place; logout emits a remove-routing-rules event with prefix `nord-` to purge any dangling references. - XrayPage.vue: holds warpOpen / nordOpen flags, ensures the outbounds array exists before mutating it, and wires the modal events (add-outbound / reset-outbound / remove-outbound / remove-routing-rules) to in-place edits of templateSettings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
<WarpModal
v-model:open="warpOpen"
:template-settings="templateSettings"
@add-outbound="onAddOutbound"
@reset-outbound="onResetOutbound"
@remove-outbound="onRemoveOutboundByTag"
/>
<NordModal
v-model:open="nordOpen"
:template-settings="templateSettings"
@add-outbound="onAddOutbound"
@reset-outbound="onResetOutbound"
@remove-outbound="onRemoveOutboundByIndex"
@remove-routing-rules="onRemoveRoutingRules"
/>
</a-layout>
</a-config-provider>
</template>
<style scoped>
.xray-page {
--bg-page: #f0f2f5;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.xray-page.is-dark {
--bg-page: #0a1222;
--bg-card: #151f31;
}
.xray-page.is-dark.is-ultra {
--bg-page: #21242a;
--bg-card: #0c0e12;
}
.xray-page :deep(.ant-layout),
.xray-page :deep(.ant-layout-content) {
background: transparent;
}
.content-shell { background: transparent; }
.content-area { padding: 24px; }
.loading-spacer { min-height: calc(100vh - 120px); }
.header-row {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.header-actions { padding: 4px; }
.header-info {
display: flex;
justify-content: flex-end;
}
.tab-pane { padding-top: 20px; }
.restart-icon {
font-size: 16px;
cursor: pointer;
color: var(--ant-primary-color, #1890ff);
}
.restart-result {
max-width: 480px;
white-space: pre-wrap;
font-size: 12px;
margin: 0;
}
.json-editor {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 12px;
}
</style>