Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/web
AgeCommit message (Collapse)Author
2026-04-26Increase KCP maxSendingWindow to 2MiBMHSanaei
2026-04-26Replace with-block with explicit settingsMHSanaei
2026-04-26Default to dark theme when unsetMHSanaei
2026-04-26wireguard: linkMHSanaei
2026-04-26DNS outbound: Add rulesMHSanaei
2026-04-23Merge pull request #4092 from pwnnex/fix/iplimit-live-only-slot-countpwnnex
iplimit: dont count idle db-only ips toward the per-client limit (#4091)
2026-04-23iplimit: dont count idle db-only ips toward the per-client limitpwnnex
after #4083 the staleness window is 30 minutes, which still lets an ip that stopped connecting a few minutes ago sit in the db blob and keep the protected slot on the ascending sort. the ip that is actually connecting right now gets classified as excess and sent to fail2ban, and never lands in inbound_client_ips.ips so the panel doesnt show it until you clear the log by hand. only count ips observed in the current scan toward the limit. db-only entries stay in the blob for display but dont participate in the ban decision. live subset still uses the "protect oldest, ban newcomer" rule. closes #4091. followup to #4077.
2026-04-23Feature: Copy clients between inbounds (#4087)Rs.Nest
* 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-22SS: remove unsupported cipher methodMHSanaei
2026-04-22hysteria: also accept "hysteria2" protocol stringpwnnex
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-22Merge pull request #4083 from pwnnex/fix/iplimit-stale-db-evictpwnnex
Fix IP Limit continuous ban loop after a legitimate ban expires (#4077)
2026-04-22Fix IP Limit continuous ban loop from stale DB entries (#4077)pwnnex
After 60abeaa flipped the excess-IP selector to "oldest wins, newest loses" (to protect the original/current connections), the per-client IP table in `inbound_client_ips.ips` never evicted IPs that stopped connecting. Their stored timestamp stayed ancient, so on every subsequent run they counted as the "oldest protected" slot(s) and whichever IP was actually using the config now was classified as "new excess" and re-banned via fail2ban. This is exactly the #4077 scenario: two IPs connect once and get recorded, the ban lifts after the configured duration, the lone legitimate IP that reconnects gets banned again, and again, and again — a permanent 3xipl.log loop with no real abuser anywhere. Fix: when merging the persisted `old` list with the freshly observed `new` log lines, drop entries whose last-seen timestamp is older than `ipStaleAfterSeconds` (30 minutes). A client that's actually still active refreshes its timestamp any time xray emits a new `accepted` line for a fresh TCP, so the cutoff is far above even idle streaming sessions; a client that's genuinely gone falls out of the table in bounded time and frees its slot. Extracted the merge into `mergeClientIps` so it can be exercised by unit tests without spinning up the full DB-backed job. Tests cover: - stale old entry is dropped (the #4077 regression) - fresh old entries are still carried forward (access-log rotation is still backed by the persisted table) - newer timestamp wins when the same IP appears in both lists - a clock-skewed old `new` entry can't resurrect a stale IP - a zero cutoff never over-evicts Closes #4077
2026-04-22Extract bot command setup into trySetBotCommandsMHSanaei
2026-04-22fix(panel): set ALPN to h3 when switching to Hysteria protocol (#4076)Imgodmaoyouknow
- Automatically explicitly set ALPN to ['h3'] for Hysteria to prevent QUIC handshake mismatch.
2026-04-21Lower minimum Xray versionMHSanaei
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-21Add None option VLESS auth selectionMHSanaei
2026-04-21Fix blank Xray Settings page from wrapped xrayTemplateConfig (#4059) (#4069)pwnnex
`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-21fix timelocation for windowsMHSanaei
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-21sub json fix fragment noises effectMHSanaei
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-21Fix xhttp xPadding settings missing from generated links (panel + subs) (#4065)pwnnex
* Fix: propagate xhttp xPadding settings into generated subscription links The four `genXLink` helpers in `sub/subService.go` only copied `path`, `host` and `mode` out of `xhttpSettings` when building vmess:// / vless:// / trojan:// / ss:// URLs. Everything else — `xPaddingBytes`, `xPaddingObfsMode`, `xPaddingKey`, `xPaddingHeader`, `xPaddingPlacement`, `xPaddingMethod` — was silently dropped. That meant an admin who set, say, `xPaddingBytes: "80-600"` plus obfs mode with a custom `xPaddingKey` on the inbound had a server config that no client could match from the copy-pasted link: the client kept the xray/sing-box internal defaults (`100-1000`, `x_padding`, `Referer`), hit the server, and was rejected by invalid padding (queryInHeader=Referer, key=x_padding) length: 0 The user-visible symptom on OpenWRT / Podkop / sing-box was "xhttp inbound just won't connect" — no obvious pointer to what was actually wrong because the link itself *looks* complete. Fix: * New helper `applyXhttpPaddingParams(xhttp, params)` writes `x_padding_bytes=<range>` (flat, sing-box family reads this) and an `extra=<url-encoded-json>` blob carrying the full set of xhttp settings (xray-core family reads this). Both encodings are emitted side-by-side so every mainstream client can pick at least one up. * All four link generators (`genVmessLink` via the obj map, `genVlessLink`, `genTrojanLink`, `genShadowsocksLink`) now invoke the copy. * Obfs-only fields (`xPaddingKey`, `xPaddingHeader`, `xPaddingPlacement`, `xPaddingMethod`) are only included when `xPaddingObfsMode` is actually true and the admin filled them in. An inbound with no custom padding produces exactly the same URL as before — existing subscriptions are unaffected. * Also propagate xhttp xPadding settings into the panel's own Info/QR links The previous commit covered the subscription service (sub/subService.go). The admin-panel side — the "Copy URL" / QR / Info buttons inside inbound details — has four more xhttp-emitting link generators in `web/assets/js/model/inbound.js` (`genVmessLink`, `genVLESSLink`, `genTrojanLink`, `genSSLink`) that had the exact same gap: only `path`, `host` and `mode` were copied. Mirror the server-side fix on the client: * Add two static helpers on `Inbound`: - `Inbound.applyXhttpPaddingToParams(xhttp, params)` for `vless://` / `trojan://` / `ss://` style URLs — writes `x_padding_bytes=<range>` (sing-box family) and `extra=<url-encoded-json>` (xray-core family). - `Inbound.applyXhttpPaddingToObj(xhttp, obj)` for the VMess base64 JSON body — sets the same fields directly on the object. * Call them from all four link generators so an admin who enables obfs mode + a custom `xPaddingKey` / `xPaddingHeader` actually gets a working URL from the panel. * Only non-empty fields are emitted, so default inbounds produce exactly the same URL as before. Also fixes a latent positional-args bug in `web/assets/js/model/outbound.js`: both VMess-JSON (L933) and `fromParamLink` (L975) were calling `new xHTTPStreamSettings(path, host, mode)` — but the 3rd positional arg of the constructor is `headers`, not `mode`, so `mode` was landing in the `headers` slot and the actual `mode` field stayed at its default. Construct explicitly and set `mode` by name; while here, also pick up `x_padding_bytes` and the `extra` JSON blob from the imported URL so the symmetric case of importing a padded link works too. --------- Co-authored-by: pwnnex <eternxles@gmail.com>
2026-04-21Fix: hysteria link gen crashes when echConfigList is a string (#4064)pwnnex
`genHysteriaLink` was calling `.join(',')` on `this.stream.tls.settings.echConfigList`, but that field is bound to an `<a-input>` (single-line string) in `tls_settings.html` and defaults to `''` in `TlsStreamSettings.Settings`. Calling `.join()` on a string throws `TypeError: echConfigList.join is not a function`, which breaks the Info / QR buttons for every hysteria / hysteria2 inbound. All three sibling link generators (`genVmessLink`, `genVlessLink`, `genTrojanLink`) already pass the value directly: params.set("ech", this.stream.tls.settings.echConfigList) `URLSearchParams.set` will stringify arrays with `,` on its own, so the same one-liner works for both string and array inputs. Align `genHysteriaLink` with the other three. Fixes #4063 Co-authored-by: pwnnex <eternxles@gmail.com>
2026-04-21Reduce observatory probe intervals and timeoutMHSanaei
2026-04-21 balancerTags with a default empty entryMHSanaei
2026-04-21kcp : default value maxSendingWindowMHSanaei
MaxSendingWindow must be >= Mtu
2026-04-21Set CWND multiplier default and min to 1MHSanaei
2026-04-21salamander obfs and remove auth fieldMHSanaei
2026-04-21revert Fix geosite:ru rule (ram leak)MHSanaei
#4050 #4055
2026-04-20v2.9.0v2.9.0MHSanaei
2026-04-20Bump Xray version cutoff to 26.4.17MHSanaei
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-20bug fixMHSanaei
2026-04-20XDNS finalmask: Support resolvers (client) and domains (server)MHSanaei
Treat the xdns mask type as a multi-value setting and update forms accordingly. Inbound/outbound UdpMask models now return arrays for xdns (inbound: settings.domains, outbound: settings.resolvers) using Array.isArray checks. UI templates were split so 'header-dns' still uses a single domain string, while 'xdns' renders a tags-style <a-select> for multiple entries (domains/resolvers). Conditionals were made explicit (mask.type === ...) instead of using includes(). Changed 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-04-20Sniffing: Add ipsExcluded, domainsExcluded (supports IP, CIDR, "geoip:", "ext:")MHSanaei
2026-04-20tun: dual MTU, gateway, DNS, auto routingMHSanaei
Change TunSettings to support separate IPv4/IPv6 MTU values and add gateway, DNS, autoSystemRoutingTable and autoOutboundsInterface properties. Introduces _normalizeMtu to accept legacy single-value or array forms and provide sensible defaults. Update fromJson/toJson to handle new fields and preserve backward compatibility. Update tun form UI to expose MTU IPv4/IPv6 inputs, Gateway/DNS tag selects, Auto Routing Table and Auto Outbounds input.
2026-04-20Add ipsBlocked to FreedomMHSanaei
Expose an ipsBlocked array on Outbound.FreedomSettings and wire it into the outbound form. The constructor now defaults fragment to {} and noises/ipsBlocked to arrays for robustness; fromJson/toJson handle ipsBlocked and omit it when empty. The outbound HTML adds a tag-style <a-select> bound to outbound.settings.ipsBlocked (with comma tokenization and placeholder) so users can enter IP/CIDR/geoip entries.
2026-04-20mKCP transport: Add cwndMultiplierMHSanaei
Replace legacy KCP buffer options with cwndMultiplier and maxSendingWindow across models and UI. Updated KcpStreamSettings in web/assets/js/model/inbound.js and web/assets/js/model/outbound.js (constructor, fromJson and toJson) to remove congestion/readBuffer/writeBuffer and use cwndMultiplier/maxSendingWindow instead. Updated web/html/form/outbound.html to reflect the new KCP fields in the stream form and to include extensive template formatting/markup cleanup for consistency and readability.
2026-04-20reset button for auth passwordMHSanaei
2026-04-20finalmaskMHSanaei
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-20add hysteria inboundMHSanaei
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-20Centralize session options and adjust cookiesMHSanaei
Configure session cookie options centrally in initRouter and remove per-login MaxAge handling. Deleted SetMaxAge helper and its use in the login flow; session.Options are now applied once using basePath with HttpOnly and SameSite defaults, and MaxAge is set only when the stored setting is available and >0. Also make CookieManager.setCookie treat exdays as optional (only add expires when provided) and stop using a hardcoded 150-day expiry for the lang cookie in the JS language manager. Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2026-04-20feat: Add NordVPN NordLynx (WireGuard) integration (#3827)Peter Liu
* 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-20Add SSRF protection (#4044)Sanaei
* 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-19feat add clash yaml convert (#3916)zhuzn
* 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-19revert: Disconnect client due to exceeded IP limit (#3948)HamidReza Sadeghzadeh
* fix: Ban new IPs with fail2ban instead of disconnected the client. * fix: Remove unused strconv import * fix: Revert log fail2ban format * fix: Disconnect the client to remove the banned IPs connections * fix: Fix getting the xray inbound api port * fix: Run go formatter * fix: Disconnect only the supported protocols client * fix: Ensure the required "cipher" field is present in the shadowsocks protocol * fix: Log the errors in the resolveXrayAPIPort function * fix: Run go formatter
2026-04-19Fix geosite:ru rule (Normalization to RU vs lowercase ru) (#3971)Troodi
* Fix geosite:ru rule (Normalization to RU vs lowercase ru) * fix
2026-04-19Add new hourly reset traffic (#3966)Andrew Smirnov
* Add new hourly reset traffic * fix
2026-04-19Add custom geosite/geoip URL sources (#3980)Vladislav Tupikin
* feat: add custom geosite/geoip URL sources Register DB model, panel API, index/xray UI, and i18n. * fix
2026-04-19fix: enhance WebSocket stability, resolve XHTTP configurations and fix UI ↵lolka1333
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-03-18Add Go code analyzer workflowMHSanaei
2026-03-18feat(tgbot): send connection links and qrs on client creation (closes ↵Abdalrahman
#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-17Update translate.ru_RU.toml (#3889)Nikolay
Change to plural (geofiles, not geofile)