Commit graph

2327 commits

Author SHA1 Message Date
MHSanaei
1faecbe1dd
fix(frontend): anchor Vite dev proxy so /login.html isn't forwarded
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>
2026-05-08 11:55:40 +02:00
MHSanaei
ebe57ef273
feat(frontend): Phase 5b — port four shared components to Vue 3
CustomStatistic.vue and SettingListItem.vue are mechanical
Vue.component → SFC ports.

AppSidebar.vue: AD-Vue 4 dropped <a-icon :type="dynamic">, so the
five sidebar icons (dashboard/user/setting/tool/logout) live in a
name→component map and render via <component :is>. The legacy
<a-drawer slot="handle"> hack is replaced with a sibling fixed-
position toggle button. Tab paths take basePath/requestUri as
props instead of pulling them from Go template scope.

TableSortable.vue: the biggest Vue 3 rewrite of this phase.

  - $listeners is gone — replaced by inheritAttrs: false +
    explicit attrs forwarding
  - scopedSlots: this.$scopedSlots collapsed into Vue 3's unified
    slots object — just iterate Object.keys(this.slots) and forward
  - Vue 2 h(tag, { props, on, scopedSlots }, children) →
    Vue 3 h(tag, { ...props, ...on }, slotsObject)
  - 'a-table' string → resolveComponent('a-table') so app.use(Antd)
    registration is honored
  - inject: ['sortable'] (Options API) → inject('sortable', null)
    (Composition API) inside the trigger child
  - beforeDestroy → beforeUnmount
  - customRow's return shape flattened (no nested props/on/class)

Two intentional skips, documented in the migration doc:

  - aClientTable.html — slot fragments, not a component. Migrates
    inline with inbounds.html (new Phase 5f).
  - aPersianDatepicker.html — wraps a Persian-only third-party
    lib; defer until settings.html lands.

Build verified with vite 8.0.11.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 11:52:52 +02:00
MHSanaei
138696cf36
feat(frontend): Phase 5a — theme system + Vite 8 + vue-i18n 11
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>
2026-05-08 11:11:06 +02:00
MHSanaei
772e778aa0
feat(frontend): Phase 4 — port login.html to Vue 3 + AD-Vue 4 + Vite 8
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>
2026-05-08 11:04:20 +02:00
MHSanaei
3ca644eb3d
refactor(frontend): Phase 3 — port utils, models, axios, websocket as ES modules
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>
2026-05-08 10:47:15 +02:00
MHSanaei
179c025250
build(frontend): Phase 2 — scaffold Vite + Vue 3 + AD-Vue 4
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>
2026-05-08 10:36:03 +02:00
MHSanaei
f874060a4d
docs(migration): Phase 1 inventory — Vue 2 / AD-Vue 1 surface area
Captures the breakage surface for the Vue 3 + Ant Design Vue 4 + Vite
migration: 17,650 lines across 69 templates, 3,145 a-* component
instances across 63 files, with per-pattern counts and file lists.

Key findings:
- No Vue filters anywhere — dodges a major Vue 3 breaking change
- 358 v-model uses; AD-Vue 4 absorbs most, custom components don't
- 233 <template slot="X"> usages must become <template #X>
- 49 scopedSlots: { ... } column defs need new slots: { ... } shape
- a-icon is removed in AD-Vue 4 — every icon must be imported

Establishes the 8-phase order; Phase 2 (Vite toolchain) is next.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:33:24 +02:00
MHSanaei
12c10dbd98
feat(custom-geo): refresh index UI
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
Split the single ext-snippet column into Alias / URL / Routing /
Last-updated, with the alias surfaced next to a colored type tag,
the URL ellipsized with a tooltip + open-in-new-tab, and the
ext:file.dat:tag snippet click-to-copy via ClipboardManager.

Switch Last-updated to a relative time ("2 hours ago") with the
absolute timestamp on hover, add a friendly empty state, and show
a result toast when "Update All" finishes with partial failures.

customGeoEmpty translated for all 13 locales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:09:33 +02:00
MHSanaei
2fd2cd0af1
fix(panel): silence update-check WARN spam when offline
The panel polls api.github.com on every page load. When the host has no
internet (DNS fails, GitHub blocked, etc.) jsonMsg's auto-WARN logging
floods the log with the same error every poll.

Bypass jsonMsg for getPanelUpdateInfo: log the error at Debug level and
return Success:false with the existing localized message so the frontend
popover behavior is unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 09:51:05 +02:00
MHSanaei
37fb48ffff
Axios v1.16.0 2026-05-08 09:41:56 +02:00
MHSanaei
d8198f543b
fix(warp): harden API client and frontend, bump to v0a4005
Backend:
- check HTTP status on every Cloudflare API call so error bodies don't
  get parsed as success
- replace unchecked type assertions with comma-ok form (no more panics
  when Cloudflare returns an error response)
- return real errors when license/id/token fields are missing instead
  of swallowing the failure
- guard SetWarpLicense against an empty errors array
- 15s timeout on the shared http.Client
- build all request bodies and persisted state with json.Marshal
- bump API path to v0a4005 and CF-Client-Version to a-6.30-3596 to
  match the current Cloudflare WARP client

Frontend (warp_modal.html):
- remove stray </a-form-item> closing tag
- declare config/peer with const and null-check before dereferencing
- guard addOutbound/resetOutbound against missing warpOutbound
- rename getResolved -> getReserved (the array it builds is "reserved")

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 09:29:42 +02:00
MHSanaei
f2bc4938b7
Reality: remove tesla.com because of blocking
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
#4175
2026-05-08 00:59:09 +02:00
MHSanaei
7f703f927e
fix(scripts): harden server-IP detection with multi-provider + manual fallback
Try six IPv4 providers in turn, accept only HTTP 200 + IPv4-shaped body,
and prompt the user to enter their IP if every provider fails.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 00:51:28 +02:00
MHSanaei
f2c79b57fa
Bump Go to 1.26.3
Raise module Go version to 1.26.3 and upgrade dependencies including github.com/valyala/fasthttp to v1.71.0 and google.golang.org/genproto/googleapis/rpc to a newer revision. go.sum was updated by module tooling to reflect these changes.
2026-05-08 00:19:05 +02:00
MHSanaei
c394938f01
refactor(websocket): split controller into service + thin controller
Move per-connection lifecycle out of the controller and into a new
service.WebSocketService. The controller is now HTTP-layer only:
authenticate, validate origin, upgrade, and hand the connection off.

- web/service/websocket.go (new): owns the read/write pumps, hub
  registration, and connection lifetime. Pump constants are prefixed
  (wsWriteWait, wsPongWait, wsPingPeriod, wsClientReadLimit) to avoid
  collisions in the larger service package namespace.
- web/controller/websocket.go: trimmed to the upgrader, same-origin
  check, auth gate, and hand-off to the service.
- web/web.go: wires controller.NewWebSocketController(service.NewWebSocketService(hub)).

The hub package (web/websocket) stays as low-level fan-out
infrastructure. Behavior is unchanged — this is a structural cleanup
to align with the rest of the codebase's controller/service split.

Also includes a small range-int modernization in login_limiter_test.go
that gopls flagged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 00:00:44 +02:00
MHSanaei
b84b58ef21
fix(websocket): guard stale events and disconnect race in JS client
Two subtle race conditions in the browser WebSocket client:

1. Stale-event clobber. When connect() is called while the old socket is
   in CLOSING state, the readyState guard falls through and a new socket
   is assigned to this.ws. The old socket's queued close event then
   nulls out this.ws, silently breaking send() until the next reconnect.
   Same risk for delayed open/error/message handlers.

2. Reconnect-after-disconnect. clearTimeout() does not cancel a callback
   that has already fired but whose macrotask has not yet run. If
   disconnect() lands in that window, the queued reconnect callback
   still calls #openSocket() and resurrects the connection.

Every event handler now bails out if this.ws no longer points at the
socket that fired the event, and the reconnect timer callback re-checks
shouldReconnect before opening a new socket.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 00:00:10 +02:00
Farhad H. P. Shirvan
10ebc6cbdc
Implement CSRF protection and security hardening across the application (#4179)
* Implement CSRF protection and security hardening across the application

- Added CSRF token handling in axios requests and HTML templates.
- Introduced CSRF middleware to validate tokens for unsafe HTTP methods.
- Implemented login limiter to prevent brute-force attacks.
- Enhanced security headers in middleware for improved response security.
- Updated login notification to include safe metadata without passwords.
- Added tests for CSRF middleware and login limiter functionality.

* fix
2026-05-07 23:36:11 +02:00
Harry NG
a1b2382877
chore: fix shadowrocketUrl client (#4183) 2026-05-07 20:59:10 +02:00
MHSanaei
59c55dfc92
fix(panel-update): poll for restart, fix dark-mode version label
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 20:55:22 +02:00
MHSanaei
28a3dddb60
refactor(fallbacks): share template, tighter UX, cleaner JSON
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 20:27:34 +02:00
MHSanaei
39bf31bd56
fix(tun): use single mtu number per Xray spec
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 19:50:47 +02:00
MHSanaei
42b2ebc00b
refactor(xhttp): split fields by direction, expand outbound coverage
Audit panel xhttp config against xray-core's runtime paths and split
fields per direction so each side carries only what it actually uses:

- Bidirectional (must match): host, path, mode, all xPadding*,
  session*/seq*, uplinkData*/Key, scMaxEachPostBytes
- Server-only (inbound): noSSEHeader, scMaxBufferedPosts,
  scStreamUpServerSecs, serverMaxHeaderBytes
- Client-only (outbound): uplinkHTTPMethod, uplinkChunkSize,
  noGRPCHeader, scMinPostsIntervalMs, xmux

The inbound previously held client-only fields and the outbound was
missing every must-match field beyond host/path/mode — meaning a
panel-built outbound couldn't connect to an inbound with a custom
xPaddingKey/sessionKey/etc.

Headers stay on the inbound for URL-share purposes only; xray's
listener ignores them at runtime, but they travel through the share
link's `extra` blob so the client picks them up.

Renames the URL helpers (applyXhttpPadding* -> applyXhttpExtra*) since
the blob now carries more than padding, and folds path/host/mode into
the helper so each link generator's xhttp branch is one line.

Adds two enforcement points for xray's "uplinkHTTPMethod=GET only in
packet-up" rule: the GET option is disabled when mode != packet-up,
and a watcher on the outbound modal auto-clears GET when the user
switches modes.

Hides the XMUX block behind an `enableXmux` switch on the outbound
form (mirrors the QUIC Params toggle) so the section doesn't clutter
the form by default; fromJson auto-flips it on for outbounds with
saved xmux config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 19:26:40 +02:00
MHSanaei
3b64a62137
refactor(vless): drop selectedAuth, expose two explicit auth buttons
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
selectedAuth was UI-only metadata (Xray never reads it) and entirely
redundant with the encryption string itself — the dropdown only
controlled which block from `xray vlessenc` to apply. Replace it with
two explicit buttons ("X25519" and "ML-KEM-768") so the user picks
the auth mode in one click instead of dropdown + Get-New-Keys.

- VLESSSettings drops the field from constructor, fromJson, and toJson;
  legacy `selectedAuth` values still in DB will be silently shed on the
  next save.
- getNewVlessEnc(authLabel) now takes the label as a parameter; clear
  resets only decryption/encryption.
- Fallbacks visibility now keys on encryption === "none" (the same
  thing the dropdown was effectively gating on).
- Info modal drops the redundant Authentication tag and colours the
  encryption tag red when it's "none", green otherwise.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 15:08:06 +02:00
MHSanaei
79a7e7a5b5
fix(vless): scope testseed to xtls-rprx-vision flow
testseed is only meaningful for the exact xtls-rprx-vision flow, but the
panel was emitting it for any non-empty flow (including the UDP variant)
and keeping it on the inbound after the flow was cleared via the client
modal. Tighten the gate end-to-end:

- VLESSSettings.toJson (inbound + outbound) now only emits testseed when
  the flow is exactly xtls-rprx-vision and the array is 4 positive ints;
  default state is empty so unmodified inbounds omit the field entirely.
- canEnableVisionSeed drops the udp443 variant per spec.
- Form adds a tooltip + theme-aware help text and an inline error when
  the user partially fills the four inputs; submit is blocked in that
  state. Reset clears to empty (= use server defaults).
- UpdateInboundClient strips a now-orphaned testseed when the spliced
  client no longer leaves any XRV flow in the inbound.
- MigrationRequirements cleans up legacy rows where testseed lingered
  after flow changes or was saved for non-XRV flows by older versions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 14:44:33 +02:00
MHSanaei
3349dcbc13
fix(fail2ban): fix banning regression and Docker zero-jail issue
- DockerEntrypoint.sh: create jail.d/filter.d/action.d config files
  before starting fail2ban so Docker containers no longer start with
  0 active jails (fixes #4134)

- x-ui.sh create_iplimit_jails: lower maxretry from 2 to 1 so
  fail2ban bans on the first log entry; with maxretry=2 and the
  partitionLiveIps logic the second occurrence could arrive after the
  32 s findtime window, silently preventing any ban (fixes #4163)

- x-ui.sh: fix datepattern (%%Y -> %Y) so fail2ban parses the Go
  log timestamp correctly instead of looking for a literal %%Y string

- x-ui.sh / DockerEntrypoint.sh: fix date command in actionban /
  actionunban echo (%%Y -> %Y) so the ban log records actual dates

- check_client_ip_job.go: replace log.SetOutput / log.SetFlags on
  the global standard-library logger with a local log.New instance,
  eliminating the dangling closed-file-handle between calls and
  stopping unrelated stdlib log output from polluting 3xipl.log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 13:53:34 +02:00
MHSanaei
ad30298700
Exclude virtual interfaces from network stats
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
Switch net.IOCounters to per-interface mode and aggregate traffic while excluding loopback and common virtual/tunnel interfaces. Adds isVirtualInterface helper to filter interfaces by exact names and prefixes (docker, veth, tun, wg, tailscale, etc.), sums BytesSent/BytesRecv across valid interfaces, and assigns the totals to status.NetTraffic. Removes the previous warning branch when no counters were found and preserves NetIO rate calculations using lastStatus.
2026-05-06 17:28:41 +02:00
MHSanaei
9be11e109e
fix design 2026-05-06 17:12:08 +02:00
MHSanaei
7117d19fd1
fix: filter view in mobile
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
2026-05-06 14:45:46 +02:00
MHSanaei
c88627a839
outbound: mobile style 2026-05-06 13:27:40 +02:00
MHSanaei
c718e7ca5b
fix(inbounds): remove stale reverse outbound tags after client deletion 2026-05-06 11:43:21 +02:00
pwnnex
6a483fa987
inbound: check transport in port conflict, allow tcp and udp on same port (#4169)
the panel rejected configurations like vless reality on tcp/443 and
hysteria2 on udp/443 even though those are independent sockets in
linux. the old checkPortExist looked only at port + listen.

inboundTransports now classifies each inbound by L4 transport:
hysteria/hysteria2/wireguard are udp; streamSettings.network=kcp is
udp; shadowsocks reads settings.network ("tcp"/"udp"/"tcp,udp");
mixed (socks/http) adds udp when settings.udp is true; everything
else is tcp. checkPortConflict pulls every row on the same port and
only flags a conflict when transport masks overlap. the listen-
overlap rule (specific addr vs any-addr on the same port) is kept.

inbounds.tag has a unique DB constraint and the controller derives
tags from port ("inbound-443"). without disambiguation a second
inbound on the same port would still hit a unique-constraint error.
generateInboundTag keeps the historical "inbound-<port>" shape when
the base tag is free, so existing routing rules survive the upgrade
unchanged, and appends "-tcp"/"-udp" only when the base is already
taken.

closes #4103.
2026-05-06 11:41:21 +02:00
MHSanaei
47163c1418
Skip 26.5.3 and bump Xray version cutoff
In GetXrayVersions, explicitly ignore the tag "26.5.3" and raise the minimum accepted Xray release from 26.3.10 to 26.4.25. This excludes a specific problematic release and updates the version parsing logic to only include >26 or 26.4.25+ releases.
2026-05-06 10:13:55 +02:00
MHSanaei
09f4f09b84
fix design 2026-05-06 10:06:56 +02:00
MHSanaei
3313086071
fix: Swap left/right classes for client table cells
Swap tr-table-rt and tr-table-lt on the size and totalGB elements in aClientTable.html so the size display and the total GB display are positioned correctly (size on the left, total on the right). This is a UI alignment fix with no functional logic changes.
2026-05-06 09:12:25 +02:00
MHSanaei
03d8ad4d5a
Revert "Xray Core v26.5.3" buggy version(vless reverse doesn't work)
This reverts commit 74e97fec4c.
2026-05-06 08:52:36 +02:00
MHSanaei
a8dff126c7
outbound: reverse Sniffing 2026-05-06 08:17:27 +02:00
MHSanaei
74e97fec4c
Xray Core v26.5.3
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
2026-05-06 07:07:48 +02:00
MHSanaei
50603fd430
fix: get client reverse tag in the outbound 2026-05-06 00:50:40 +02:00
MHSanaei
8bea0fde2b
v2.9.4
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
2026-05-05 21:31:24 +02:00
MHSanaei
b2d32f588f
new: vless reverse
legacy reverse removed
2026-05-05 21:00:03 +02:00
lolka1333
8177f6dc66
ws/inbounds: realtime fixes + perf for 10k+ client inbounds (#4123)
* ws/inbounds: realtime fixes + perf for 10k+ client inbounds

- hub: dedup, throttle, panic-restart, deadlock fix, race tests
- client: backoff cap + slow-retry instead of giving up
- broadcast: delta-only payload, count-based invalidate fallback
- filter: fix empty online list (Inbound has no .id, use dbInbound.toInbound)
- perf: O(N²)→O(N) traffic merge, bulk delete, /setEnable endpoint
- traffic: monotonic all_time + UI clamp + propagate in delta handler
- session: persist on update/logout (fixes logout-after-password-change)
- ui: protocol tags flex, traffic bar normalize

* Remove hub_test.go file

* fix: ws hub, inbound service, and frontend correctness

- propagate DelInbound error on disable path in SetInboundEnable
- skip empty emails in updateClientTraffics to avoid constraint violations
- use consistent IN ? clause, drop redundant ErrRecordNotFound guards
- Hub.Unregister: direct removeClient fallback when channel is full
- applyClientStatsDelta: O(1) email lookup via per-inbound Map cache
- WS payload size check: Blob.size instead of .length for real byte count

* fix: chunk large IN ? queries and fix IPv6 same-origin check

* fix: chunk large IN ? queries and fix IPv6 same-origin check

* fix: unify clientStats cache, throttle clarity, hub constants

* fix(ui): align traffic/expiry cell columns across all rows

* style(ui): redesign outbounds table for visual consistency

* style(ui): redesign routing table for visual consistency

* fix:

* fix:

* fix:

* fix:

* fix:

* fix: font

* refactor: simplify outbound tone functions for consistency and maintainability

---------

Co-authored-by: lolka1333 <test123@gmail.com>
2026-05-05 17:27:49 +02:00
MHSanaei
77d94b25d0
Add 'active' filter option to inbounds
Some checks are pending
Release 3X-UI / Analyze Go code (push) Waiting to run
Release 3X-UI / build (386) (push) Blocked by required conditions
Release 3X-UI / build (amd64) (push) Blocked by required conditions
Release 3X-UI / build (arm64) (push) Blocked by required conditions
Release 3X-UI / build (armv5) (push) Blocked by required conditions
Release 3X-UI / build (armv6) (push) Blocked by required conditions
Release 3X-UI / build (armv7) (push) Blocked by required conditions
Release 3X-UI / build (s390x) (push) Blocked by required conditions
Release 3X-UI / Build for Windows (push) Blocked by required conditions
2026-05-04 23:33:48 +02:00
MHSanaei
32b7ada549
subpage: enabled state
Track and surface a subscription's enabled state from backend to frontend so the UI can show inactive subscriptions and use it in active-state logic.

Changes:
- sub/subService.go: track hasEnabledClient, set traffic.Enable, add Enabled to PageData and populate it in BuildPageData.
- sub/subController.go: include enabled in the page context.
- web/html/settings/panel/subscription/subpage.html: emit data-enabled attribute and render an "inactive" tag when disabled.
- web/assets/js/subscription.js: read data-enabled and include it in isActive() checks.

This ensures subscriptions with no enabled clients are marked inactive in the UI and excluded from being considered active.
2026-05-04 23:27:57 +02:00
MHSanaei
6099a07ff0
feat: add configurable auto-restart on client auto-disable
Add a configurable option to restart Xray when clients are auto-disabled and persist disable actions.

Changes include:
- New setting restartXrayOnClientDisable (default true), getters/setters in SettingService, UI toggle in general settings, and translations for multiple locales.
- AddTraffic signature updated to return a third bool (clientsDisabled). disableInvalidClients now calls Xray API to remove users, marks client_traffics.enable=false, updates inbound.Settings JSON so clients appear disabled in stored settings, and returns appropriate counts/errors.
- XrayTrafficJob now checks the clientsDisabled flag and restarts Xray when the setting is enabled (with fallback to mark Xray as needing restart on failure).
- XrayService.GetXrayConfig call adjusted to ignore AddTraffic returns.
- Subscription generation (subService/subJson/subClash) no longer filters clients by their enable flag when matching subId.
- Minor fixes: check_client_ip_job now checks scanner.Err and improved API error handling/logging.

These changes ensure auto-disabled clients are propagated to Xray and the stored inbound settings, and provide an option to restart Xray automatically after auto-disable events.
2026-05-04 23:19:25 +02:00
MHSanaei
e9806832ec
reality: remove apple, icloud 2026-05-04 19:49:28 +02:00
MHSanaei
15ebf3df10
fix: client count for Hysteria
#4143
2026-05-04 17:49:53 +02:00
MHSanaei
d44b70682c
Update QUIC params defaults and UI validations
#4142
Adjust QUIC parameter defaults and tighten form validation across inbound/outbound components.

- Set default brutalUp/brutalDown to 65537 and only include them in JSON when congestion is 'brutal' or 'force-brutal'.
- Change keepAlivePeriod defaults (inbound QUIC -> 5s, Hysteria stream -> 2s) and enforce minimums in the UI.
- Expose and serialize additional QUIC fields in outbound QuicParams: init/max stream windows, init/max connection windows, maxIdleTimeout, disablePathMTUDiscovery, maxIncomingStreams.
- Add UI min/placeholder constraints: stream/connection receive windows min=16384 and updated placeholders to show defaults, brutal fields min=65537, maxIncomingStreams min=8 (placeholders updated), keepAlive min adjusted.
- Add Wireguard and Hysteria entries to Protocols.

Touched files: web/assets/js/model/inbound.js, web/assets/js/model/outbound.js, web/html/form/outbound.html, web/html/form/stream/stream_finalmask.html.
2026-05-04 17:42:55 +02:00
MHSanaei
fb75e3d7c7
Check scanner error in GetXrayLogs
Add a check for scanner.Err() after scanning log lines and return nil if an error occurred. This prevents further processing of potentially incomplete or invalid log entries when the scanner encountered an error.
2026-05-04 17:02:00 +02:00
MHSanaei
e9979b6774
API: Check client existence
#3706
2026-05-04 17:00:09 +02:00
MHSanaei
2b83dc047b
Bump Go module dependency versions 2026-05-04 16:40:50 +02:00