3x-ui/frontend
MHSanaei 8e7d215b4a
feat(nodes): traffic-writer queue, full-mirror sync, WS event fixes
- Traffic-writer single-consumer queue (web/service/traffic_writer.go)
  serialises every DB write that touches up/down/all_time/last_online
  (AddTraffic, SetRemoteTraffic, Reset*, UpdateClientTrafficByEmail) so
  overlapping goroutines can no longer clobber each other's column-scoped
  Updates with a stale tx.Save.

- DB pool: WAL + busy_timeout=10s + synchronous=NORMAL + _txlock=
  immediate, MaxOpenConns=8 / MaxIdleConns=4. The immediate-tx PRAGMA
  fixes residual "database is locked [0ms]" cases where deferred-tx
  writer-upgrade conflicts bypass busy_timeout.

- SetRemoteTraffic full-mirrors node-authoritative state into central:
  settings JSON, remark, listen, port, total, expiry, all_time, enable,
  plus per-client total/expiry/reset/all_time. Inbounds and
  client_traffics rows present on node but missing from central are
  created; rows missing from snap are deleted (with cascading
  client_traffics removal).

- NodeTrafficSyncJob detects structural changes from the mirror and
  broadcasts invalidate(inbounds) so open central UIs re-fetch via REST
  on node-side add/del/edit without manual refresh.

- XrayTrafficJob broadcasts invalidate(inbounds) when auto-disable flips
  client_traffics.enable so the per-client toggle reflects depletion
  without manual refresh.

- Frontend: inbounds page now subscribes to the BroadcastInbounds 'inbounds'
  WS event (full-list pushes from add/del/update controllers were silently
  dropped). Fixes invalidate payload field (dataType -> type). Restart-
  panel modal switched from Promise-wrap to onOk-only so Cancel actually
  cancels.

- Node files trimmed of stale prose-comments; cron cadence dropped
  10s -> 5s to match the inbounds page UX.

- README badges and Go module path bumped v2 -> v3 to match module rename.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:25:23 +02:00
..
src feat(nodes): traffic-writer queue, full-mirror sync, WS event fixes 2026-05-10 16:25:23 +02:00
.gitignore Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
eslint.config.js Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
inbounds.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
index.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
login.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
nodes.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
package-lock.json Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
package.json Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
README.md Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
settings.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
subpage.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00
vite.config.js fix(panel): make webBasePath work end-to-end in dev and prod 2026-05-09 21:47:37 +02:00
xray.html Vue3 migration (#4198) 2026-05-09 17:47:35 +02:00

3x-ui frontend

Vue 3 + Ant Design Vue 4 + Vite. Multi-page app — one HTML entry per panel route — built into ../web/dist/ and embedded into the Go binary via embed.FS.

Dev

npm install
npm run dev

Vite serves on http://localhost:5173/. API calls and /panel/* routes proxy to the Go panel at http://localhost:2053/, so start the Go panel first (go run main.go) and then Vite.

The proxy auto-rewrites /panel, /panel/settings, /panel/inbounds, /panel/xray to the matching Vite-served HTML in dev mode (see MIGRATED_ROUTES in vite.config.js), so the sidebar's production-style links work without round-tripping through Go.

Production build

npm run build

Outputs to ../web/dist/ (HTML at the root, hashed JS/CSS under assets/). The Go binary embeds this directory at compile time and web/controller/dist.go serves the per-page HTML.

Lint

npm run lint

ESLint 10 with eslint.config.js (flat config) — vue3-recommended plus a few rule overrides for the project's formatting style.

Layout

frontend/
├── *.html                 # Vite entry HTML, one per panel route
├── eslint.config.js
├── vite.config.js
└── src/
    ├── entries/           # Per-page bootstrap (createApp + mount)
    ├── pages/             # One folder per route, each with the page
    │   ├── index/         # component + helpers + sub-components
    │   ├── login/
    │   ├── inbounds/
    │   ├── xray/
    │   ├── settings/
    │   └── sub/
    ├── components/        # Cross-page Vue components
    ├── composables/       # Reusable reactive logic (useTheme, …)
    ├── api/               # Axios setup, CSRF interceptor
    ├── i18n/              # vue-i18n init (locales live in web/translation/)
    ├── models/            # Inbound, Outbound, Status, … domain classes
    └── utils/             # HttpUtil, ObjectUtil, LanguageManager, …

Adding a new page

  1. Add frontend/<page>.html referencing /src/entries/<page>.js.
  2. Add src/entries/<page>.js that imports the page component and mounts it.
  3. Add the page component under src/pages/<page>/.
  4. Register the entry in rollupOptions.input in vite.config.js.
  5. If the page is reachable from the sidebar at /panel/<route>, add it to MIGRATED_ROUTES so the dev proxy serves the Vite HTML.
  6. Wire the Go controller to serveDistPage(c, "<page>.html").