Commit Graph

366 Commits

Author SHA1 Message Date
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
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
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
ad30298700 Exclude virtual interfaces from network stats
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
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
50603fd430 fix: get client reverse tag in the outbound 2026-05-06 00:50:40 +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
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
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
Farhad H. P. Shirvan
f21ed92296 feat: add panel update functionality via web GUI (#4117)
* feat: add panel update functionality via web GUI

* feat: enhance panel update notifications in web GUI

* feat: implement panel update modal and enhance translation strings

* fix design
2026-04-28 18:46:55 +02:00
pwnnex
22de983752 xray-setting: pin api routing rule to index 0 on save (#4124)
when the admin adds a custom outbound (eg vless cascade to a second
server) and a routing rule sending all inbound traffic to it, that
catch-all gets evaluated before the existing api->api rule, so the
panel's internal stats inbound's traffic ends up on the cascade
outbound. the grpc stats query then can't see anything, GetTraffic
returns no inbound/user counters, and every client appears offline
with zero traffic even though the actual proxy path works fine.

before save, find the api rule and move it to the front of
routing.rules. if it's missing entirely, insert a default. other
rules keep their relative order.

closes #4113. probably also fixes the long-standing #2818 where the
documented workaround was "manually move the api rule to the top".
2026-04-28 17:49:39 +02:00
MHSanaei
b56db67759 fix: handle Init error in GetXrayTraffic to prevent nil pointer panic
#3969
2026-04-27 14:11:28 +02:00
MHSanaei
a62c637632 DNS outbound: Add rules 2026-04-26 17:34:31 +02:00
Rs.Nest
6bcaf61c44 Feature: Copy clients between inbounds (#4087)
* feat: copy clients between inbounds

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* revert: undo install.sh/deploy.sh changes; i18n: add copy-clients translations for all languages

---------

Co-authored-by: Нестеров Руслан <r.nesterov@comagic.dev>
2026-04-23 15:19:07 +02:00
pwnnex
eb4791a1cd hysteria: also accept "hysteria2" protocol string
UI stores v1 and v2 both as "hysteria" with settings.version, but
inbounds that came in from imports / manual SQL can carry the
literal "hysteria2" string and get silently dropped everywhere we
switch on protocol.

Add Hysteria2 constant + IsHysteria helper, use it in the places
that gate on protocol (sub SQL, getLink, genHysteriaLink, clash
buildProxy, json gen, inbound.go validation, xray AddUser).

Existing "hysteria" inbounds are untouched.

closes #4081
2026-04-22 18:55:09 +03:00
MHSanaei
8f30d14716 Extract bot command setup into trySetBotCommands 2026-04-22 10:47:30 +02:00
MHSanaei
814e6ad69c Lower minimum Xray version
Update GetXrayVersions filter to accept Xray releases >= 26.3.10 instead of the previous >= 26.4.17. This changes the conditional in web/service/server.go so releases from 26.3.10 onward are included when building the versions list.
2026-04-21 21:20:59 +02:00
pwnnex
15be803da9 Fix blank Xray Settings page from wrapped xrayTemplateConfig (#4059) (#4069)
`getXraySetting` builds its response as

    { "xraySetting": <db value>, "inboundTags": ..., "outboundTestUrl": ... }

and embeds the raw DB value as the `xraySetting` field without
checking whether the stored value already has that exact shape.

The frontend pulls the textarea content from `result.xraySetting`
and saves it back verbatim. If the DB ever ends up holding the
response-shaped wrapper instead of a real xray config (older
installs where this happened at least once, users who imported a
copy-pasted response into the textarea, a botched migration, etc.),
the next save nests another layer, the one after that nests a
third, and the Vue-side JSON.parse of the resulting blob silently
fails — the Xray Settings page goes blank.

Fix both ends of the round-trip:

* Add `service.UnwrapXrayTemplateConfig`. It peels off any number of
  `xraySetting`-keyed layers, leaving a real xray config behind.
  The check is conservative: if the outer object already contains
  any top-level xray key (`inbounds`, `outbounds`, `routing`, `api`,
  `dns`, `log`, `policy`, `stats`), it is returned unchanged, and
  there is a depth cap to avoid pathological inputs.

* `SaveXraySetting` unwraps before validation so a round-tripped
  wrapper from an already-corrupted page can no longer re-poison
  the DB on save.

* `getXraySetting` unwraps on read and, when it finds a wrapper,
  rewrites the DB with the corrected value. Existing broken installs
  heal themselves on the next visit to the page.

Includes unit tests for the passthrough, single-wrap, multi-wrap,
string-encoded-inner, and false-positive cases.

Co-authored-by: pwnnex <eternxles@gmail.com>
2026-04-21 20:30:02 +02:00
MHSanaei
86a8eb16b4 fix timelocation for windows
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-21 20:05:52 +02:00
MHSanaei
8d512d55e5 revert Fix geosite:ru rule (ram leak)
#4050 #4055
2026-04-21 12:55:16 +02:00
MHSanaei
ca2fd3814f Bump Xray version cutoff to 26.4.17
Update GetXrayVersions in web/service/server.go to select releases newer than 26.4.17 (previously 26.2.6). This relaxes the version filter so releases >= 26.4.17 are included.
2026-04-20 20:03:27 +02:00
MHSanaei
9f0055d193 bug fix 2026-04-20 19:46:21 +02:00
MHSanaei
ae5ad505d0 add hysteria inbound
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-20 16:05:27 +02:00
Peter Liu
36b2a58675 feat: Add NordVPN NordLynx (WireGuard) integration (#3827)
* feat: Add NordVPN NordLynx (WireGuard) integration with dedicated UI and backend services.

* remove limit=10 to get all servers

* feat: add city selector to NordVPN modal

* feat: auto-select best server on country/city change

* feat: simplify filter logic and enforce > 7% load

* fix

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-04-20 00:41:50 +02:00
Sanaei
ea53da9341 Add SSRF protection (#4044)
* Add SSRF protection for custom geo downloads

Introduce SSRF-safe HTTP transport for custom geo operations by adding ssrfSafeTransport and isBlockedIP helpers. The transport resolves hosts and blocks loopback, private, link-local and unspecified addresses, returning ErrCustomGeoSSRFBlocked on violations. Update probeCustomGeoURLWithGET, probeCustomGeoURL and downloadToPathOnce to use the safe transport. Also add the new error ErrCustomGeoSSRFBlocked and necessary imports. Minor whitespace/formatting adjustments in subClashService.go, web/entity/entity.go and web/service/setting.go.

* Add path traversal protection for custom geo

Prevent path traversal when handling custom geo downloads by adding ErrCustomGeoPathTraversal and a validateDestPath() helper that ensures destination paths stay inside the bin folder. Call validateDestPath from downloadToPathOnce, Update and Delete paths and wrap errors appropriately. Reconstruct sanitized URLs in sanitizeURL to break taint propagation before use. Map the new path-traversal error to a user-facing i18n message in the controller.

* fix
2026-04-20 00:18:20 +02:00
zhuzn
d580086361 feat add clash yaml convert (#3916)
* docs(agents): add AI agent guidance documentation

* feat(sub): add Clash/Mihomo YAML subscription service

Add SubClashService to convert subscription links to Clash/Mihomo
YAML format for direct client compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(sub): integrate Clash YAML endpoint into subscription system

- Add Clash route handler in SUBController
- Update BuildURLs to include Clash URL
- Pass Clash settings through subscription pipeline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(web): add Clash settings to entity and service

- Add SubClashEnable, SubClashPath, SubClashURI fields
- Add getter methods for Clash configuration
- Set default Clash path to /clash/ and enable by default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ui): add Clash settings to subscription panels

- Add Clash enable switch in general subscription settings
- Add Clash path/URI configuration in formats panel
- Display Clash QR code on subscription page
- Rename JSON tab to "Formats" for clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(js): add Clash support to frontend models

- Add subClashEnable, subClashPath, subClashURI to AllSetting
- Generate and display Clash QR code on subscription page
- Handle Clash URL in subscription data binding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-04-19 22:26:13 +02:00
Troodi
c2a2a36f56 Fix geosite:ru rule (Normalization to RU vs lowercase ru) (#3971)
* Fix geosite:ru rule (Normalization to RU vs lowercase ru)

* fix
2026-04-19 21:44:51 +02:00
Andrew Smirnov
e986a133f8 Add new hourly reset traffic (#3966)
* Add new hourly reset traffic

* fix
2026-04-19 21:37:34 +02:00
Vladislav Tupikin
7466916e02 Add custom geosite/geoip URL sources (#3980)
* feat: add custom geosite/geoip URL sources

Register DB model, panel API, index/xray UI, and i18n.

* fix
2026-04-19 21:24:24 +02:00
lolka1333
fec714a243 fix: enhance WebSocket stability, resolve XHTTP configurations and fix UI loading shifts (#3997)
* feat: implement real-time traffic monitoring and UI updates using a high-performance WebSocket hub and background job system

* feat: add bulk client management support and improve inbound data handling

* Fix bug

* **Fixes & Changes:**
1. **Fixed XPadding Placement Dropdown**:
   - Added the missing `cookie` and `query` options to `xPaddingPlacement` (`stream_xhttp.html`).
   - *Why:* Previously, users wanting `cookie` obfuscation were forced to use the `header` placement string. This caused Xray-core to blindly intercept the entire monolithic HTTP Cookie header, failing internal padding-length validations and causing the inbound to silently drop the connection.
2. **Fixed Uplink Data Placement Validation**:
   - Replaced the unsupported `query` option with `cookie` in `uplinkDataPlacement`.
   - *Why:* Xray-core's `transport_internet.go` explicitly forbids `query` as an uplink placement option. Selecting it from the UI previously sent a payload that would cause Xray-core to instantly throw an `unsupported uplink data placement: query` panic. Adding `cookie` perfectly aligns the UI with Xray-core restrictions.
### Related Issues
- Resolves #3992

* This commit fixes structural payload issues preventing XHTTP from functioning correctly and eliminates WebSocket log spam.
- **[Fix X-Padding UI]** Added missing `cookie` and `query` options to X-Padding Placement. Fixes the issue where using Cookie fallback triggers whole HTTP Cookie header interception and silent drop in Xray-core. (Resolves [#3992](https://github.com/MHSanaei/3x-ui/issues/3992))
- **[Fix Uplink Data Options]** Replaced the invalid `query` option with `cookie` in Uplink Data Placement dropdown to prevent Xray-core backend panic `unsupported uplink data placement: query`.
- **[Fix WebSockets Spam]** Boosted `maxMessageSize` boundary to 100MB and gracefully handled fallback fetch signals via `broadcastInvalidate` to avoid buffer dropping spam. (Resolves [#3984](https://github.com/MHSanaei/3x-ui/issues/3984))

* Fix

* gofmt

* fix(websocket): resolve channel race condition and graceful shutdown deadlock

* Fix: inbounds switch

* Change max quantity from 10000 to 500

* fix
2026-04-19 21:01:00 +02:00
MHSanaei
f0f98c7122 Add Go code analyzer workflow 2026-03-17 23:01:15 +01:00
Abdalrahman
554981d9d3 feat(tgbot): send connection links and qrs on client creation (closes #3320)\n\n- Refactored inline keyboards into getCommonClientButtons to respect DRY\n- Extended SubmitAddClient callback handlers to dispatch individual links and QR codes to the bot chat on success. (#3888) 2026-03-17 22:09:49 +01:00
Alimpo
7f7ae0c547 fix: stop overwriting client_traffics.enable with JSON enable in GetClientTrafficByEmail (#3931)
When a client hit traffic/expiry limit, disableInvalidClients sets
client_traffics.enable=false and removes the user from Xray. GetClientTrafficByEmail
was overwriting that with settings.clients[].enable (admin config), so
ResetClientTraffic never saw the client as disabled and did not re-add
the user. Clients could not connect until manually disabled/re-enabled.
Now the DB runtime enable flag is preserved; reset correctly re-adds
the user to Xray.
2026-03-17 21:20:24 +01:00
Aleksei Sidorenko
a2097ad062 feat: mask password in telegram notification on 2FA failure (#3884) 2026-03-04 18:26:53 +01:00
MHSanaei
5740996436 update dependencies 2026-03-04 13:05:29 +01:00
Aleksei Sidorenko
96b8fe472c Fix: escape HTML characters in tgbot start command (#3883) 2026-03-04 11:35:24 +01:00
MHSanaei
5b796672e9 Improve telego client robustness and retries
Add a createRobustFastHTTPClient helper to configure fasthttp.Client with better timeouts, connection limits, retries and optional SOCKS5 proxy dialing. Validate and sanitize proxy and API server URLs instead of returning early on invalid values, and build telego.Bot options dynamically. Reduce long-polling timeout to detect connection issues faster and adjust update retrieval comments. Implement exponential-backoff retry logic for SendMessage calls to handle transient connection/timeouts and improve delivery reliability; also reduce inter-message delay for better throughput.
2026-02-14 22:49:19 +01:00
MHSanaei
3fa0da38c9 Add timeouts and delays to backup sends
Add rate-limit friendly delays and context timeouts when sending backups via Telegram. Iterate admin IDs with index to sleep 1s between sends; add 30s context.WithTimeout for each SendDocument call and defer file.Close() for opened files; insert a 500ms pause between sending DB and config files. These changes improve resource cleanup and reduce chance of Telegram rate-limit/timeout failures.
2026-02-14 22:31:41 +01:00
MHSanaei
e5c0fe3edf bug fix #3785 2026-02-11 22:21:09 +01:00
MHSanaei
84013b0b3f v2.8.10 2026-02-11 18:21:43 +01:00
bakatrouble
fc6344b840 Fix ipv6 hostname parsing for subscriptions (#3782) 2026-02-11 15:33:53 +01:00
MHSanaei
c2f409c3c4 fix security issue 2026-02-09 23:36:10 +01:00
surbiks
4779939424 Add url speed test for outbound (#3767)
* add outbound testing functionality with configurable test URL

* use no kernel tun for conflict errors
2026-02-09 21:43:17 +01:00
Sanaei
5bb87fd3d4 fix : Uncontrolled data used in path expression
Co-Authored-By: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-02-07 22:54:40 +01:00
MHSanaei
f87c68ea68 Add workflow to clean old GitHub Actions caches
Adds a scheduled GitHub Actions workflow (.github/workflows/cleanup_caches.yml) that runs weekly (and via workflow_dispatch) to delete Actions caches not accessed in the last 3 days. The job uses the gh CLI with the repository token and actions: write permission to list caches, filter by last_accessed_at against a 3-day cutoff, and delete matching cache IDs.
2026-02-03 00:19:44 +01:00
Nebulosa
03f04194f2 Update geofiles according 304 http respond (#3690)
* feat: enhance geofile update process with conditional GET and modification time handling

* style: improve formatting in UpdateGeofile function
2026-02-02 23:20:57 +01:00