txlyre

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 380aff4d82 Add remote routing URL support (#6168) * Add remote routing URL support * Harden remote routing refresh * fix(sub): harden remote routing fetch and accept Mihomo src rule flag Remote routing bytes reach the YAML/JSON parsers from goroutines that run outside Gin's recovery, so a parser panic on crafted input would take down the whole panel. Contain it in fetch() (a panic now degrades to a failed refresh that keeps the last-good value and releases the in-flight slot) and start the refresh, cache-load and startup-warm goroutines through common.GoRecover like the other background workers. The route-graph validator only skipped a trailing no-resolve flag, so a valid Mihomo rule like IP-CIDR,x,DIRECT,no-resolve,src was rejected as an unknown target; skip both option flags. Also deduplicate the HTTPS-source classification into common.ParseRemoteRoutingURL so the save-time validator and the resolver can never drift (internal/sub imports internal/web/service, so the copy existed only to avoid the import cycle), move the test-only mergeRemoteClashRulesYAML helper into the test file, and trim oversized comment blocks. --------- Co-authored-by: Duxxie <[email protected]> Co-authored-by: Sanaei <[email protected]>
  • 3a2f9b48da feat(web): add network-only PWA installability (#6190) * feat(web): add network-only PWA installability Serve the manifest, registration script, network-only service worker, and icons under the runtime web base path so panels remain installable at arbitrary configured URLs. This does not add offline caching or change panel, API, database, or Xray behavior. * chore(docs): remove development planning notes Keep the pull request focused on the PWA implementation, tests, and user-facing verification documentation. * feat(web): adopt the 3X logo PWA icon set from #1865 Replace the two placeholder SVG icons with the six-size PNG set (16/24/32/64/192/512) contributed by @Incognito-Coder in PR #1865. The PNGs have transparent rounded corners, so the manifest entries drop the maskable purpose claim and rely on the default any. --------- Co-authored-by: korsun009 <277924786+[email protected]> Co-authored-by: Sanaei <[email protected]>
  • 3f1dd4bf5a fix: follow-ups from the post-merge reviews of #6221, #6227, #6230 and #6239 (#6250) * fix: follow-ups from the post-merge reviews of #6221, #6227, #6230 and #6239 Six defects the automated reviews found after those PRs merged. Each is verified rather than taken on trust — two by experiment, the rest by reading the merged code. **Import restore never wrote an empty local value** (#6227). GORM builds the assignment map from the struct passed to Assign and drops zero-valued fields, so `Assign(model.Setting{Value: ""})` produced an empty Updates and the imported row survived. Empty is the normal state: UpdateAllSetting writes a row for every AllSetting field including the blank ones. That is exactly the case the PR existed for — a destination with no certificate inheriting the source machine's path. Confirmed with a throwaway test before changing anything: the value stayed "IMPORTED". Now uses saveSetting, which is not zero-filtered. **Import destroyed node mTLS material** (#6227). The "no local row means the default applied, so drop the import" branch fires for the five nodeMtls* keys, which are minted on demand and deliberately absent from AllSetting, so a fresh install has no row for them. Reinstall-then-restore therefore deleted the CA certificate and its private key — and the backup was the only copy, since neither is surfaced in the UI or the export. Those keys are now kept. **The clients-list enable toggle wiped renewal state** (#6239, #6238). setEnable hand-builds the update payload and carried reset but not resetDay or resetMax, so one click on the switch turned calendar mode off and lifted the renewal cap permanently. The form-modal tests could not catch it because that path does send both fields. **"Delete depleted clients" deleted calendar clients** (#6239). The predicate read `reset = 0` as "does not auto-renew", which is exactly the calendar shape, in two places. Both now share one constant that also requires `reset_day = 0`. **Allowlist validation and parsing disagreed** (#6230). Save used net, scan used netip, and they differ: `198.51.100.0/024` saves without complaint and is silently dropped at scan — the failure the PR set out to remove. Verified by running both parsers. An IPv4-mapped prefix parsed but could never match, because contains() unmaps the query while the prefix stayed 128-bit; it is unmapped at parse now. A test asserts the two acceptance sets agree. **A comment stated the opposite of the truth** (#6221). GetInbounds has no enable filter, so a node reports a disabled inbound normally; the row in that bug report was missing only because it was never delivered. Reworded to the real invariant. Also trims two comment blocks in ip_limit_allowlist.go to the repo's two-line maximum. Not included: the reviewer's suggestion to lift the node hand-off out of `if inbound.Enable` in AddInbound. It is the right root-cause fix, but it changes delivery behaviour on multi-node deployments and belongs in its own change with its own testing, not in a cleanup batch. One reported finding is not real: BulkCreate does call validateClientResetDay, validateClientResetMax and validateClientTrafficReset — verified in the merged tree. * fix(netsafe): wrap both errors so errorlint passes Unrelated to this PR's subject and in a file it does not otherwise touch. It is here only because CI lints the merge result, and `main` has been red since #6242 landed: `fmt.Errorf("%w; %v", ...)` wraps the first error and formats the second, which errorlint rejects. Go 1.20 allows more than one %w, so both are wrapped now and `errors.Is` works against either.
  • abd320994a Add per-client external link controls (#5650) * Add enable toggle for external client links * Document external link enable API fields * Extend external client link metadata * Fix external subscription cache status updates * fix(sub): address the review on per-client external link controls Blocking: the expiry filter dropped legacy rows. expiry_time was added without a default, so AutoMigrate makes it nullable and backfills NULL, and `expiry_time = 0 OR expiry_time > ?` is false for NULL under three-valued logic — every external link written before the upgrade vanished from all subscriptions. Add `default:0` on expiry_time and last_fetch_at, make the predicate NULL-tolerant, and backfill the NULLs a pre-fix build could already have written. Rework fetch-status recording. It ran inside the singleflight in-flight window, so every goroutine parked on the shared fetch waited for a DB write to commit on the public, unauthenticated subscription path — and because it was keyed on the row id, waiters and cache hits recorded nothing, leaving rows that lost the race stuck on "Not fetched yet" forever. fetchSubscriptionLinks now reports whether it did the network fetch and expandEntry records afterwards, off the serving path, keyed on kind+value so every row sharing the URL is stamped by the one fetch. Keying on value also closes the recycled-rowid hazard: saves delete and re-insert rows, and SQLite reuses rowids, so an in-flight write could land on an unrelated client's row. The write no longer discards its error either. Drop the inert id round-trip. The panel never sent it, and the byId branch was guarded by the exact kind+value equality that byKindValue already keys on, so it could not change an outcome. Matching on kind+value alone is what actually preserves fetch status across saves. Reject a negative expiryTime instead of storing a row that is silently invisible in every subscription — elsewhere a negative expiryTime means "a duration from first use", so an API caller reusing that convention got no error and no links. Drop the ~50 lines of .client-form-* / .client-inbounds-field CSS that no component renders; it is leftover from the WireGuard PR this one was split from. i18n: reuse the already-translated pages.inbounds.leaveBlankToNeverExpire instead of shipping an English duplicate under pages.clients, and translate namePrefix, lastFetchAt, lastFetchError and neverFetched into all 12 non-English locales. Cover the persistence path that had no test: the fetch-status writer over a real DB against a failing then a succeeding server, a cache hit writing nothing, and the negative-expiry rejection. --------- Co-authored-by: MHSanaei <[email protected]>
  • 708a69acde fix(reality): make the REALITY target check usable on a private network (#6242) * fix(reality): make the REALITY target check usable on a private network The probe dials through netsafe.SSRFGuardedDialContext, so a fronting service reachable only inside the deployment (a Docker service name, a LAN address) always failed with "blocked private/internal address": the inbound itself works, because the guard sits in the probe path only, so the panel reported a red verdict on a healthy configuration. Instead of a panel-wide setting that lifts the guard for good, the guard is now lifted per probe and only after the operator confirms the local-network warning in a modal; the verdict keeps privateTarget set, so a passing local check stays a warning rather than a green success. The probe also sent the target host as SNI. Clients dial the target but send a name from serverNames, so a fronting proxy answered with its default certificate — a Traefik front reached as "traefik" reported "certificate is valid for <hash>.traefik.default, not traefik" on a deployment whose clients get a valid chain. The panel now sends the first configured serverName as SNI and the certificate is verified against it; empty serverNames keeps the old fallback. The reported target stays the dialled address, so a passing check no longer rewrites the target field with the SNI host. The result panel reports what was actually seen: the SNI used, the certificate subject/issuer and its expiry stay visible when the chain is untrusted (with "Not trusted" appended) instead of being replaced by that verdict alone. Certificate names are copied into the SNI field only when the chain verified — the names on a proxy's default certificate would otherwise become the SNI of the next check. The bulk/CIDR scanner keeps the guard unconditionally: honouring the opt-in there would turn it into an internal network scanner. * fix(reality): recover from a stale SNI and report a refused address reliably Review follow-up on the REALITY target check. The probe sends the stored serverNames as SNI, and the panel only wrote names back when the whole chain verified, so switching Target while the SNI field still held the previous target's names failed every rescan: the new target's real names came back from the probe but were discarded with the verdict. The certificate is now checked in two steps — chain first, then the name — and a trusted chain presented for other names is enough for the panel to offer those names, so the next scan passes. Picking a row in the bulk scanner replaces the names outright, since keeping the previous target's SNI leaves a REALITY config that cannot work. SSRFGuardedDialContext kept the refusal only in lastErr, so on a dual-stack name a refused private address followed by a failing public one lost the sentinel and the panel silently skipped the confirmation. The refusal is now tracked separately and reported alongside the last dial error. Honouring the opt-in is logged with the target and the resolved address, since it bypasses the SSRF guard on an authenticated endpoint. The read-only SNI row in the result is labelled "SNI used" so it no longer collides with the SNI field below it, and the comment blocks are back within the 2-line limit. --------- Co-authored-by: Claude <[email protected]>
  • Просмотр сравнение для этих 11 коммитов »

1 час назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 6e80a468e3 feat(server): keep this machine's own settings when importing a database (#6227) * feat(server): keep this machine's own settings when importing a database Import replaces the database wholesale, so the uploaded file's listen addresses, ports, base path, certificate paths and node identity land on the destination. Moving a configuration to a new host therefore leaves the panel answering on an address it does not own, presenting certificates it does not have, and claiming the source machine's identity towards its nodes. Capture the host-bound settings before the swap and write them back once the imported database opens. Everything else — inbounds, clients, templates, the rest of the settings — still comes from the file. A checkbox controls it, defaulting to keeping this machine's values; clearing it restores the old behaviour for anyone deliberately cloning a host. * fix(server): drop imported host settings this machine never had, and cover Postgres Two gaps in the previous commit. The snapshot only recorded rows that existed, so a key with no row here — the default for every certificate path, both listen addresses and all the node mTLS material — kept the imported value: exactly the case the change is meant to fix. The snapshot now records which keys were absent and deletes the imported row for them, letting the default apply again. The PostgreSQL path took the flag and ignored it, so a dump restore still adopted the source machine's settings. It now captures and restores the same way the SQLite path does. * chore: drop the accidentally committed dist build stub internal/web/dist/.gitkeep is what make dist-stub creates locally. Committing it changes fresh-clone behaviour for everyone: today a bare go build fails loudly on //go:embed all:dist, which is the documented signal to run the stub target; with the file present the build succeeds and the panel serves an empty dist instead. --------- Co-authored-by: n0ctal <[email protected]> Co-authored-by: Sanaei <[email protected]>
  • e940f30bb8 feat(clients): cap how many times a client may auto-renew (#6238) * feat(clients): cap how many times a client may auto-renew Auto-renew today runs forever: a prepaid or fixed-term client keeps being handed new periods until an operator remembers to switch it off. There is no way to say "renew this three times, then let it lapse". Add a per-client maximum. Zero keeps today's behaviour, so nothing changes for anyone who does not set one. When the count is reached the client is simply left to expire, like any client without auto-renew. Catching up several missed periods spends one allowance per period. A client that was away for three cycles must not receive three of them free of the cap, and the catch-up stops at the last period the cap paid for rather than jumping to the present. * fix(clients): persist the auto-renew cap and stop the capped churn resetMax lived only in the inbound settings JSON and client_traffics, so every path that rebuilds a client from the clients table wrote it back as zero. The edit dialog showed 0 for a capped client, and saving an unrelated comment change lifted the cap; an attach or a traffic reset did the same with no operator action at all. Adds reset_max to ClientRecord and threads it through ToRecord, ToClient, applyClientRecordMerge, the record update map and ClientSlim, so the cap survives the round trip. When the cap truncates a catch-up the client is still expired, but the renewal side effects fired anyway: counters were zeroed for periods it can never use, and it was enabled and pushed to xray only for disableInvalidClients to undo both in the same transaction. Those are now skipped when the new expiry has not reached the present. Also makes any non-positive resetMax mean unlimited instead of silently meaning "never renew again", rejects a negative one at the service layer, surfaces renewals used against allowed in the client info modal so the operator can see what to raise, adds the field to the bulk-add modal, translates the labels in all 13 locales, and drops the stray internal/web/dist/.gitkeep build stub. * fix(clients): let the renewal cap be changed after creation ClientService.Update writes the record columns directly only for a client with no inbounds. The normal path goes through SyncInbound and applyClientRecordMerge, which this change had not extended, so raising a cap from 3 to 6 — the natural action when a customer buys another block of periods — updated the inbound settings JSON while clients.reset_max kept the old value and the renewal query kept enforcing it. The existing test did not catch it: it asserted the cap survived an unrelated edit, and it survived precisely because nothing on that path ever wrote it. TestClientEditChangesTheRenewalCap raises the cap and then lifts it entirely; removing the record write turns it red. * chore: drop the accidentally committed dist build stub internal/web/dist/.gitkeep is what make dist-stub creates locally. Committing it changes fresh-clone behaviour for everyone: today a bare go build fails loudly on //go:embed all:dist, which is the documented signal to run the stub target; with the file present the build succeeds and the panel serves an empty dist instead. --------- Co-authored-by: n0ctal <[email protected]>
  • 6a674c7f0c fix(node): keep disabled inbounds the node snapshot cannot report (#6221) * fix(node): keep disabled inbounds the node snapshot cannot report A node builds its traffic snapshot from the inbounds Xray is actually running, so an inbound with enable=false is never in it. The central sweep reads that absence as "the node no longer has this inbound" and deletes the row, its clients' traffic history and its port reservation — on a perfectly healthy node, with no way to tell it apart from a real deletion. Disabling an inbound in the panel and waiting one sync interval is enough to lose it. Skip disabled inbounds in the sweep: their absence carries no information, and an explicit delete still removes them. * chore: drop the accidentally committed dist build stub internal/web/dist/.gitkeep is what make dist-stub creates locally. Committing it changes fresh-clone behaviour for everyone: today a bare go build fails loudly on //go:embed all:dist, which is the documented signal to run the stub target; with the file present the build succeeds and the panel serves an empty dist instead.
  • 81cfd8570e fix(inbounds): close the port check-and-claim race on the serial writer (#6225) * fix(inbounds): close the port check-and-claim race on the serial writer AddInbound reads the port conflict outside its transaction and then commits in a bare db.Transaction, so two overlapping creates both pass the read and both insert. UpdateInbound already runs on the single traffic writer, and so does the node snapshot path; AddInbound is the one inbound writer left out. Move it onto runSerializedTx and evaluate the conflict inside the transaction, in both AddInbound and UpdateInbound. The check and the claim then commit together on one goroutine, which closes the window on SQLite (immediate write lock) and PostgreSQL alike without new schema, locks or configuration. The wildcard/specific pair is the case worth naming: those are two distinct rows, so no unique index can reject them — only the semantic check can, and only if nothing can interleave between it and the insert. * fix(inbounds): restore the port check UpdateInbound lost The previous commit deleted UpdateInbound's pre-flight conflict check and never added the in-transaction one, so editing an inbound onto an occupied port was accepted outright. No test covered that path, so CI stayed green. Evaluate the conflict inside the transaction, as AddInbound already does, and add the regression test that fails without it. * chore: drop the accidentally committed dist build stub internal/web/dist/.gitkeep is what make dist-stub creates locally. Committing it changes fresh-clone behaviour for everyone: today a bare go build fails loudly on //go:embed all:dist, which is the documented signal to run the stub target; with the file present the build succeeds and the panel serves an empty dist instead. --------- Co-authored-by: n0ctal <[email protected]>
  • 5c9268c431 feat(i18n): translate the log levels, access events and calendar labels (#6226) * feat(i18n): translate the log levels, access events and calendar labels The log-level selector, the access-log event tags, the Sub Formats sidebar entry and the calendar choices were hardcoded English, so a fully translated locale still showed them in English on core screens. Add eleven keys across the 13 locales and reference them. Russian and Ukrainian are translated; the remaining locales carry the English string, the same convention the existing files already use for untranslated entries. Two module-level constants had to move: the calendar list and the access-event map were built outside the component, where t is not in scope. The event map now stores keys and resolves them at render. * fix(i18n): keep the log export language-independent and fit the translations Three follow-ups from review. The downloaded x-ui.log had started carrying the translated event text, so its contents depended on the panel language and the Russian value for PROXY contains a space in a field format whose other values are single tokens. The export keeps DIRECT/BLOCKED/PROXY; only the on-screen tag is translated. The log-level select had a fixed 95px width sized for "Warning", which clips "Предупреждение"; it now grows with its content. The three access filters stayed English while the tags they filter became translated, so they use the same keys. --------- Co-authored-by: n0ctal <[email protected]>
  • Просмотр сравнение для этих 7 коммитов »

9 часов назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 8cec47a8a5 fix(ci): resync the bot prompts with the repo and close the gaps an audit found The three prompts still enforced the comment ban CLAUDE.md replaced with the 2-line cap on Aug 1 (1ff90c5b), so the review bot would flag every legitimate short comment; frontend/CLAUDE.md and CONTRIBUTING.md carried the same stale rule. The PR reviewer's recipe for reading a post-change file (headRefOid + pr diff) was unfulfillable with its allowlist - it now fetches refs/pull/N/head and reads blobs via git show, object-only, no checkout. Conventions the reviewer checks now include the unchecked docs openapi.json copy step, the docs/lib/xray third link implementation, the both-ways route contract, and the i18n dead-key half of the rule. Also: drop the SUBPROCESS_ENV_SCRUB=0 override on the two untrusted-input jobs (the mention job proves gh works scrubbed); teach the triage prompt the issue forms (pre-applied labels, required fields, no re-asking); add a security-report exception plus SECURITY.md so vulnerabilities are not confirmed publicly; add a clarification follow-up job so a reporter's reply to "clarification needed" is actually processed; review PRs again on ready_for_review and skip drafts; stamp the reviewed head SHA so force-pushes visibly date a review; scope gh issue/pr edit to label and title flags; per-job concurrency; comment guards now match the actual bot login after the run started; artifact names survive re-runs; the mention prompt's repo map and env-var facts corrected (XUI_PORT, XUI_TUNNEL_HEALTH_*, distro env files, memory.high, encrypt-tokens). The bug and feature forms also referenced a "needs triage" label that does not exist in the repo and was silently never applied - dropped.

1 день назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 4b0e9f9b60 fix(nodes): log the inbound the node snapshot removes centrally (#6219) The orphan sweep deletes a central inbound and the traffic history of every client on it, but wrote nothing. An inbound that vanishes minutes after being created is then indistinguishable from one that never arrived, and the only way to tell them apart is reading the source. Name the node, tag, id and port so the removal is visible in the panel log. Co-authored-by: n0ctal <293235942+[email protected]>
  • 5d6d98d1f9 fix(warp): preserve WARP Plus license key when changing IP (#6218) ChangeWarpIP rotated the WireGuard keypair by registering a brand-new Cloudflare device via RegWarp, which overwrites the stored warp data with the fresh registration's empty license_key. The old key was then re-applied only best-effort: any SetWarpLicense failure was swallowed with a warning log, permanently deleting the saved WARP Plus key, and even on success the response returned to the UI carried the pre-reapply snapshot (empty key). Fix: write the old license key back into the stored warp data immediately after RegWarp (before the remote upgrade attempt), so storage never loses it; keep the remote re-apply as best-effort but surface its failure as a warning field in the response; and return the final stored data so the modal shows the preserved key. The auto-update IP job shares this path and is fixed too. warpAPIBase is now a var so integration tests can point at a mock Cloudflare API. Co-authored-by: rqzbeh <[email protected]>
  • Просмотр сравнение для этих 2 коммитов »

2 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • b53a5515d6 fix(frontend): make the jalali expiry clear button actually clear persian-calendar-suite seeds today's date and emits it whenever it mounts without a value. Clearing the expiry remounts the picker with a null value, so the library immediately fired onChange(today) and the date came straight back — and it also painted that seeded date into its read-only input. Swallow the mount-time emit (re-armed on every clear-remount) and hide the seeded text while the value is empty, so a cleared expiry stays empty and a fresh client/inbound form no longer silently adopts today as its expiry.
  • 3fa88adbd7 fix(inbounds): surface form validation errors (#6084) * inbounds: surface form validation errors React Hook Form validation previously returned early without showing why an inbound save was blocked. Report the first field error and switch to the corresponding form tab so operators can correct it. * Fix inbound form tab error navigation Improve react-hook-form error traversal so validation stops on real `FieldError` leaves (detected by `type`) instead of any object with a `message`. This makes Save reliably jump to the tab containing the first invalid field and show the specific error, avoiding the previous generic/ambiguous invalid-state handling. --------- Co-authored-by: sonic <[email protected]>
  • 930a0ed59d feat(inbound): DisableFlow — opt an inbound out of auto XTLS Vision (#5689) (#5698) * feat(inbound): add DisableFlow to opt an inbound out of auto XTLS Vision Adds an inbound-level DisableFlow flag so operators can suppress automatic xtls-rprx-vision injection on a specific inbound even when its transport is flow-capable — e.g. a tunneled/CDN-fronted XHTTP+vlessenc inbound where Vision is not wanted, while keeping it on the same client's Reality inbounds. When set, the inbound reports tlsFlowCapable=false, the write path clamps each attached client's flow to empty (so flow_override stores ""), and share links/subscriptions never carry the flow for it. The flag is panel-only metadata and is never sent to xray. Closes part of #5689. * feat(inbound): DisableFlow toggle in the inbound form (frontend) Wire the DisableFlow field through the form schema + adapters and add a VLESS-gated switch in the inbound form, plus en-US strings. tsc --noEmit and eslint pass. * fix(inbound): honor DisableFlow in all emitters + on toggle; regen OpenAPI Addresses review on #5690: - Clash (clash_service.go) and JSON (json_service.go) subscription emitters now also skip the flow for a DisableFlow inbound — previously only the raw share-link path was gated, so those two still advertised it (blocking 1). - UpdateInbound now strips any flow already stored on a DisableFlow inbound's clients (settings.clients[].flow + client_inbounds.flow_override) so xray and the subscription agree; otherwise toggling DisableFlow on an existing Vision client left xray expecting a flow the client no longer sends. - Regenerated the OpenAPI + zod/types/examples artifacts for the new field and added an example tag (blocking 2; make gen-check is clean). - Added Clash + JSON DisableFlow suppression tests alongside the raw-link one. * fix(inbound): make DisableFlow durable, clamp on create, guard live config Addresses the review + completeness audit on #5690: - UpdateInbound now persists inbound.DisableFlow onto the saved row. It was only read to branch strip-vs-restore, so toggling the flag on an existing inbound never stuck and MigrationRestoreVisionFlow re-injected the flow — the exact #5689 path (editing a multi-inbound client's inbound) self-reverted. - DBInbound (frontend) declares + initializes disableFlow so ObjectUtil .cloneProps carries the API value through; the edit Switch previously always read false and re-saving silently reverted the opt-out. - AddInbound strips client flow (settings + parsed clients) when DisableFlow is set, so a created-disabled inbound never persists a flow xray would expect. - GetXrayConfig forces flow="" for DisableFlow inbounds (VLESS + Trojan) as defense-in-depth, keeping the live config and the subscription in agreement. - genTrojanLink share link honors DisableFlow too. - Drop the dead explicit flow_override clear in UpdateInbound (SyncInbound rebuilds it from the stripped settings). - Clear disableFlow in the inbound form when switching to a non-VLESS protocol. - Add disableFlow/disableFlowHelp to the remaining 12 locales. Tests: stripClientFlows unit cases; DB-backed AddInbound clamp; UpdateInbound persist+strip+resist-restore regression (fails without the persist fix); frontend DBInbound + adapter round-trip (fails without the model field). * style(inbound): drop // line comments per repo CLAUDE.md The DisableFlow work followed the surrounding code's commenting style; the repo CLAUDE.md forbids // line comments in committed Go/TS. Remove the comments I added (Go + frontend + tests) and regenerate OpenAPI/schemas, which drops the generated field descriptions sourced from the Go doc comments. No behavior change; full go test (service+sub, CGO) + frontend typecheck/vitest green; golangci-lint clean on the changed files. * fix(runtime): propagate disableFlow to nodes Preserve the inbound DisableFlow flag when syncing inbounds across nodes and when recreating central records from remote traffic snapshots. This keeps multi-node deployments from reintroducing VLESS Vision flow in node configs and share links, and updates the related tests to cover the wired field and VLESS JSON generation.
  • f22df49a71 fix(sub): restore the subscription info page for browser visits Revert 43bc9153 and its follow-up 338822ab. The copy-only notice replaced the themed sub page for every browser request, so mobile users got a bare "This is a subscription link" screen instead of their traffic, expiry and links — and it left serveSubPage plus the custom-theme renderer as dead code.
  • b4e4478699 feat(inbounds): add a narrow endpoint for subscription sort order (#6179) * feat(inbounds): add a narrow endpoint for subscription sort order Changing an inbound's position in subscription output currently goes through /update/:id, which takes a whole inbound: the caller has to send settings and the entire client list back, and whatever it read before the edit is what gets written. Two people reordering and editing clients in the same inbound race on one blob, and the reorder wins by overwriting. Mirror the existing /setEnable/:id shape. The handler takes only the index and the service reads the stored inbound, so nothing in the request can reach the settings JSON. Node-owned inbounds are marked dirty in the same transaction and pushed through the existing runtime update. * fix(nodes): scope sub sort index updates --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • Просмотр сравнение для этих 7 коммитов »

2 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 43bc915397 fix(sub): serve a copy-only page when a subscription URL is opened in a browser (#6183) * fix(sub): show copy-only page for browser subscription visits Browser navigation to /sub previously rendered the normal subscription page, which exposed subscription material in page data or raw base64 depending on request headers. Keep VPN clients on the raw subscription body, but classify browser document requests and return a neutral static copy-only HTML page with no embedded share links or page data. This preserves the C1 LimitIP parser fix in the same master candidate while avoiding a DE rollback of the browser subscription UX. * fix(sub): keep the themed page for an explicit html request Only implicit browser navigation is downgraded to the copy-only page. An operator who appends html=1 or view=html already holds the URL, so the themed subscription page keeps rendering for them and serveSubPage stays in use. * fix(sub): keep browser pages copy-only
  • dafd3c0e64 feat(sub): warn when salamander settings cannot reach the client (#6177) * feat(sub): warn when salamander settings cannot reach the client A hysteria2 share link carries obfuscation as obfs=salamander plus obfs-password, and nothing else. Xray's finalmask accepts more than that — packetSize among them — and those extra settings change what the server expects on the wire. The emitted URI then looks complete but describes a server the client cannot reach: every standard client applies plain salamander, the server drops the packets, and the failure is silent on both ends. Log the unexpressible keys when building such a link, naming the inbound, so the cause is visible instead of appearing as a client-side problem. * fix(sub): deduplicate salamander warnings
  • acbf09e710 fix(frontend): restore responsive table height Remove viewport-capped vertical scrolling so page size controls the rendered table height and page scrolling remains responsive.
  • be70535b94 feat(inbounds): improve multi-node online attribution (#6164)
  • 2d669fa4b7 feat(sub): add template variables to subscription metadata (#6163)
  • Просмотр сравнение для этих 17 коммитов »

3 дней назад

txlyre синхронизированные и удаленные ссылки copilot/fix-review-comment-3774434337 на txlyre/3x-ui из зеркала

3 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • ad32144c42 fix(sub): use a fullwidth percent in USAGE_PERCENTAGE (#6174) * fix(sub): use a fullwidth percent in USAGE_PERCENTAGE A remark is placed in the share link fragment, so an ASCII percent is percent-encoded to %25. Happ treats such a fragment as malformed, discards the whole remark and falls back to showing the server hostname, which defeats the point of a remark template and leaks the host into the client's server list. Emit U+FF05 FULLWIDTH PERCENT SIGN instead. It renders the same to a reader, never produces %25, and round-trips through url.Parse unchanged. * test(sub): exercise production fragment encoding --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • 34c248bb79 fix(cli): stop -getApiToken accumulating admin tokens (#6175) * fix(cli): stop -getApiToken accumulating admin tokens `x-ui setting -getApiToken` reads like a getter, but when tokens already exist it minted a brand-new one named `cli-fallback-<unix>` on every invocation. The plaintext is printed once and the row stays enabled forever, so an operator who runs the command a few times while debugging silently leaves several admin-equivalent credentials behind that nobody can tell apart or revoke knowingly. Keep the convenience the fallback was added for, but rotate a single `cli-fallback` token instead: RecreateByName drops any existing row with that name before issuing a new one, so at most one CLI-issued token exists at a time and the previous plaintext stops working. * fix(api-token): preserve token on failed replacement --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • 17fea2f656 fix(database): keep IP limits when the fail2ban probe is inconclusive (#6176) * fix(database): keep IP limits when the fail2ban probe is inconclusive ResetIpLimitNoFail2ban clears limitIp on every client — inbound settings JSON and the clients table — whenever fail2banCanEnforce() returns false, then records itself in the seeder history so it never re-evaluates. The probe was a single `fail2ban-client -h` run, so it answered false both when fail2ban is genuinely absent and when the command merely failed that once: a panel that starts before fail2ban is up, or in a container where it is installed a moment later, permanently loses every configured limit with no log line and no way back. Separate the two. A missing binary still means "absent" and the cleanup runs as before; a binary that exists but will not run is reported as unknown, leaves the configured values untouched, logs why, and does not record the seeder, so the next start decides again. * test(database): cover fail2ban reset safeguards --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • c5dec64d36 fix(clients): push bulk client changes to nodes only after the commit lands (#6181) * fix(clients): apply bulk mutations after durable commit * test(clients): guard bulk pushes behind commit * fix(clients): fully delete remote bulk clients --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • b56b087254 fix(migration): stop a half-applied startup migration from committing silently (#6182) * fix(traffic): check maintenance commits and IP-limit errors * fix(migrations): propagate transactional failures --------- Co-authored-by: n0ctal <293235942+[email protected]>
  • Просмотр сравнение для этих 13 коммитов »

3 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • d291e1c5ee Bump Go toolchain and x dependencies Refresh the Go toolchain from 1.26.5 to 1.26.6 and update the related x/* and protobuf dependency set in go.mod/go.sum. This keeps the project aligned with the current patch releases and ensures the module graph matches the expected transitive versions.
  • ecadfd0e60 fix(clients): stop recomputing the summary badges from the client_stats snapshot (#6169) * fix(clients): stop recomputing the summary badges from the client_stats snapshot pickClientsSummary's coverage guard (serverSummary.total > allClientStats.length) only catches a net shortfall: an orphaned client_traffics row and a client still missing one can cancel out, or an orphan surplus alone can pass uncaught, and either way the guard fails to fall back (#6116). client_paging.go's q.summary() already derives the same bucket counts with clients as the driving table (LEFT JOIN client_traffics), so it cannot miscount either shape regardless of how the row got there, and listQuery already polls it every 5s — the same cadence client_stats ticks on. The client-side recompute bought no fresher a number than the server already provides on its own poll, only a window to get one wrong, so this drops it: the summary badges now always read serverSummary directly. allClientStats, computeClientsSummary, pickClientsSummary and sameSummaryInputs are removed as dead code along with it; the per-row live traffic patch in applyClientStatsEvent is untouched, since it reads the same snapshot by email match rather than by count and was never exposed to this class of bug. * fix(clients): force a refetch on window focus and drop a stale comment Review feedback on PR #6169: listQuery combines staleTime: Infinity with refetchInterval: 5000, which pauses while the tab is hidden. The WS-driven per-row traffic patch in applyClientStatsEvent has no such visibility gating, so on a background tab a row's live numbers keep moving while the summary badges above them freeze at whatever they were before the tab was hidden, and staleTime: Infinity blocks refetchOnWindowFocus from closing that gap on return. Before this PR the client-side recompute this branch removed happened to paper over the same underlying gap; now that it's gone, the gap is directly visible. refetchOnWindowFocus: 'always' forces exactly one refetch on refocus, ignoring staleTime, without touching the interval/staleTime pairing that governs the rest of this query's behavior. Separately, useInbounds.ts still referenced computeClientsSummary by name in a comment explaining bucket priority; that function no longer exists after this PR. Dropped the comment rather than repoint it, per the repo's no-//-comment convention.
  • d05e44e401 fix(outbound): import Hysteria2 salamander properly from standard obfs params (#6166) * fix(outbound): import Hysteria2 salamander from standard obfs params The outbound share-link importers only reconstructed salamander from the private fm=<json> finalmask dump. Every standard Hysteria2 link — and this panel's own generator (internal/sub) since it stopped emitting fm= — carries the obfuscation as the standard obfs=salamander & obfs-password=<pw> pair, which the importers ignored. As a result, importing a normal Hysteria2 link (pasted into the outbound form or pulled from a subscription) silently dropped the salamander config and produced an outbound that negotiates plain QUIC against a server expecting obfuscation. Parse the standard obfs/obfs-password pair in both the Go importer (internal/util/link, used by subscription + JSON import) and the frontend form parser (outbound-link-parser.ts), folding it into finalmask.udp. A salamander mask already supplied via fm= still wins, so 3x-ui→3x-ui links are unchanged. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(outbound): address review — mport hop, password-less fm mask, tests Follow-up to the automated PR review on #6166: - Import the Hysteria2 UDP port-hopping range from the standard `mport` param (finalmask.quicParams.udpHop.ports) in both importers — the same class of gap as salamander: the subscription generator emits `mport` standalone and no `fm=`, so port hopping was silently lost on import. An `fm=`-supplied udpHop still wins. - When `fm=` carries a salamander mask without a usable password, fill it in from the obfs pair instead of treating the empty mask as authoritative (would otherwise enable obfuscation with an empty password). - Trim the duplicated rationale comments to two lines each. - Tests: collapse the four per-case Go functions into table-driven subtests; cover the obfs_password/obfsPassword aliases, case-insensitive obfs value, append-onto-non-salamander-udp, password-less-fm fill, and the mport paths; assert the fm-wins masks stay length 1 in both suites. Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: Sanaei <[email protected]>
  • 9165ab67eb fix(install): preserve custom bin/ files (e.g. hand-added geoip) across updates (#6152) * fix(install): preserve custom bin/ files (e.g. hand-added geoip) across updates Every reinstall/update wipes /usr/local/x-ui/ wholesale and re-extracts the release tarball, which only ships known assets (xray/mtg binaries, the bundled geoip*/geosite*.dat sets). A user-reported real incident: a hand-placed custom geoip file referenced from a routing rule via "ext:<file>:<code>" got silently deleted on update, and Xray refused to start at all afterward ("failed to open <file>: no such file or directory"), taking down every inbound until the file was manually restored from the user's own backup. install_x-ui now backs up the old bin/ before the wipe and restores, after extraction, only the files the fresh release doesn't provide -- bundled assets still get the newer per-release copy, nothing custom silently disappears. Verified in isolation: standard files (geoip.dat, the xray binary) end up as the fresh release's copy; a custom file absent from the release survives untouched. Co-Authored-By: Claude Sonnet 5 <[email protected]> * fix: harden the bin/ snapshot-and-restore against the review round on #6152 - Replace the mktemp+cp snapshot with a same-filesystem mv of bin/ aside: an unchecked mktemp failure previously made the very next line copy bin/'s contents into "/" (empty custom_bin_backup + trailing slash), and a silently-ignored cp failure (stderr redirected, exit code never checked) could leave a truncated custom geo file that gets "restored" as if it were intact. A rename is atomic and needs no extra disk space, removing both failure modes at once; if it fails, back off cleanly and say so instead of proceeding as if a backup exists. - Add a trap so an interrupted update (Ctrl-C, signal) between the backup and the restore doesn't leave the snapshot (which contains bin/config.json and every mtproto client's FakeTLS secret) sitting around indefinitely; the two exit-path cleanups this replaces are gone since the trap now covers those exits too. - Move the restore below the arm arch-rename/chmod block instead of before it, so xray-linux-arm32/mtg-linux-arm already exist under their final names and don't get needlessly restored-then-overwritten and misreported as "custom". - Exclude bin/config.json and bin/mtproto/*.toml from the restore: those are the panel's own generated runtime state (internal/xray/process.go, internal/mtproto/manager.go), not admin-placed files, and restoring a stale one only resurrects dead state or recreates bin/mtproto/ with the wrong (more permissive) directory mode. - Match symlinks in the restore's find, not just plain files -- cp -a already preserves them in the snapshot, but the restore loop was silently dropping them, which is exactly the failure mode (a geo file symlinked in from elsewhere) this PR set out to fix. - Quote the two new xui_folder expansions. - Extend the non-interactive smoke test to reinstall over an existing install with a sentinel file in bin/, asserting it survives and that the bundled geoip.dat is still the release's own copy -- the update path this PR touches had no CI coverage at all before this. Co-Authored-By: Claude Sonnet 5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5 <[email protected]>
  • Просмотр сравнение для этих 4 коммитов »

4 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

5 дней назад

txlyre синхронизированные коммиты с copilot/fix-review-comment-3774434337 на txlyre/3x-ui из зеркала

5 дней назад

txlyre синхронизированные новые ссылки copilot/fix-review-comment-3774434337 к txlyre/3x-ui из зеркала

5 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • 5b80d4562d chore(docs): bump docs dependencies Update fumadocs-core/mdx/ui to 16.14.3, lucide-react to 1.31.0, @types/node to 26.2.0, typescript-eslint to 8.67.0, esbuild to 0.28.2, shiki to 4.4.3, and various other transitive dependencies.

5 дней назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • e2f75acad2 chore(deps): bump github.com/klauspost/compress from 1.19.1 to 1.19.2 (#6212) Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.19.1 to 1.19.2. - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](https://github.com/klauspost/compress/compare/v1.19.1...v1.19.2) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.19.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • 69a8237581 fix(i18n): localize Chinese Xray labels (#6202) * fix(i18n): localize Traditional Chinese Xray labels Several navigation, outbound, balancer, VPN, and DNS labels still displayed their English source text in the zh-TW interface. Translate the non-protocol labels while retaining established Xray terminology. * fix(i18n): localize Simplified Chinese Xray labels Mirror the reviewed Xray UI coverage in zh-CN so the same labels no longer fall back to English there. Signed-off-by: 陳廷安 <73953029+[email protected]> --------- Signed-off-by: 陳廷安 <73953029+[email protected]> Co-authored-by: Sanaei <[email protected]>
  • f3f57e66f5 fix(frontend): wait out Collapse fade before a11y scan in ConfigBlock story Collapse animates opacity in over motionDurationMid; the Collapsed story's play function only waited for visibility, so the addon-a11y color-contrast check could sample a mid-fade, lower-contrast frame and fail flakily in CI. Wait for the panel's opacity to settle to 1 first.
  • 8a8da88548 fix(frontend): isolate swagger deps from main vendor chunk Keep swagger-ui-react and its transitive dependencies in the lazy swagger chunk so the initial panel bundle stays smaller. This avoids eager loading the OpenAPI UI on first paint while keeping the API docs route unchanged.
  • 1f846c3cb2 fix(frontend): clean test validation output
  • Просмотр сравнение для этих 7 коммитов »

6 дней назад

txlyre синхронизированные и удаленные ссылки dependabot/npm_and_yarn/frontend/npm_and_yarn-37951cc692 на txlyre/3x-ui из зеркала

6 дней назад

txlyre синхронизированные коммиты с dependabot/npm_and_yarn/frontend/npm_and_yarn-37951cc692 на txlyre/3x-ui из зеркала

  • e2ac7a85d3 chore(deps): bump dompurify Bumps the npm_and_yarn group with 1 update in the /frontend directory: [dompurify](https://github.com/cure53/DOMPurify). Updates `dompurify` from 3.4.12 to 3.4.13 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.4.12...3.4.13) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.4.13 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]>
  • ece1655939 fix(docs): prevent theme switch hydration mismatch
  • cb902314db fix(docs): restore theme switch without runtime warnings Move html/body shell and global css to root app layout to avoid hydration/script warnings from nested document nodes. Disable provider theme injection and add a custom script-free theme switch in shared layout slots. Also migrate docs search static client initializer to ZBSearch (initDB), add zbsearch dependency, and align docs lint tooling with ESLint 9 compatibility so npm run lint passes.
  • 7eacce6a46 chore(frontend): resolve the high-severity brace-expansion advisory (#6180) npm audit --omit=dev --audit-level=high is a CI gate and it currently fails on main: swagger-ui-react pulls @swagger-api/apidom-reference, which pins minimatch, which resolves brace-expansion to 5.0.8 — the range covered by GHSA-rgw5-rvv9-x895. Pin the patched 5.0.9 through the existing swagger-ui-react overrides block rather than globally: minimatch@3 under eslint-plugin-jsx-a11y still needs the 1.x line, and a blanket override would force v5 there too.
  • 199ddaf485 chore(deps-dev): bump brace-expansion (#6172) Bumps the npm_and_yarn group with 1 update in the /frontend directory: [brace-expansion](https://github.com/juliangruber/brace-expansion). Updates `brace-expansion` from 1.1.16 to 1.1.18 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.16...v1.1.18) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Просмотр сравнение для этих 10 коммитов »

1 неделя назад

txlyre синхронизированные новые ссылки dependabot/npm_and_yarn/frontend/npm_and_yarn-37951cc692 к txlyre/3x-ui из зеркала

1 неделя назад

txlyre синхронизированные коммиты с main на txlyre/3x-ui из зеркала

  • ece1655939 fix(docs): prevent theme switch hydration mismatch
  • cb902314db fix(docs): restore theme switch without runtime warnings Move html/body shell and global css to root app layout to avoid hydration/script warnings from nested document nodes. Disable provider theme injection and add a custom script-free theme switch in shared layout slots. Also migrate docs search static client initializer to ZBSearch (initDB), add zbsearch dependency, and align docs lint tooling with ESLint 9 compatibility so npm run lint passes.
  • 7eacce6a46 chore(frontend): resolve the high-severity brace-expansion advisory (#6180) npm audit --omit=dev --audit-level=high is a CI gate and it currently fails on main: swagger-ui-react pulls @swagger-api/apidom-reference, which pins minimatch, which resolves brace-expansion to 5.0.8 — the range covered by GHSA-rgw5-rvv9-x895. Pin the patched 5.0.9 through the existing swagger-ui-react overrides block rather than globally: minimatch@3 under eslint-plugin-jsx-a11y still needs the 1.x line, and a blanket override would force v5 there too.
  • 199ddaf485 chore(deps-dev): bump brace-expansion (#6172) Bumps the npm_and_yarn group with 1 update in the /frontend directory: [brace-expansion](https://github.com/juliangruber/brace-expansion). Updates `brace-expansion` from 1.1.16 to 1.1.18 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.16...v1.1.18) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • d142307366 chore(deps-dev): bump postcss (#6173) Bumps the npm_and_yarn group with 1 update in the /docs directory: [postcss](https://github.com/postcss/postcss). Updates `postcss` from 8.5.21 to 8.5.23 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.21...8.5.23) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.23 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Просмотр сравнение для этих 5 коммитов »

1 неделя назад

txlyre синхронизированные и удаленные ссылки dependabot/npm_and_yarn/frontend/npm_and_yarn-84557ebc70 на txlyre/3x-ui из зеркала

1 неделя назад

txlyre синхронизированные и удаленные ссылки dependabot/npm_and_yarn/docs/npm_and_yarn-6b7f7a8c69 на txlyre/3x-ui из зеркала

1 неделя назад