Commit Graph

12 Commits

Author SHA1 Message Date
Masterain bd6a6aba43 feat(pia): add PIA login-and-add WireGuard outbounds (#6272)
* feat(pia): add login-and-add WireGuard outbounds (#2)

* fix(pia): keep PIA outbounds identifiable after the editor strips hostname

The outbound editor drops piaHostname, so last-segment matching failed for hyphenated servers. Identify rows by the computed tag, re-encrypt stored tokens onto the active key, skip unusable catalog rows, and always release the catalog refresh latch.
2026-08-22 23:11:06 +02:00
ilyusha af3e6c11b6 docs(api): document WireGuard and mtproto secret generation on clients/add (#6282)
* docs(api): document WireGuard and mtproto secret generation on clients/add

The POST /panel/api/clients/add summary enumerated the protocols whose
secrets the server fills in, and that list stopped being complete when
WireGuard gained per-client keys and mtproto gained a FakeTLS secret.
Read literally it says the endpoint is unusable for WireGuard without a
hand-made keypair and address, while defaultWireguardClients in fact
generates the keypair, derives the public key from a supplied private
one, and allocates a free /32.

Rather than extend an enumeration that goes stale on every new protocol,
the summary now states the rule alone and the per-protocol detail moves
into the operation description - a field Endpoint already declares and
build-openapi.mjs already maps, but that no endpoint used until now.
Swagger UI in the panel and the docs site both render it.

The attach operation gets the rule added for #5785 that nothing
documented: a client already carrying allowedIPs brings them into the
new inbound instead of being given a fresh address, and is rejected when
another client of that inbound holds it.

Closes #6276

* docs(api): correct the clients/add generation rules flagged in review

Three claims in the new description did not hold:

Shadowsocks does not keep every supplied password. fillProtocolDefaults
regenerates it when validShadowsocksClientKey rejects it, which on a
2022-blake3-* inbound means any password that does not base64-decode to
16 or 32 bytes - the call still returns success, so the caller has to
read the client back to notice. Split off from Trojan and spelled out.

The UUID is not always fresh: re-adding an email that already exists,
with the stored subId, reuses the stored id, password, auth and secret
so the identity stays in sync across its inbounds. That branch was
documented nowhere.

The mtproto secret falls back to www.cloudflare.com when the inbound
carries no fakeTlsDomain.
2026-08-22 20:37:39 +02:00
Kuzz007 1250fbb734 feat(clients): allow removing a single HWID device (#6265)
* feat(clients): allow removing a single HWID device

Only "list" and "clear all" existed for registered HWID devices, so
freeing one slot under a client's HWID limit meant clearing every
device and waiting for the ones you kept to re-register. Adds a
per-device delete: DELETE /panel/api/clients/hwids/:email/:id, scoped
to the client's own sub_id (device ids are a global auto-increment,
not per-subID, so this also prevents deleting another client's
device), plus a delete button next to each device in the existing
HWID modal.

Addresses MHSanaei/3x-ui#6245.

* feat(clients): surface HWID limit + device log in the client info card

Mirrors the existing IP-limit row/eye-icon-modal pattern that's
already in this card. The HWID devices modal reuses the same
list/clear-all/per-device-delete UI already shipped for the edit
form's own HWID modal, so a device can be removed without opening the
edit form at all.

* i18n: add HWID single-delete strings to all 13 locales

deleteHwid/deleteHwidConfirm/hwidDeleted were only added to en-US and
ru-RU in the previous commit; backfilling the other 11 locales the
project's own translation set covers.

* fix(clients): address automated review of HWID single-delete PR

- ClientInfoModal: use the existing dateLabel() helper (Jalali-aware)
  for HWID first/last-seen instead of a raw dayjs format, matching
  every other timestamp in the same modal.
- Add okText/cancelText to the delete-device Popconfirm in both
  ClientInfoModal and ClientFormModal so all 13 locales get a
  translated confirm dialog instead of Antd's English default.
- deleteHwid controller: stop reusing the success toast key on both
  error paths, which rendered a red "Update successful" toast on a
  real (not just theoretical) failure such as a stale HWID modal.
- Trim DeleteClientHwid's doc comment to the repo's 2-line cap and
  correct it: deletion is scoped by sub_id, which can span more than
  one ClientRecord, not strictly "this client only".
- Add TestDeleteClientHwid covering cross-sub_id id rejection, unknown
  id rejection, and a real successful delete.

* chore: retrigger CI (previous run stuck installing Playwright Chromium)

* fix(clients): address the arbiter review on the HWID single-delete PR

- Extract the HWID device list into a shared frontend/src/lib/clients/
  hwid-log.ts type/normalizer, a shared useClientHwids hook, and a
  shared ClientHwidListModal component, mirroring the existing IP-log
  pattern. ClientInfoModal and ClientFormModal both render the same
  component now, so the two copies can no longer drift the way they
  already had (different date formatting, different tag styles).
- Add a Popconfirm to the HWID "Clear all" button (previously
  unconfirmed, unlike the per-device delete right next to it) — closes
  the confirm/no-confirm asymmetry the review flagged as the main risk.
- Sync docs/public/openapi.json with the two hwids paths and regenerate
  clients.mdx. Scoped to just those two paths rather than a full copy
  from frontend/public/openapi.json: the docs copy is far enough behind
  on unrelated paths (a host-group API rename) that a full sync breaks
  the Next.js build on locale pages referencing the old shape — out of
  scope for this PR.

* fix(clients): trim HWID list comment blocks to 2 lines

Repo convention caps comment blocks at 2 lines; both were 1 line over.

* chore: retrigger CI

build (arm64) and build (armv6) failed on a transient Go module proxy
network error (INTERNAL_ERROR stream reset), unrelated to this PR's
changes.
2026-08-21 14:17:05 +02:00
Sanaei 3c087f6fd9 chore(docs): update dependencies and adapt to zbsearch 4
fumadocs-core 16.14.5 switched its search engine from Orama to zbsearch 4,
so the panel docs follow it up to the same major.

zbsearch 4 still rejects locale codes as tokenizer languages ("en" throws,
only "english" is accepted), so the custom search dialog that forces an
English index stays necessary — verified by loading the built static index
for all four locales and searching it through fumadocs' own client.

Around that:
- use `staticClient`, as `oramaStaticClient` is now a deprecated alias
- drop @orama/orama, which nothing depends on or imports any more
- correct the two comments that still described Orama and pointed at its
  docs and tokenizer package, one of them suggesting a language zbsearch
  does not have
- restore the corepack integrity hash on `packageManager`, which CI reads
  through pnpm/action-setup
- prune minimumReleaseAgeExclude entries for versions no longer installed

The API reference MDX changes are serialization-only: fumadocs-openapi
11.2.4 emits plain scalars where it used folded ones. Parsed frontmatter
and page bodies are unchanged.
2026-08-19 18:38:40 +02:00
n0ctal 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+n0ctal@users.noreply.github.com>
2026-08-15 21:48:24 +02:00
n0ctal 7ecd88b9e3 fix(nodes): apply a rotated master mTLS certificate without restarting the panel (#6194)
* fix(mtls): invalidate pooled clients after credential rotation

* fix(mtls): make connection reload read-only

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
2026-08-15 16:03:42 +02:00
n0ctal 1230559e69 feat(api): scoped, optionally expiring API tokens (#6201)
* security(api): add scoped expiring API tokens

* security(api): make scoped token lifecycle enforceable

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
2026-08-15 15:31:49 +02:00
PathGao ad288a7ecc fix(sub): honor trustedProxyCIDRs before forwarded URLs (#6135)
* fix(sub): honor trustedProxyCIDRs before forwarded URLs

* fix(sub): avoid unused trust-setting lookups

Skip the trustedProxyCIDRs lookup when no forwarded header can affect a subscription URL. Keep the shipped proxy default in one exported setting constant and document the subscription-link behavior for custom proxy boundaries.

* fix(frontend): meet config text contrast requirements

Keep compact configuration text readable in the light theme and satisfy the Storybook accessibility check.

---------

Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
2026-07-29 21:01:59 +02:00
Shichao Song 17e6b5a460 inbounds: allow custom monthly traffic reset days (#6071) 2026-07-28 23:27:09 +02:00
PathGao a2774bf212 fix(ui): explain the REALITY client version gate and drop the impossible placeholder (#6125)
* fix(ui): explain the REALITY client version gate and drop the impossible placeholder

An empty Min Client Ver looks unrestricted, but Xray-core silently
falls back to a built-in minimum (currently 26.3.27) that rejects
third-party cores such as Mihomo and sing-box with a bare REALITY
verification failure, and nothing in the panel points at the field.
Add tooltips to both version fields explaining the fallback and its
TLS-fingerprint-freshness rationale.

The Max Client Ver placeholder (25.9.11) sat below the built-in
minimum, so filling in both placeholders produced a range that
rejects every client. Remove it; empty genuinely means no upper
limit for that field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(reality): warn that an empty min client version rejects old cores

Common pitfalls covered bad targets, SNI mismatches, leaked keys and
wrong flow, but not the client version gate that currently bites
Mihomo and sing-box users. Add it to all four doc languages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): word the version hints against the effective minimum

Address the automated review: the Max Client Ver hint said only 'not
lower than Min Client Ver', which re-establishes the empty-means-unset
mental model when the effective floor is the core's built-in minimum.
Both hints now name the effective minimum and tie the quoted 26.3.27
to the core build the panel runs, since operators can install any
Xray-core version.

Also from review: full-width quotes and a missing verb in the zh doc
bullet, the idiomatic Arabic opening, and a format-only x.y.z
placeholder on Max Client Ver so the field still conveys its shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 22:08:39 +02:00
MHSanaei 814cda3fb4 feat(xray): update xray-core to v26.7.11 and adapt panel
Bump xtls/xray-core to 50231eaf (v26.7.11) and the three binary pins
(DockerInit.sh, release.yml x2) in lockstep.

Adapt the panel to the upstream changes:

- Shadowsocks "none"/"plain" and VMess "none"/"zero" were removed from
  the core. A migration rewrites stored none/plain SS methods to a
  supported cipher and none/zero VMess security to "auto" (on both the
  clients column and inbound settings JSON); the SS build-time heal does
  the same so a row injected after boot cannot brick startup. The removed
  values are dropped from every frontend option list, schema and adapter,
  and coerced to "auto" at the Go link/sub/Clash emit sites and both link
  importers. Fix the CipherType_NONE sentinel that no longer compiles.

- Unencrypted vless/trojan outbounds to a public address are now refused
  by the core. Validate outbounds through the vendored config loader when
  saving the xray template and when storing/merging outbound
  subscriptions, so one such outbound cannot keep the core from starting.

- New TCP finalmask type "xmc" (Minecraft mimicry): add it to the sub
  link allowlist, the frontend enum and the FinalMask form (hostname,
  usernames, required password), and document it.

- streamSettings gained a "method" alias for "network"; canonicalize it
  to "network" at inbound save time and in the form adapters/schema so a
  method-keyed config keeps its transport.

- New root "env" config key is passed through xray.Config, compared in
  Equals, and forces a restart in the hot diff.

- REALITY now defaults minClientVer to 26.3.27; update the form
  placeholder.
2026-07-12 00:30:47 +02:00
MHSanaei 9b91f0f42e docs: vendor the documentation site into the monorepo
Fold the standalone 3x-ui-docs project (Next.js 16 + Fumadocs, deployed to
docs.sanaei.dev) into docs/ so the panel and its documentation share a single
source of truth, the way sing-box keeps its docs in-tree. The old repo becomes
redundant and can be retired.

- Import the full site under docs/ (app, components, content, lib, public,
  scripts, config). The self-contained pnpm project sits alongside the existing
  engineering notes with no filename collisions.
- Re-point "Edit on GitHub" links from MHSanaei/3x-ui-docs to this repo's
  docs/content/docs path (docs/lib/shared.ts, docs/app/.../page.tsx).
- Add docs-ci.yml and docs-deploy.yml under .github/workflows/, scoped to
  docs/** and run with working-directory: docs, since GitHub only runs
  workflows from the repo-root .github/. deploy-static.yml's GitHub Pages
  publish (CNAME docs.sanaei.dev) carries over unchanged.

Follow-up (outside this commit): attach the docs.sanaei.dev custom domain to
this repository's Pages (or set the Vercel project's root directory to docs),
confirm the site is live from the monorepo, then delete MHSanaei/3x-ui-docs.
2026-07-07 23:07:14 +02:00