The subscription info page was the last page still rendered by Go
templates. Move it to the Vite multi-page setup so the whole panel
loads through one toolchain.
Frontend: SubPage.vue mounts at /sub/<id>?html=1 and reads window.__SUB_PAGE_DATA__
for the parsed view-model (traffic / quota / expiry + rendered share
links). Fix descriptions borders against the light-theme card by
painting the row divider on each cell's bottom edge — AD-Vue's <tr>
border doesn't render reliably under border-collapse:collapse.
Backend: serveSubPage reads dist/subpage.html, injects
window.__X_UI_BASE_PATH__ + window.__SUB_PAGE_DATA__ before </head>,
and rewrites Vite's absolute /assets/ URLs when the panel runs under
a URL prefix. Drop the legacy template-FuncMap wiring and switch the
sub server's static mount from web/assets to web/dist/assets.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Index dashboard regains the 8 cards that were lost in the SPA port
(3X-UI panel info, Operation Hours, System Load, Usage, Overall Speed,
Total Data, IP Addresses, Connection Stats), plus a Config button that
shows the live xray config.json. Version display falls back through
panelUpdateInfo → window.__X_UI_CUR_VER__ → '?' so dev mode isn't blank.
- Xray config no longer hangs on load: useXraySetting surfaces failures
instead of leaving a perpetual spinner, and the Vite dev proxy stops
hijacking POST requests to migrated routes (only GETs get bypassed).
- Inbound page no longer throws __asyncLoader/emitsOptions errors —
inbound.js was missing imports (NumberFormatter, SizeFormatter,
Wireguard) and InboundList kept emitting after unmount.
- Login round-trip works after logout: a public /csrf-token endpoint
bootstraps the SPA before authentication, axios caches the token
module-level, and the dev 401 handler navigates to /login.html
instead of reloading the dashboard into a redirect loop.
- legacy.css mirrors the legacy panel's surface/text variables so dark
and ultra-dark themes match main; every SPA entry imports it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The fifth and last legacy page comes online. Tabs are scaffolded with
a-empty placeholders for the structured editors (Basics / Routing /
Outbounds / Balancers / DNS) so navigation is stable; the
Advanced (JSON) tab is fully functional and lets power users edit
the raw xraySetting tree exactly like the legacy CodeMirror pane.
- xray.html + src/xray.js: fifth Vite multi-page entry, mounted as
XrayPage; vite.config.js routes /panel/xray and /panel/xray/ to it
through the dev proxy bypass alongside the other pages.
- XrayPage.vue: page chrome with the Save / Restart-xray bar, restart-
output popover (surfaces /panel/xray/getXrayResult content when
startup fails), 6 a-tabs, and a textarea-backed Advanced JSON editor.
CodeMirror is intentionally not pulled in — the textarea works for
every modern browser and keeps the bundle slim while structured
editors land in 6-ii through 6-v.
- useXraySetting.js composable: POST /panel/xray/ on mount, mirrors
the settings-page 1s busy-loop dirty check for both xraySetting
and outboundTestUrl, and exposes saveAll + restartXray. The dirty
flag relies on string equality of the pretty-printed JSON, so
reformat-only edits don't enable Save.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the inbounds entry as a fourth Vite multi-page input and wires
/panel/inbounds through the dev proxy bypass. Lays down the page
chrome (sidebar, summary statistics card, refresh button) and the
fetch lifecycle composable so 5f-ii onward can drop in the table
columns and the modals without re-implementing it.
- inbounds.html + src/inbounds.js: fourth Vite entry; mounts InboundsPage.
- InboundsPage.vue: sidebar + summary card (totals over up/down,
all-time, inbound count, client tags) + a basic table with enable/
remark/port/protocol/traffic/expiry columns. Row actions, popovers,
search/filter, auto-refresh, and the WebSocket delta path are all
deferred to subsequent 5f subphases.
- useInbounds.js composable: GET /panel/api/inbounds/list +
POST /panel/api/inbounds/onlines + POST /panel/api/inbounds/lastOnline +
POST /panel/setting/defaultSettings, then computes the
per-inbound clientCount roll-ups (active/deactive/depleted/expiring/
online/comments) the table popovers consume.
- models/dbinbound.js + models/inbound.js: switched the legacy-utils
import to the @/utils alias for consistency with the rest of the
app. Semantics unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The sidebar links to production-style URLs like /panel/settings, but
in dev that gets proxied to the legacy Go template — which fails
because we haven't loaded the legacy asset chain. Add a proxy bypass
so /panel and /panel/settings are served from index.html / settings.html
on the Vite dev server itself. Unmigrated routes (inbounds, xray)
still proxy to Go.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the settings entry as a new Vite multi-page input. Lays down the
shared page chrome (sidebar, save bar, restart, security alert) and the
AllSetting fetch/dirty-poll lifecycle so 5d-ii through 5d-vi can drop
in tab partials without re-implementing it.
- settings.html + src/settings.js: third Vite entry; mounts SettingsPage.
- SettingsPage.vue: page chrome with the legacy two-button save/restart
bar, conf-alerts banner, and 5 a-tabs (4 always-visible + the formats
tab gated on subJsonEnable || subClashEnable). Each tab body is an
a-empty placeholder until 5d-ii…vi fill them in.
- useAllSetting.js composable: POST /panel/setting/all on mount, mirrors
the legacy 1s busy-loop dirty check via setInterval, and exposes
fetchAll/saveAll. saveDisabled flips off as soon as the user diverges
from the server snapshot.
- restartPanel rebuilds the URL (host/port/scheme/base path) from the
saved settings so users land on the new endpoint after a port or
cert change.
- models/setting.js: adopts the @/utils alias and a leading file-level
doc — semantics unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two issues from running login.html against no Go backend:
1. Dark mode toggled the body class but didn't actually re-theme any
AD-Vue components. The legacy panel relied on custom.min.css which
we haven't ported. AD-Vue 4 ships its own dark algorithm — wrap
LoginPage in <a-config-provider :theme="{ algorithm }"> driven by
our useTheme state, and AD-Vue restyles every component for free.
Page chrome (background, card, title) gets explicit .is-dark CSS
since the algorithm only covers AD-Vue components.
2. Vite logged every failed proxy attempt loudly. When the Go panel
isn't running locally that's pure noise. Added a configure()
callback that swallows ECONNREFUSED specifically; real errors
(timeouts, 5xx, anything else) still surface.
Both fixes are dev-experience only — production build is unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The /login proxy entry was matching any path starting with /login —
including /login.html, which Vite is supposed to serve itself. Without
the Go backend running, this caused ECONNREFUSED noise on every page
load.
Switched to regex patterns anchored with ^...$ so only the bare backend
paths (/login, /logout, /getTwoFactorEnable) and explicit sub-routes
(/panel/*, /server/*) get proxied. Static .html files Vite serves
directly are no longer matched.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First real page in the new toolchain. Multi-page Vite: each migrated
page is its own entry. login.html now lives at frontend/login.html with
a thin entrypoint at frontend/src/login.js mounting LoginPage.vue.
Vite 6 → Vite 8.0.11 (per user request). Requires Node 20.19+ or 22.12+.
@vitejs/plugin-vue bumped to ^6.0.6 (peers vite ^8). Ant Design Vue
stays on 4.2.6 — there is no AD-Vue 6.
Vue 2 → Vue 3 / AD-Vue 1 → AD-Vue 4 syntax changes hit on this page:
- new Vue({ el, delimiters, data, methods }) → createApp + <script setup>
- mounted() → onMounted()
- <template slot="X"> → <template #X>
- <a-icon slot="prefix" type="user"> → <template #prefix><UserOutlined />
</template> with explicit @ant-design/icons-vue imports
- v-model.trim → v-model:value (AD-Vue 4 uses named v-model on inputs)
Three legacy features deferred so Phase 4 stays small:
- i18n (Phase 7 wires up vue-i18n)
- theme switcher (custom component pending Phase 5)
- headline word-cycle animation (purely aesthetic)
Run `cd frontend && npm install && npm run dev`, open
http://localhost:5173/login.html. With Go panel running on :2053 the
form submits real credentials via the configured proxy.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a frontend/ directory that lives alongside the legacy web/html/
Vue 2 templates during the migration. Vite builds into ../web/dist/
so the Go binary will be able to embed the result via embed.FS once
Phase 4 starts moving real pages over.
- package.json pins Vue 3.5, Ant Design Vue 4.2, Vite 6, vue-i18n 10
- vite.config.js: dev server on :5173 with API proxy to the Go panel
on :2053; build output to ../web/dist/
- src/App.vue is currently a smoke-test placeholder — delete once the
first real page (login) lands in Phase 4
- node_modules and dist are already ignored at repo root
To verify locally:
cd frontend && npm install && npm run dev
Pages will be migrated one at a time on the vue3-migration branch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>