Commit Graph

2376 Commits

Author SHA1 Message Date
Black 194de8869e feat(panel): add 'Edit' button to tables and enhance layout (#4355)
- Move 'Edit' button from dropdown to the table since it's the most used action. Only for desktop.
- Increase column widths for action keys in Inbounds, Balancers, Outbounds and Routing tables.
- Slightly enhance layout for consistency.
2026-05-14 01:55:00 +02:00
MHSanaei 26accfd8f7 fix(qr): lock QR code modules to black-on-white across all themes
AntD's <a-qrcode> defaults the module color to the active theme's
text token. Under the dark and ultra-dark themes that text is a light
gray, so the QR rendered low-contrast on the white canvas background
and phones could not lock onto it. Pinned color="#000000" and
bg-color="#ffffff" on every <a-qrcode> usage (share links in
QrPanel, 2FA enrollment in TwoFactorModal, sub/json/clash codes on
SubPage) so the contrast stays high regardless of panel theme.
2026-05-14 01:45:00 +02:00
MHSanaei 2551a673c3 fix(inbounds): refresh client rows live over websocket
Two bugs combined to leave per-client traffic / remained / all-time
columns stuck at stale numbers while only the inbound-level row and
the online badge refreshed:

1. Backend (xray + node sync traffic jobs) only included the per-client
   array in the client_stats broadcast when activeEmails / touched
   was non-empty. Cycles with no client deltas — or any node sync that
   failed to fetch a snapshot — shipped only the inbound summary, so
   the frontend had nothing to merge for clients. Replaced both code
   paths with a single GetAllClientTraffics() snapshot per cycle; the
   broadcast now always carries the full client list.

2. Frontend mutated dbInbound.clientStats[i] in place. DBInbound is a
   plain class instance (not wrapped in reactive()), so Vue could not
   see the field-level changes and ClientRowTable's statsMap computed
   stayed cached forever. Added a statsVersion tick bumped on every
   merge and read inside statsMap so the computed re-evaluates and the
   template pulls fresh up/down/allTime/expiryTime each push.

Removed the now-dead emailSet helper from node_traffic_sync_job and
the activeEmails filter from xray_traffic_job.
2026-05-14 01:31:49 +02:00
MHSanaei ce4c42e09c feat(json): swap raw textareas for a CodeMirror 6 JsonEditor
A new JsonEditor.vue component wraps CodeMirror 6 + lang-json with
line numbers, JSON syntax highlighting, bracket matching, code
folding, search (Ctrl+F), undo/redo, lint (red squiggle and gutter
icon on invalid JSON), tab indent, and line wrapping. It is wired
into the four raw-JSON spots that previously used <a-textarea
class="json-editor">: the Xray Advanced Template tab, the Outbound
JSON tab, the Balancer Observatory pane, and the Inbound Advanced
tab (settings / streamSettings / sniffing).

Chrome colors are driven by EditorView.theme so they win the
specificity fight cleanly against CodeMirror's own injected styles.
A single buildDarkTheme() factory yields a Dark+ palette (#1e1e1e
background, #252526 active line, #2d2d30 panels) for the regular
dark mode and a near-black variant (#0a0a0a / #141414 / #1f1f1f
border) for ultra-dark — both pair with oneDarkHighlightStyle for
the syntax colors. Light mode stays on basicSetup's default.

CodeMirror lazy-loads as a ~17 kB gzipped chunk that only appears
on the Xray/Inbounds bundles.
2026-05-14 00:02:59 +02:00
MHSanaei 18614bd6ea feat(tabs): collapse settings and xray tab bars to evenly-spread icons
On phones the five Settings tabs and six Xray tabs overflowed the
viewport. Now the tab labels are stripped (v-if="!isMobile"), the
nav-list stretches to full width via display:flex + width:100%, and
each tab claims an equal share with flex:1 1 0 so the icons spread
across the row instead of bunching. Icons bumped to 18px with a
tooltip carrying the original label for discoverability.
2026-05-13 23:33:50 +02:00
MHSanaei e564c9283d feat(nodes): mobile card list, info modal, and tighter summary layout
NodeList now branches on isMobile: a vertical card list mirrors the
inbound mobile redesign — status dot + name + an Info icon that opens
an a-modal with the full per-node stats (address, status, CPU/mem,
xray version, uptime, latency, last heartbeat). The card head expands
to surface NodeHistoryPanel inline (parity with the desktop expandable
row), and the more-dropdown carries probe/edit/delete.

NodesPage also gets two layout fixes: an 8px vertical gutter between
the summary card and the node list on mobile (was 0), and a 2x2 grid
for the four summary statistics on phones via :xs="12" plus a 16px
inner vertical gutter, so Total/Online/Offline/Avg Latency no longer
crowd each other.
2026-05-13 23:14:56 +02:00
MHSanaei 933567d423 feat(inbounds): collapse mobile cards to id/email + info button
Mobile inbound cards now show only #id and remark; mobile client cards
show only the status badge and email. The full stat grid (protocol,
port, node, traffic, all-time, clients, expiry — and per-client
remained/online/expiry) moves behind a new info icon that opens an
a-modal, so the list stays scannable on small screens.
2026-05-13 23:03:19 +02:00
MHSanaei 771bc7c8ef feat(inbounds): align tunnel, tun, and hysteria UI with Xray docs
* tunnel: rename settings to Xray's current schema (address →
  rewriteAddress, port → rewritePort, network → allowedNetwork) in
  the model, form modal, info modal, and the bundled API inbound
  template; expose portMap so per-port forwarding can be configured
  from the panel.
* tun: add the full TUN protocol form and read-only info blocks
  (name, mtu, gateway, dns, userLevel, autoSystemRoutingTable,
  autoOutboundsInterface) — previously the protocol was selectable
  but the form rendered blank.
* hysteria: surface the stream-level version, obfs password, and
  udpIdleTimeout fields that the model already supported.

Refs https://xtls.github.io/config/inbounds/tunnel.html
Refs https://xtls.github.io/config/inbounds/tun.html
Refs https://xtls.github.io/config/transports/hysteria.html
2026-05-13 22:44:08 +02:00
MHSanaei 61ab602887 fix(iplog): parse xray access-log timestamps in local time
Xray writes access-log timestamps in the server's local timezone, but
time.Parse interpreted them as UTC, shifting the stored unix epoch by
the host offset. The panel rendered the epoch back to local time, so
CST users saw IP-log times 8 hours in the future. Parse the log
timestamp with time.ParseInLocation(time.Local) so it round-trips.

Fixes #4147
2026-05-13 21:50:16 +02:00
MHSanaei adc262a238 fix(warp): set license against Cloudflare API and surface errors inline
The license update was always failing because the Cloudflare response has
no `success` field — the check rejected every successful PUT. On real
errors (e.g. "Too many connected devices."), the toast leaked the raw URL
+ JSON body. Now the WARP API's error envelope is parsed into a clean
message and shown inline next to the Update button.
2026-05-13 21:13:16 +02:00
Vladislav Kasperov 67b098dfd3 Add possibility to remove client email from sub (#4297) 2026-05-13 19:04:17 +02:00
MHSanaei 5543466fcc fix(forms): validate JSON tabs before applying or saving
InboundFormModal: switching out of the Advanced tab now parses the three
JSON textareas and rebuilds the structured Inbound via Inbound.fromJson,
so the Basic tab reflects what was pasted. Invalid JSON keeps the user
on Advanced with a specific parse error.

XrayPage: Save now parses xraySetting upfront and snaps the user back to
the Advanced tab on invalid JSON instead of letting the backend reject a
generic blob.
2026-05-13 19:01:12 +02:00
MHSanaei b10a9f1de7 fix(inbounds): hide node UI when no enabled node exists
The Deploy-to selector, node column, node stat row, and node filter all
appeared whenever a node row existed in the DB. Local-only deployments
with no nodes (or only disabled nodes) saw a dropdown that only had
"Local Panel" and a filter that did nothing.

useNodeList now exposes hasActive (any node with enable === true).
Inbounds form and list gate node UI on hasActive instead of map size.
2026-05-13 17:42:40 +02:00
Amirmohammad Sadat Shokouhi 4399fe2a85 add log rotate to 3xui.log file to avoid disk space consumption (#4277)
* add log rotate to 3xui.log file to avoid disk space consumption
2026-05-13 17:03:56 +02:00
MHSanaei 6c6b40e063 fix(outbound): accept JSON-only configs and sync JSON to basic form on tab switch
Pasting a JSON config and clicking OK failed with "Something went wrong"
because validation read the empty form-side tag input instead of the
JSON's tag. Switching from the JSON tab to Basic also discarded any
JSON the user had pasted.

- onOk now validates and submits from the JSON tab using the parsed JSON
- Tab switch JSON→Basic deserializes the JSON back into the structured form
- Invalid JSON keeps the user on the JSON tab with a clear parse error
- Empty form-tag / duplicate-tag errors are now specific, not generic
2026-05-13 16:48:16 +02:00
MHSanaei b97ff40ad6 feat(api-tokens): manage multiple named tokens; add tab/section anchor URLs
Replace the single regenerable API token with a named-token list:
- New ApiToken model + service with constant-time auth matching
- Seeder migrates the legacy `apiToken` setting into a "default" row
- Security tab gets create/enable/delete UI; api-docs page links to it
- Dedicated "API Tokens" section in the in-panel docs

URL anchors now reflect the active tab/section on Settings, Xray, and
API Docs pages, so deep links like `/panel/settings#security` work.

Translations for the 8 new SecurityTab strings added across all locales.
2026-05-13 16:34:31 +02:00
MHSanaei 46b6f8c66c feat(routing): drag-reorder rules, split balancer column, mobile card layout
- Grip-handle drag-and-drop on the # cell to reorder rules, built on
  Pointer Events so the same code works for mouse, touch, and pen
  (HTML5 drag doesn't fire from touch on iOS Safari). 5px threshold
  keeps quick taps from triggering a reorder; up/down arrow menu
  items stay as a keyboard/a11y fallback. Drop indicator is a 2px
  blue line on the target edge; dragged row fades to 40%.
- Split the old combined target column into Outbounds and Balancer
  columns. Each row now has exactly one populated cell — green
  outbound tag or purple balancer tag.
- Mobile drops the a-table (520px+ of column widths overflowed every
  phone) for a stacked card layout: # + grip + actions on top, an
  "Inbound → Outbound/Balancer" flow row in the middle, and criteria
  chips (domain, IP, port, src IP/port, L4, protocol, user, VLESS)
  below for whichever fields are actually set. Multi-value chips
  collapse to "first +N" with full value on hover.
2026-05-13 15:30:25 +02:00
Abdalrahman 102df7a290 style(api-docs): redesign TOC, section icons, endpoint rows, and code blocks with ultra-dark support (#4332)
* style(api-docs): redesign TOC, section icons, endpoint rows, and code blocks with ultra-dark support

* style(api-docs): rename visibleSections to visibleEndpoints, drop dead toc-stuck CSS

- visibleSections counted endpoints, not sections — rename matches
  the displayed "X / Y endpoints" label.
- .toc-nav.toc-stuck was never toggled by any code path.

* docs(api): add missing POST /panel/api/inbounds/:id/resetTraffic entry

This route was added in #4334/#4338 but endpoints.js wasn't updated,
breaking TestAPIRoutesDocumented (91 routes in source, 90 documented).

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-05-13 15:05:23 +02:00
Abdalrahman f29c8a5e29 fix: single inbound traffic reset resets all inbounds (#4334) (#4338) 2026-05-13 14:49:54 +02:00
Abdalrahman ad81649c16 fix: strip main-panel TLS cert file paths when sending inbound to remote node (#4339)
When the main panel creates an inbound assigned to a remote node,
the wireInbound helper sends StreamSettings as-is, including
certificateFile/keyFile paths that only exist on the main panel's
filesystem. The remote node's Xray then fails to load them and crashes.

This adds sanitizeStreamSettingsForRemote() which strips file-based
cert paths before forwarding to a remote node. Inline certificate
content (certificate/key) is preserved unchanged.

Closes #4335
2026-05-13 14:47:09 +02:00
Abdalrahman b47f794ed0 fix: reality random target/sni buttons not working (#4337) (#4340) 2026-05-13 14:42:20 +02:00
MHSanaei 4e1b597914 feat(ui): use the host as the browser tab title prefix 2026-05-13 14:23:57 +02:00
MHSanaei bbefe91011 fix(auth): invalidate sessions when 2FA is enabled, fix dev 401 loop
Add UserService.BumpLoginEpoch and call it from updateSetting when
TwoFactorEnable flips false → true. Existing cookies (issued under
the looser no-2FA policy) get a 401 on their next request and are
forced through the login flow. Disabling 2FA is a relaxation and
does not bump the epoch — sessions stay valid.

Also fix the dev-mode 401 redirect: targeting `${basePath}login.html`
breaks when basePath isn't "/" (Vite has no file at e.g.
"/test/login.html"; the SPA fallback loops the 401). Navigate to
basePath instead — Vite's bypassMigratedRoute and Go's index
handler both serve login.html for that path.

Strip stale doc-comment from netsafe and IndexController.logout
in line with the project's no-inline-comments convention.
2026-05-13 14:08:16 +02:00
MHSanaei e40554a7d5 fix(inbound): require email when adding or updating a client
AddInboundClient and UpdateInboundClient previously accepted an
empty Email field for every protocol except shadowsocks (where
email doubles as the client ID). Empty emails break downstream
features that key off email — IP-limit logging, traffic stats,
client-online tracking, subscription remarks.

Reject empty/whitespace-only emails at the service layer so the
API surface (POST /panel/api/inbounds/addClient and
/updateClient/:id) returns a clear error instead of persisting
an unidentifiable client.

Also drop the stale `len(Email) > 0` guard in UpdateInboundClient
that became dead code once empty emails are rejected.
2026-05-13 13:45:31 +02:00
MHSanaei 3569b1be73 ci(codeql): run on push to main 2026-05-13 13:39:32 +02:00
MHSanaei 38da210ded fix(security): SSRF-guard node and remote HTTP clients
The Node.Probe and Remote.do paths built outbound URLs by string-
formatting admin-controlled fields (Scheme/Address/Port/BasePath)
straight into requests, then dialed the result with the default
transport. CodeQL flagged this as go/request-forgery — an admin
(or anyone who compromises the admin account) could point a node
at internal infrastructure (cloud metadata, RFC1918 ranges, etc.)
and the panel would dutifully fetch it.

Add util/netsafe with a shared TOCTOU-safe DialContext that
resolves the host, rejects private/internal IPs unless the
per-request context whitelists them (per-node AllowPrivateAddress
flag, plumbed through context.Value), and dials the resolved IP
directly so the IP that passed the check is the IP we connect to.
This closes the DNS-rebinding window where a hostname could
resolve to a public IP at check time and a private one at dial.

Also tighten address validation (NormalizeHost rejects anything
that isn't a bare hostname or IP literal — no embedded paths,
userinfo, schemes) and switch URL construction from fmt.Sprintf to
url.URL{} + net.JoinHostPort so admin-supplied values can't smuggle
URL components.

custom_geo.go's isBlockedIP now delegates to netsafe so there's
one source of truth.
2026-05-13 13:33:53 +02:00
MHSanaei 9fc47b3d41 ci: gate workflows on relevant source paths
- ci.yml: only run on Go/frontend source and lockfiles.
- codeql.yml: scope push/PR triggers to Go and JS/TS sources;
  weekly cron still does a full scan.
- release.yml: add matching paths allowlist to pull_request so
  doc/workflow-only PRs don't kick off the multi-arch build.

Skips workflow runs on changes to docs, translations, GitHub
configs, and unrelated scripts.
2026-05-13 13:21:26 +02:00
MHSanaei 210c25cf13 Bump Go module dependency versions
Routine update of Go module dependencies and tidy: bump indirect deps (github.com/quic-go/quic-go v0.59.0→v0.59.1, github.com/sagernet/sing v0.8.9→v0.8.10, github.com/tklauser/go-sysconf v0.3.16→v0.4.0, github.com/tklauser/numcpus v0.11.0→v0.12.0), and update several golang.org/x modules (arch, exp, mod, net, tools) and google.golang.org/genproto. Removed duplicate require entries (x/crypto, x/sys, x/text) and updated go.sum to match the new versions.
2026-05-13 13:04:44 +02:00
dependabot[bot] 5dd7e44594 build(deps): bump golang.org/x/text from 0.36.0 to 0.37.0 (#4345)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.36.0 to 0.37.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-05-13 13:00:02 +02:00
dependabot[bot] 4e4a8e9ff7 build(deps): bump golang.org/x/crypto from 0.50.0 to 0.51.0 (#4344)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.50.0 to 0.51.0.
- [Commits](https://github.com/golang/crypto/compare/v0.50.0...v0.51.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-05-13 12:58:42 +02:00
dependabot[bot] 23970e72a7 build(deps): bump golang.org/x/sys from 0.43.0 to 0.44.0 (#4343)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.43.0 to 0.44.0.
- [Commits](https://github.com/golang/sys/compare/v0.43.0...v0.44.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-13 12:57:07 +02:00
dependabot[bot] 8bdb093d6e build(deps): bump actions/setup-node from 5 to 6 (#4342)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-05-13 12:56:51 +02:00
dependabot[bot] 3b0bcb910e build(deps): bump actions/checkout from 5 to 6 (#4341)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-13 12:56:08 +02:00
Farhad H. P. Shirvan 428f1333ac Security hardening: sessions, SSRF, CSP nonce, CSRF logout, trusted proxies (#4275)
* refactor(session): store user ID in session instead of full struct

Replaces storing the full User object in the session cookie with just
the user ID. GetLoginUser now re-fetches the user from the database on
every request so credential/permission changes take effect immediately
without requiring a re-login. Includes a backward-compatible migration
path for existing sessions that still carry the old struct payload.

* feat(auth): block panel with default admin/admin credentials and guide credential change

checkLogin middleware now detects default admin/admin credentials and
redirects every panel route to /panel/settings until they are changed.
The settings page auto-opens the Authentication tab, shows a
non-dismissible error banner, and lists 'Default credentials' first in
the security checklist. Login response includes mustChangeCredentials
so the login page can redirect directly. Logout is now POST-only.
Password must be at least 10 characters and cannot be admin/admin.

* feat(settings): redact secrets in AllSettingView and add TrustedProxyCIDRs

Introduces AllSettingView which strips tgBotToken, twoFactorToken,
ldapPassword, apiToken and warp/nord secrets before sending them to
the browser, replacing them with boolean hasFoo presence flags. A new
/panel/setting/secret endpoint allows updating individual secrets by
key. Secrets that arrive blank on a save are preserved from the DB
rather than overwritten. Adds TrustedProxyCIDRs as a configurable
setting (defaults to localhost CIDRs). URL fields are validated before
save.

* fix(security): SSRF prevention, trusted-proxy header gating, CSP nonce, HTTP timeouts

Adds SanitizeHTTPURL / SanitizePublicHTTPURL to reject private-range
and loopback targets before any outbound HTTP request (node probe,
xray download, outbound test, external traffic inform, tgbot API
server, panel updater). Forwarded headers (X-Real-IP, X-Forwarded-For,
X-Forwarded-Host) are now only trusted when the direct connection
arrives from a CIDR in TrustedProxyCIDRs. CSP policy is tightened with
a per-request nonce. HTTP server gains read/write/idle timeouts. Panel
updater downloads the script to a temp file instead of piping curl into
shell. Xray archive download adds a size cap and response-code check.
backuptotgbot is changed from GET to POST.

* feat(nodes): add allow-private-address toggle per node

Adds AllowPrivateAddress to the Node model (DB default false). When
enabled it bypasses the SSRF private-range check for that node's probe
URL, allowing nodes hosted on RFC-1918 or loopback addresses (e.g.
a private VPN or LAN setup).

* chore: frontend UX improvements, CI pipeline, and dev tooling

- AppSidebar: logout via POST /logout instead of navigating to GET
- InboundList: persist filter state (search, protocol, node) to
  localStorage across page reloads; add protocol and node filter dropdowns
- IndexPage: add health status strip (Xray, CPU, Memory, Update) with
  quick-action buttons
- dependabot: weekly go mod and npm update schedule
- ci.yml: add GitHub Actions workflow for build and vet
- .nvmrc: pin Node 22 for local development
- frontend: bump package.json and package-lock.json
- SubPage, DnsPresetsModal, api-docs: minor fixes

* fix(ci): stub web/dist before go list to satisfy go:embed at compile time

* chore(ui): remove health-strip bar from dashboard top

* Revert "feat(auth): block panel with default admin/admin credentials and guide credential change"

This reverts commit 56ce6073ce09f08147f989858e0e88b3a4359546.

* fix(auth): make logout POST+CSRF and propagate session loss to other tabs

- Switch /logout from GET to POST with CSRFMiddleware so it matches the
  SPA's existing HttpUtil.post('/logout') call (previously 404'd silently)
  and blocks GET-based logout via image tags or link prefetchers. Handler
  now returns JSON; the SPA already navigates client-side.
- Return 401 (instead of 404) from /panel/api/* when the caller is a
  browser XHR (X-Requested-With: XMLHttpRequest) so the axios interceptor
  redirects to the login page on logout-in-another-tab, cookie expiry,
  and server restart. Anonymous callers still get 404 to keep endpoints
  hidden from casual scanners.
- One-shot the 401 redirect in axios-init.js and hang the rejected
  promise so queued polls don't stack reloads or surface error toasts
  while the browser is navigating away.
- Add the CSP nonce to the runtime-injected <script> in dist.go so the
  panel loads under the existing script-src 'nonce-...' policy.
- Update api-docs endpoints.js: GET /logout doc entry was missing.

* fix(settings): POST /logout after credential change

* fix(auth): invalidate other sessions when credentials change

When the admin changes username/password from one machine, sessions
on every other machine kept working until they manually logged out
because session storage is a signed client-side cookie — there is
no server-side session list to revoke.

Add a per-user LoginEpoch counter stamped into the session at login
and re-verified on every authenticated request. UpdateUser and
UpdateFirstUser bump the epoch (UpdateUser via gorm.Expr so a single
update statement is atomic), so any cookie issued before the change
no longer matches the user's current epoch and GetLoginUser returns
nil — the SPA's 401 interceptor then redirects to the login page.

Backward compatible: the column defaults to 0 and missing cookie
values are treated as 0, so sessions issued before this change
remain valid until the first credential update.

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-05-13 12:52:52 +02:00
MHSanaei 406cb6dbc0 fix(api-docs): resolve no-useless-escape lint errors
- endpoints.js: replace `\"` with `\\"` in xray response example so the
  rendered docs actually show escaped JSON-in-JSON (the original
  single-quoted `\"` collapsed to a bare `"` and produced malformed output).
- CodeBlock.vue: drop the unnecessary `\[` inside the regex character
  class `[{}\[\]]`; `[` does not need escaping inside `[...]`.
2026-05-13 11:31:34 +02:00
Aleksandr 5fb36d34c9 fix(fail2ban): escape percent signs in 3x-ipl datepattern (#4328)
* Update DockerEntrypoint.sh

fix(fail2ban): escape percent signs in Docker datepattern

* Update x-ui.sh

fix(fail2ban): escape percent signs in x-ui datepattern
2026-05-13 01:49:09 +02:00
Abdalrahman 4884a2972a fix(graphs): increase y-axis paddingLeft from 32 to 56 to prevent clipped labels (#4309) 2026-05-13 01:47:54 +02:00
Abdalrahman 6e12329d9d feat(api-docs): enhance in-panel API documentation (#4312)
* feat(api-docs): enhance API documentation with missing endpoints, search, collapse, and route sync test

- Add 29 undocumented routes across 4 new sections (Settings, Xray Settings,
  Subscription Server, WebSocket) plus 4 missing Server API endpoints
- Fix inaccuracies: history metric keys, node metric keys, VLESS enc description
- Add response schemas to 15+ key endpoints
- Add search bar and expand/collapse all controls to the docs page
- Add collapsible endpoint sections with endpoint count
- Add Go test (TestAPIRoutesDocumented) to verify all Go routes are documented

* feat(api-docs): add JSON syntax highlighting and top-right copy button to code blocks

* fix(api-docs): use distinct colors for JSON syntax highlighting (green strings, amber numbers)

* feat(api-docs): add request body examples, error responses, WebSocket message types, and subscription response headers

* fix(api-docs): use ClipboardManager.copyText instead of copy to fix API token copy button
2026-05-13 01:47:09 +02:00
Abdalrahman 9f7e8178d4 fix: delete button missing after searching for a user (#4315)
When searching for a user, the projected DBInbound only contains the
matching clients, so isRemovable evaluated to alse (since a single
match made clients.value.length === 1), hiding the Delete button.

Pass the original total client count from the parent's clientCount
prop and use it in the isRemovable check instead of the projected
clients array length.
2026-05-13 01:27:10 +02:00
Abdalrahman 60e6b12f4c fix(hysteria2): restore missing masquerade config in inbound form (#4316)
* fix(hysteria2): restore missing masquerade config in inbound form

Fixes #4303

The Hysteria2 Masquerade option was missing from the Stream settings
tab after the v3.0.0 form rewrite. Added the UI form and ensured the
masquerade block is passed through in subscription JSON generation.
2026-05-13 01:25:00 +02:00
Abdalrahman 0dbadf82c0 fix: auto-renew must re-enable client in inbound settings JSON (#4317)
Since v2.9.4, disableInvalidClients sets c['enable']=false in inbound settings JSON when a client hits its limit. autoRenewClients only updated client_traffics.enable - never flipped the JSON field back. The Xray config generator checks both, so client stayed excluded after renewal.
2026-05-13 01:15:52 +02:00
Abdalrahman 48e90bba51 fix: show UDP tag for Hysteria and fix client count spacing (#4318) 2026-05-13 01:12:25 +02:00
Abdalrahman 6de9b24229 fix: preserve space between date and time in log modal (#4326)
Vue 3's whitespace: condense strips bare whitespace text nodes and
trailing whitespace inside elements, causing the &lt;template&gt; trick
to fail. Use mustache interpolations (which compile to _createTextVNode)
for all spacing between fields so they survive compilation.
2026-05-13 01:02:48 +02:00
MHSanaei 07bc74a521 feat(nodes): blur address column with eye-toggle, mirroring IndexPage IP card 2026-05-12 12:38:38 +02:00
MHSanaei f570b991e7 fix(api-docs): copy API token button 2026-05-12 12:34:22 +02:00
MHSanaei 80031e67cc feat(inbounds): restore copy-clients-between-inbounds modal
The menu item, backend endpoint (POST /panel/api/inbounds/:id/copyClients),
and i18n keys were already in place after the Vue3 migration, but the modal
itself was never ported — clicking the menu just toasted "coming soon".

Adds CopyClientsModal.vue: source inbound dropdown (multi-user inbounds
except the target), per-client checkbox selection via a-table row-selection,
optional Flow override when the target supports TLS flow, and result toasts
for added/skipped/errors.
2026-05-12 12:30:07 +02:00
Farhad H. P. Shirvan fdaa65ad7e Feat: clarify VLESS encryption auth selection (#4271)
* feat(traffic_writer): enhance traffic writer with concurrency safety and state management

* Revert "feat(traffic_writer): enhance traffic writer with concurrency safety and state management"

This reverts commit e6760ae39629a592dec293197768f27ff0f5a578.

* feat(vless): clarify VLESS encryption auth selection and enhance parsing logic
2026-05-12 11:39:28 +02:00
Farhad H. P. Shirvan d86e87ed30 Fix: traffic writer restart freeze (#4265)
* feat(traffic_writer): enhance traffic writer with concurrency safety and state management

* Revert "feat(traffic_writer): enhance traffic writer with concurrency safety and state management"

This reverts commit e6760ae39629a592dec293197768f27ff0f5a578.

* feat(traffic_writer): enhance traffic writer with concurrency safety and state management

* feat(web): implement panel-only start/stop methods for in-process restarts
2026-05-12 11:36:05 +02:00
Abdalrahman 89a8f549f2 feat: sortable inbounds table columns (#4300) 2026-05-12 11:29:32 +02:00
MHSanaei 355bb4c9c0 feat(panel): xray metrics dashboard with observatory probe history
Polls xray's /debug/vars on the 2s status tick, stores memstats and per-outbound observatory delay in the metric history ring buffer, and exposes them through a new XrayMetricsModal opened from the Charts card. Restructures the dashboard to consolidate uptime, usage, version, and Telegram link into stat-style or action-style cards consistent with the existing AntD aesthetic.
2026-05-12 02:17:45 +02:00