From bb74e425fe2199194c90267118eef97f2143c376 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Fri, 8 May 2026 13:52:45 +0200 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20Phase=205f-v=20=E2=80=94=20in?= =?UTF-8?q?bound=20info=20+=20QR-code=20modals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires the row "info" and "qrcode" actions and ports the legacy inbound_info_modal end-to-end. The info modal handles every protocol the legacy panel did: • multi-user (VMess/VLess/Trojan/SS-multi/Hysteria) — per-client table + share links + per-link QR; • SS single-user — share link + QR; • WireGuard — full peer table with downloadable peer-N.conf and a wg:// share link per peer; • Mixed/HTTP/Tunnel — connection-detail tables. - QrPanel.vue: shared link card (header tag, copy button, optional download button, optional QR canvas, monospace footer with the raw value). Per-instance QRious instances are repainted on value/size change. - InboundInfoModal.vue: full info modal. Subscription URL block keys off subSettings.subURI/subJsonURI; IP-log lazy-loads on open and surfaces refresh + clear; tg-id, last-online, depleted/enabled tags all match legacy. - QrCodeModal.vue: lighter modal used for the row "qrcode" action on SS-single and WireGuard inbounds (just the QRs, no info table). - InboundsPage.vue: wires both flows. checkFallback() reproduces the legacy logic — when an inbound listens on a unix-socket fallback (`@`), the link generator is pointed at the root inbound that owns the listen address so QRs/links carry the public host:port + the right TLS state. Multi-client navigation (focusing a specific client's links) is deferred to 5f-vi where the per-inbound expand- row table will pass the email through. Co-Authored-By: Claude Opus 4.7 --- .../src/pages/inbounds/InboundInfoModal.vue | 768 ++++++++++++++++++ frontend/src/pages/inbounds/InboundsPage.vue | 78 ++ frontend/src/pages/inbounds/QrCodeModal.vue | 68 ++ frontend/src/pages/inbounds/QrPanel.vue | 128 +++ 4 files changed, 1042 insertions(+) create mode 100644 frontend/src/pages/inbounds/InboundInfoModal.vue create mode 100644 frontend/src/pages/inbounds/QrCodeModal.vue create mode 100644 frontend/src/pages/inbounds/QrPanel.vue diff --git a/frontend/src/pages/inbounds/InboundInfoModal.vue b/frontend/src/pages/inbounds/InboundInfoModal.vue new file mode 100644 index 00000000..6611bbce --- /dev/null +++ b/frontend/src/pages/inbounds/InboundInfoModal.vue @@ -0,0 +1,768 @@ + + + + + diff --git a/frontend/src/pages/inbounds/InboundsPage.vue b/frontend/src/pages/inbounds/InboundsPage.vue index c951091a..6c0d3b4c 100644 --- a/frontend/src/pages/inbounds/InboundsPage.vue +++ b/frontend/src/pages/inbounds/InboundsPage.vue @@ -19,6 +19,8 @@ import InboundList from './InboundList.vue'; import InboundFormModal from './InboundFormModal.vue'; import ClientFormModal from './ClientFormModal.vue'; import ClientBulkModal from './ClientBulkModal.vue'; +import InboundInfoModal from './InboundInfoModal.vue'; +import QrCodeModal from './QrCodeModal.vue'; import { useInbounds } from './useInbounds.js'; const antdThemeConfig = computed(() => ({ @@ -38,6 +40,8 @@ const { subSettings, tgBotEnable, ipLimitEnable, + remarkModel, + lastOnlineMap, refresh, fetchDefaultSettings, } = useInbounds(); @@ -65,6 +69,54 @@ const clientIndex = ref(null); const bulkOpen = ref(false); const bulkDbInbound = ref(null); +// === Info / QR-code modals =========================================== +const infoOpen = ref(false); +const infoDbInbound = ref(null); +const infoClientIndex = ref(0); + +const qrOpen = ref(false); +const qrDbInbound = ref(null); + +// `checkFallback` mirrors the legacy helper: when an inbound listens +// on a unix-socket fallback (`@`), point the link generator at +// the root inbound that owns the listen address so QRs/links carry +// the externally-reachable host:port and the right TLS state. +function checkFallback(dbInbound) { + // We don't keep parsed Inbounds in state right now (the page works + // off DBInbounds); compute on the fly. + if (!dbInbound.listen?.startsWith?.('@')) return dbInbound; + for (const candidate of dbInbounds.value) { + if (candidate.id === dbInbound.id) continue; + const parsed = candidate.toInbound(); + if (!parsed.isTcp) continue; + if (!['trojan', 'vless'].includes(parsed.protocol)) continue; + const fallbacks = parsed.settings.fallbacks || []; + if (!fallbacks.find((f) => f.dest === dbInbound.listen)) continue; + // Build a one-off DBInbound copy with the parent's listen/port + + // copied stream so the link gen sees the public endpoint. + const projected = JSON.parse(JSON.stringify(dbInbound)); + projected.listen = candidate.listen; + projected.port = candidate.port; + const inheritedStream = parsed.stream; + const ownInbound = dbInbound.toInbound(); + ownInbound.stream.security = inheritedStream.security; + ownInbound.stream.tls = inheritedStream.tls; + ownInbound.stream.externalProxy = inheritedStream.externalProxy; + projected.streamSettings = ownInbound.stream.toString(); + // Re-wrap so callers get the same DBInbound shape they had. + return new dbInbound.constructor(projected); + } + return dbInbound; +} + +function findClientIndex(dbInbound) { + // For now we always show client 0 — multi-client navigation lives + // in the per-inbound expand-row table (5f-vi). A future commit will + // route client.email through this helper. + void dbInbound; + return 0; +} + function onAddInbound() { formMode.value = 'add'; formDbInbound.value = null; @@ -204,6 +256,15 @@ function onRowAction({ key, dbInbound }) { case 'addBulkClient': openAddBulkClient(dbInbound); break; + case 'showInfo': + infoDbInbound.value = checkFallback(dbInbound); + infoClientIndex.value = findClientIndex(dbInbound); + infoOpen.value = true; + break; + case 'qrcode': + qrDbInbound.value = checkFallback(dbInbound); + qrOpen.value = true; + break; case 'delete': confirmDelete(dbInbound); break; @@ -345,6 +406,23 @@ function onRowAction({ key, dbInbound }) { :ip-limit-enable="ipLimitEnable" @saved="refresh" /> + + diff --git a/frontend/src/pages/inbounds/QrCodeModal.vue b/frontend/src/pages/inbounds/QrCodeModal.vue new file mode 100644 index 00000000..9eddb7be --- /dev/null +++ b/frontend/src/pages/inbounds/QrCodeModal.vue @@ -0,0 +1,68 @@ + + + diff --git a/frontend/src/pages/inbounds/QrPanel.vue b/frontend/src/pages/inbounds/QrPanel.vue new file mode 100644 index 00000000..2773c92a --- /dev/null +++ b/frontend/src/pages/inbounds/QrPanel.vue @@ -0,0 +1,128 @@ + + + + +