Skip to content

[pull] master from Ehco1996:master#312

Merged
pull[bot] merged 4 commits into
Kiterepo:masterfrom
Ehco1996:master
May 2, 2026
Merged

[pull] master from Ehco1996:master#312
pull[bot] merged 4 commits into
Kiterepo:masterfrom
Ehco1996:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 2, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

Ehco1996 and others added 4 commits May 2, 2026 14:32
* xray: replace gRPC stats/handler with in-process user mgmt + conn tracker

Drop the gRPC StatsService polling and HandlerService roundtrip: xray's
inbound.Manager is reached directly for AddUser/RemoveUser, and a custom
outbound replaces the default freedom outbound to count bytes per user
and register every dialed conn in a tracker. The tracker backs a small
admin API (GET/DELETE /api/v1/xray/conns) so the upstream can list and
kill live conns without going through xray.

Per-user upload/download counters become atomic and are snapshot-and-reset
on each upstream sync. The api inbound, policy.system stats, Stats and
OutboundConfigs blocks are stripped programmatically before core.New so
xray's stats accumulators don't run unused.

E2E tests cover trojan / vless / ss2022 over TCP, trojan / ss2022 over
UDP, and vless+REALITY — each verifies echo round-trip, tracker
registration, per-user counters, and KillByUser semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* xray: simplify outbound + user.go + e2e harness, wire test-e2e make target

- outbound.go: drop dead direction enum, fold the wrap helpers and the
  meteringWriter.Close indirection — counter wrap is now a single if-block
  in Dispatch.
- user.go: replace the hand-rolled containsLower with strings.Contains
  + strings.ToLower.
- e2e_test.go: collapse the per-protocol orchestration into a single
  runScenario that takes a serverCfg/clientFactory/runSession trio.
  TCP and UDP each have one shared session helper; the 6 tests are now
  ~12-line scenario constructions.
- Makefile: bump make test timeout to 3m for e2e headroom; add a
  test-e2e shortcut for fast iteration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix pricing

* xray: report access IP per user, fix outbound lint

- Make connEntry hold *session.Inbound/*session.Outbound directly so source IP is available without duplicating fields; expose SourceIP on the admin /xray/conns response
- Per-user FIFO IP set (cap 10) recorded in metered outbound and snapshotted alongside byte counters; populates UserTraffic.IPList
- Populate UserTraffic.TcpCount from connTracker.CountTCPByUser at sync time
- Fix errcheck lint: discard common.Interrupt return values in outbound.go

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: spurious xray reload + missing IPs for long-lived conns

- config: reset XRayConfig before re-decoding so xray-conf UnmarshalJSON
  methods (e.g. PortList.Range) don't accumulate state across reloads.
  Without this, the relay reloader's LoadConfig() mutates the shared
  Config, xray's needReload sees a phantom listener change and triggers
  a reload that kills all live conns.
- xray: merge live-conn source IPs from connTracker into the per-cycle
  IP list. RecordIP only fires once per Dispatch, so long-lived conns
  spanning multiple sync cycles previously only reported their IP in the
  first cycle.
- xray: log the sync payload JSON before POST to help diagnose upstream
  reporting issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: don't fail traffic sync when bandwidth fetch fails

UserPool's first sync runs synchronously inside xrayS.Start, where it
GETs the local /metrics/ endpoint for bandwidth. At boot the web server
hadn't started yet, so the fetch errored and the entire sync (including
the user traffic upload) was aborted.

- Reorder boot in MustStartComponents: start the web server goroutine
  before xrayS.Start so /metrics/ is reachable when the first sync hits.
- Demote bandwidth fetch errors to a warn and continue with bandwidth=0
  for that cycle. Bandwidth is a delta tracker; missing one tick is
  fine, but losing the user traffic upload is not.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: add CLAUDE.md with project conventions and gotchas

Captures non-obvious project knowledge for future Claude sessions:
boot order constraints, the shared-Config + xray-conf UnmarshalJSON
append bug pattern, xray/ package design (in-process bypass of gRPC,
stripUnused, user_id-as-email convention, reload semantics, per-cycle
reporting), and code/commit conventions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* web: redesign admin dashboard around DataTable + grouped nav

The previous UI rendered the full user list (498 rows in production) in
one pass with no pagination, no sort, no traffic-aware filtering, so the
signal (users actually pushing bytes) was buried under hundreds of
probe-noise rows. Overview also duplicated host metrics shown on the
Host page and left half the screen empty.

- DataTable primitive: sortable columns, 50/page pagination, density
  toggle, mdOnly columns for narrow screens. Replaces the per-page
  hand-rolled <table> + mobile card list duplication.
- Segmented control for filter chips and time-range pickers.
- Layout: split sidebar into Live (Overview/Users/Conns/Logs) and
  Config (Rules/Host/Settings); rename Node -> Host.
- Overview: triage landing page. Top users by recent throughput +
  recent connections, both click-through. Host metrics collapsed to
  a 4-stat strip linking to detail. Configuration & sync endpoint
  blocks moved to Settings.
- XrayUsers: default view is "Active" (users with traffic in the last
  5min OR live conns OR cumulative >1KB). Active/Running/All/Disabled
  segments with counts. Sortable by traffic, status, conn count.
- XrayConns: Flat / By user / By target view modes (URL-state). Group
  view exposes a per-user kill action.
- Rules: ported to DataTable, sortable by xfer / conns / ping.
- Logs: error-count pill in header, level segments, logger dropdown
  populated from observed frames.
- Settings: gains Runtime configuration + Sync endpoint cards (with
  copy-to-clipboard).

Removes the old ui/Table.tsx (no remaining refs) and the /node alias.

* web: fold Host into Overview, expose page-size + refresh-interval controls

- Drop the standalone Host page; its CPU/Mem/Net charts now live at the
  bottom of Overview with their own time-window picker. Removes /host
  route and the Host nav entry.
- DataTable footer is always visible and includes a page-size selector
  (25 / 50 / 100 / All), so users can pull the full list without
  hunting for a hidden control. Pagination chevrons only render when
  more than one page exists.
- New util/polling.ts + ui/RefreshPicker: a single hook drives the
  interval, persists the choice in localStorage, and skips ticks while
  the tab is hidden. Default poll interval bumped from 2-5s to 15s
  (Overview/Users) and 5s (Conns), with off/5s/15s/30s/1m options
  exposed in each page header.
- Replace hand-rolled setInterval/Pause-Play widgets in Overview /
  Users / Conns with the shared RefreshPicker.

* web: fix sign-in/sign-out by centralizing auth state

The previous flow had two latent bugs:

1. Sign-out called `clearToken()` then `location.reload()`. If the SPA
   was originally opened via a bookmarked `?token=...` URL, the reload
   re-executed the URL-token bootstrap path and immediately re-saved
   the token from the URL, so sign-out had no visible effect.
2. The boot probe in App.tsx treated any non-401 error (network blip,
   5xx) as "ok" and let the user past LoginGate. Subsequent API calls
   then failed silently and the dashboard looked broken with no way
   back to the login screen.

Move all auth state into store/auth.ts:

- `authState` signal ("checking" | "needed" | "ok") drives App.tsx.
- `probeAuth()` runs once on mount; treats 401/403 as needed and
  clears any stale token; treats other errors as needed too so the
  user always has a path back in.
- `signIn(token)` saves, probes, and resolves to an error message or
  null. LoginGate just awaits it.
- `signOut()` clears the token and flips state to "needed" without
  reloading the page, sidestepping the URL-token resurrection bug.

Drop the Access token card from Settings — sign out from the sidebar
already covers token rotation, and the redundant entry point made
the sessionStorage / signal split easy to misuse.

* web: support BasicAuth in the SPA login flow

Setups using web_auth_user / web_auth_pass were stuck: the browser's
native BasicAuth dialog handled login, the SPA never saw it, and
"Sign out" was a no-op because there's no JS API to clear the
browser's cached credentials. After my previous refactor the SPA's
LoginGate would never appear for these deployments either — it only
asked for a token.

Switch to fully SPA-owned auth UX:

- Replace echo's middleware.BasicAuthWithConfig with a custom one
  that returns a plain 401 (no WWW-Authenticate). The browser no
  longer pops its native dialog, so the SPA can render its own form
  and signOut() actually has somewhere to clear creds back to.
- New public GET /api/v1/auth/info advertises which schemes the
  server enforces; LoginGate uses it to render token / user+pass /
  both.
- store/auth: track {token, user, pass}; persist all three; expose
  authInfo so LoginGate knows which fields to show.
- api/client: send Authorization: Basic when user/pass are set, in
  addition to the existing ?token= query param. WS still uses the
  token (browsers can't attach Authorization headers to WS upgrades
  reliably; out-of-scope for this fix).

* web: hide sign-out and surface real auth status when no auth is set

Running ehco without web_token / web_auth_user / web_auth_pass leaves
every admin endpoint open. The dashboard still rendered a Sign out
button, which was confusing: clicking it cleared (empty) creds and
flipped to the LoginGate, but the next probe immediately succeeded
without any credentials and bounced the user straight back.

Also reword the Settings copy that hardcoded "?token= when web_token
is set" — that's only one of three possible states now.

- Layout: gate Sign out (sidebar + mobile sheet) on
  authInfo.token || authInfo.basic.
- Settings runtime card: add an "auth" row showing none / token /
  basic / "basic + token".
- Settings API surface footer: drive the requirement string off the
  real authInfo signal; if neither scheme is enabled, say so plainly.
@pull pull Bot locked and limited conversation to collaborators May 2, 2026
@pull pull Bot added the ⤵️ pull label May 2, 2026
@pull pull Bot merged commit 876bf65 into Kiterepo:master May 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant