Wires up the inbound CRUD flows. The protocol-specific and transport-
specific forms are still ahead in 5f-iii-b — for now the modal exposes
those as JSON textareas so users can both edit existing inbounds without
losing settings and create new ones from default templates.
- InboundFormModal.vue: tabbed modal with a full Basics tab (enable,
remark, protocol, listen, port, total GB, traffic reset, expiry
date) and three JSON-edit tabs (Settings, Stream, Sniffing). Add
mode stamps a fresh template per protocol via
Inbound.Settings.getSettings(protocol); changing the protocol in
add mode restamps the JSON. Edit mode pretty-prints the existing
JSON so the user sees the same fields they save back.
- POST /panel/api/inbounds/add or /panel/api/inbounds/update/:id on
submit; on success the parent refreshes the list and the modal
closes. Malformed JSON in any of the three textareas surfaces a
message.error and aborts the save without losing user input.
- InboundsPage.vue: wires the row action menu to real handlers —
edit (opens the modal in edit mode), delete, reset-traffic,
clone, reset-clients, del-depleted-clients all go through
Modal.confirm and refresh on success. General actions menu wires
reset-inbounds / reset-clients / del-depleted-clients the same way.
Remaining actions (qrcode/info/import/export/copyClients) still
toast as "coming soon" — those land in 5f-iv and 5f-v.
- Adds dayjs ^1.11.20 dep for the a-date-picker v-model interop.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports the panel/security partial: change-credentials form and 2FA
toggle. The 2FA modal is a new shared component since enabling 2FA,
disabling 2FA, and changing credentials all funnel through it with
slightly different copy.
- TwoFactorModal.vue: 'set' flow renders a QR code + manual key + a
6-digit verifier; 'confirm' flow renders just the verifier. The
parent passes a confirm(success) callback that fires only when the
entered code matches the live TOTP value (otpauth lib).
- SecurityTab.vue: holds the local user form (oldUsername/oldPassword/
new*), POSTs /panel/setting/updateUser, and on success force-redirects
to logout. When 2FA is on, the credentials change goes through the
confirm-modal first.
- toggleTwoFactor leaves the switch read-only (the v-bound :checked
matches AllSetting) and only flips after the modal succeeds, so
cancelling out leaves state unchanged.
- Adds otpauth ^9.5.1 dep (qrious was already present).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bumps Vite to 8.0.11 (npm install picked up 6.4.2 from the stale
lockfile; clean install resolves the new constraint). Bumps vue-i18n
to 11.1.4 since v10 was just EOL'd.
Migrates aThemeSwitch.html — the two-flavor theme picker + global
themeSwitcher object — into:
- composables/useTheme.js: single reactive `theme` state with
toggleTheme / toggleUltra. Boot side-effect applies the stored theme
to <body>/<html> before Vue renders; watchEffect persists changes
back to localStorage.
- components/ThemeSwitch.vue: full menu version for the main panel.
- components/ThemeSwitchLogin.vue: login-popover version.
AD-Vue 1 → 4 changes hit on this component:
- <a-icon type="bulb" :theme="filled|outlined"> dropped — replaced by
explicit BulbFilled / BulbOutlined imports from
@ant-design/icons-vue, swapped via <component :is="BulbIcon">
- Vue.component('a-theme-switch', { ... }) global registration → SFC
+ per-page import
- this.$message.config(...) (Vue 2 instance method) → message.config(...)
imported from ant-design-vue, called once in login.js at boot
Login page now surfaces a settings button → popover → theme picker.
Known gap: web/assets/css/custom.min.css isn't yet imported into the
new bundle, so toggling dark mode currently only re-themes AD-Vue's
own components, not the panel chrome. The body class is still toggled
so behavior is correct; visual fidelity returns when custom.css is
ported or directly imported.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports the framework-agnostic JS from web/assets/js/ into frontend/src/
so Vue 3 pages can import what they need without relying on script-tag
globals.
- web/assets/js/util/index.js (927 lines, 21 classes) →
frontend/src/utils/legacy.js + a barrel at utils/index.js. All
classes are now named exports.
- Vue.prototype.$message in HttpUtil → direct import of `message`
from ant-design-vue (Vue 3 has no Vue.prototype).
- RandomUtil.randomShadowsocksPassword previously defaulted to
SSMethods.BLAKE3_AES_256_GCM from inbound.js, creating a circular
import. Replaced with the literal string default.
- MediaQueryMixin (Vue 2 mixin) removed. Replaced by
composables/useMediaQuery.js — Vue 3 composable returning reactive
`isMobile`.
- axios-init.js wrapped as setupAxios(); Qs global → npm `qs`.
- websocket.js exported as WebSocketClient class; the implicit
window.wsClient global is gone — pages instantiate it themselves.
- model/{inbound,outbound,dbinbound,setting,reality_targets}.js
copied with `export` added on every top-level declaration. Imports
between models and utils are wired up explicitly.
- subscription.js deferred to Phase 5 (it's a Vue 2 mount, not a util).
- App.vue smoke test exercises SizeFormatter / RandomUtil / Wireguard /
useMediaQuery so the user can verify Phase 3 with `npm run dev`.
Run `cd frontend && npm install && npm run dev` — qs was added so a
fresh install is required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>