mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-26 07:44:59 +03:00
56ac8d0fc7c5df9c3bc84cc933967a1a493521bc
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
56ac8d0fc7 |
feat(amneziawg): add opt-in Linux kernel datapath (#6877)
* feat(amneziawg): add opt-in Linux kernel datapath Add the kernel backend as an explicit per-inbound option while keeping the embedded backend as the default. Include host preflight, UI/API support, the bundled installer CLI, lifecycle and accounting integration, regression tests, and upstream-facing documentation. Kernel mode is NAT-only and rejects unsupported routing, IPv6, forwarding, and pinned-listen configurations. Local saves check the local host; remote nodes check their own host during apply. Preserve adopted interface accounting across panel restarts, retain manager state after failed teardown so the job can retry, prevent embedded startup while a kernel interface remains live, and prune collector state for removed peers. Tested the restart accounting path on Debian 13 with XanMod 7.1.11 on x86_64; Docker and cross-kernel coverage are not claimed. * fix(amneziawg): align readiness and endpoint reporting Expire endpoint sightings using the latest handshake and sweep stale addresses for the polled interface. Keep the readiness API schema, generated client types, and ready-host empty blocker list consistent; watch the nested form field and defer local readiness checks for remote targets. Document safe rollback before using a build without kernel support. Regression evidence: the idle endpoint test failed before the collector change because the old address remained visible after 31 minutes; endpoint tests pass after the fix. The form component suite passes 3 tests; restoring the invalid array watch made its off-mode severity test fail, while the corrected dot-path watch passes. Targeted Go regressions across amneziawgkern, job, and service pass. * style(amneziawg): format readiness test * test: reproduce kernel AWG client validation gaps * test(service): cover kernel AWG shared client fanout * fix(amneziawg): validate kernel client fanout and config values * test(amneziawgkern): cover owned interface lifecycle recovery * fix(amneziawgkern): recover owned interfaces on restart * test(amneziawgkern): retain visibility after recovered cleanup failure * fix(amneziawgkern): report recovered interfaces while cleanup retries * style(amneziawgkern): satisfy static analysis * test(amneziawgkern): cover long rendered peer comments * fix(amneziawgkern): accept long generated config comments * test(amneziawgkern): refuse foreign occupied interfaces Capture the current Ensure ordering failure with real temporary config paths and manager side-effect boundaries. * test(amneziawgkern): cover stale interface collisions Extend the Ensure regression across absent links, mismatched live interfaces, cached manager state, and Remove teardown. * fix(amneziawgkern): verify interface ownership before mutation Refuse to replace or tear down links unless the canonical config is a valid panel-owned file and its key matches the live interface. Preserve normal adoption and key rotation, and document migration of legacy unmarked configs. Validation: go test ./internal/amneziawgkern -count=1; go test -race ./internal/amneziawgkern -count=1; golangci-lint run ./internal/amneziawgkern/...; git diff --check. * build: update Go toolchain to 1.27.3 * test(amneziawg): reproduce unsafe key rotation recovery Keep the previous owned config authoritative until teardown succeeds, and require fresh managers to rebuild structural key changes. These regressions expose the stuck live-key/disk-key mismatch after teardown failure. * fix(amneziawg): recover interrupted key rotations Keep the old owned config on disk until a structural rebuild has confirmed the previous interface is down. Fresh managers only adopt when the live config and desired structural identity match, so retries and removals retain the key needed to prove ownership. * fix(scanner): align Go image with module version * fix(docs): pin pnpm release with bootstrap fix pnpm 12.9.1 restores a Node launcher before its postinstall replaces the target with ELF; pnpm 11 then runs that binary as JavaScript. Pin 12.10.0, which includes the shim fix, so the docs CI bootstrap can switch versions safely. * fix(xray): build core from pinned module The configured Xray release asset tag is unpublished, and the latest published binary lacks GetUsersStats used by the panel. Build the packaged core from the exact Xray module version already selected in go.mod for Docker and release targets. * fix(tests): stabilize local network fixtures Keep the TUIC stream fragmentation check on a working UDP loopback path, allocate the AmneziaWG fixture ports dynamically, and wait for a real Xray stats response before starting API mutation assertions. Recreate failed readiness channels so an early connection cannot stall the probe. Verified host and offline parallel Go suites, repeated occupied-port and UDP tests, race/shuffle runs, and golangci-lint. Production behavior and dependency versions are unchanged. * fix(i18n): add the kernel-mode AmneziaWG keys to sk-SK main gained the sk-SK locale after this branch added its kernel-mode keys to the other 13, so i18n-dead-keys.test.ts failed on the merged tree. * refactor(amneziawgkern): apply kernel interfaces with direct commands, no awg-quick awg-quick evaluates PostUp/PostDown through a root shell, so every value the panel rendered into its config was one validation slip away from root command execution. The interface is now built with direct ip/awg/nft execs from a setconf-only file: link add, awg setconf, address, mtu, an atomically replaced nftables table per inbound, and the IPv4 forwarding sysctl written directly. - Panel-owned names and paths: xui-awg<id> interfaces, xui_awg<id> NAT tables and configs under the panel's data dir, so nothing collides with an operator's own awg0/awg1 awg-quick tunnels in /etc/amnezia/amneziawg. - FORWARD accepts (comment 3x-ui:xui-awg<id>) when iptables is present: Docker and ufw set the FORWARD policy to DROP, which silently black-holed the tunnel. - A full panel stop records the final counters and tears the interfaces down, like the embedded datapath; a stopped panel enforces no quota or expiry. Crash survivors are still adopted without a rebuild. - The job reads counters before reconciling, and from every managed interface, so a client disabled for quota, or a removed inbound, is charged for its last interval instead of losing it with the dropped peer. - An unchanged reconcile tick forks no process (it probed the live key with `awg show` every 10s); readiness no longer loads the module from a GET. Verified on the real module (WSL2 6.6 kernel, v3.1.20260906): create, peer and egress changes in place, SIGKILL adoption with the same ifindex, SIGTERM teardown leaving no link, table, FORWARD rule or config behind. * fix(amneziawg): gate kernel host readiness only when a save starts the datapath Every save of a kernel-mode inbound re-ran the host preflight, so a host that lost its module (a kernel upgrade without a DKMS rebuild) refused even disabling the inbound or switching it back to embedded mode. Host blockers now apply only when the save starts the kernel datapath: a new inbound, a switch from embedded, or enabling a disabled one. Feature blockers (IPv6, forwarded ports, pinned listen) still apply to every save. * feat(nodes): refuse kernel-mode AmneziaWG pushes to nodes that predate it A node built before kernel mode drops the unknown kernelMode field and serves the inbound on the embedded datapath while the master's form shows kernel mode on. Nodes now advertise awg-kernel in X-3x-Node-Caps, and Remote refuses to push a kernel-mode inbound to a node that has not advertised it, learning the caps with an inbound list first when this Remote has not heard from it. * fix(amneziawg): pin the kernel module installer to verified commits The installer ran as root and built whatever upstream tag sorted newest at install time. It now clones fixed tags (module v3.1.20260906, tools v3.1.20260812) and refuses unless each resolves to its recorded commit. The udp_tunnel perl patch is gone: those releases carry the kernel 7.1.5 compat upstream, so the patch never matched. * fix(i18n): translate the kernel-mode AmneziaWG strings All thirteen non-English locales carried the English text verbatim. * fix(amneziawg): skip the kernel traffic flush when no kernel interface runs Shutdown listed the desired AmneziaWG inbounds from the database even on a panel with no kernel interface, an unneeded query on every stop. A server stopped before its database was opened panicked on it, which turned TestServerStopWhenXrayNotRunningSucceeds red whenever it ran first. --------- Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com> |
||
|
|
a8d0700bf2 |
fix(auth): add node-admin token scope for panel updates (#6666)
* feat(auth): add node-admin API token scope Keep ordinary node-sync credentials unable to self-update a remote panel while allowing explicitly elevated node administrators to inherit synchronization access and invoke updatePanel. * style(docs): format API scope table * fix(auth): preserve mTLS node-sync semantics Allow only an explicit node-admin bearer to elevate a verified mTLS request. Existing admin, monitor, and node-sync bearer values continue to use the historical node-sync identity on mTLS connections. * chore(ci): rerun flaky checks --------- Co-authored-by: libmur-dev <333915961+libmur-dev@users.noreply.github.com> |
||
|
|
2ffc694a6d |
fix(nodes): let a master's push and reconcile converge node inbounds
Invariant: every inbound the master holds for a node, with its clients and
enable flag, converges onto that node at the next push or reconcile.
|
||
|
|
169cd86e00 |
fix(sub): never use X-Real-IP as the subscription host (#6608)
ResolveRequest and the panel's resolveHost fell back to X-Real-IP for the host when a trusted proxy sent no X-Forwarded-Host. X-Real-IP names the visitor, so behind nginx with only that header set, subscription and exported links advertised the subscriber's own public IP as the server. The host now comes from a trusted X-Forwarded-Host, else the dialed request Host. X-Real-IP stays a client-IP source only. Fixes #6589. |
||
|
|
51e0afdd90 |
fix(inbounds): allow negative subSortIndex for subscription order (#6465)
* fix(inbounds): allow negative subSortIndex for subscription order Preserve explicitly set negative indices so primary inbounds can sort ahead of the default without renumbering peers; keep 0/omitted → 1. * fix(inbounds): gofumpt model.go and trim subSortIndex comments --------- Co-authored-by: mrchatam <287639636+mrchatam@users.noreply.github.com> |
||
|
|
47d2303334 |
fix(inbounds): reject missing TLS certificates before saving (#6429)
An inbound could be saved with security "tls" and a certificate row carrying neither a file path nor inline content. Nothing rejected it, so the row reached xray-core, whose readFileOrString fails with "both file and bytes are empty" and takes the whole config build down with it — every other inbound included. Validate the credentials on both sides of the wire. validateInboundTLSCertificates follows xray's file-over-inline precedence, requires a private key for every non-verify certificate and insists on at least one server certificate, so a verify-only CA list no longer passes as a server config. The inbound form's Zod schema enforces the same rules per field and serializes only the editor mode the operator actually used, and a failed save jumps to the Security tab naming the certificate row that broke. On update the guard is scoped to a real TLS edit. A row already stored incomplete is grandfathered: it stays editable, and only a save that breaks a previously valid block is refused. A sub-node stores whatever the master pushes, and Remote.UpdateInbound falls back to AddInbound when the node does not yet hold the tag, so a grandfathered row could otherwise never be deployed or re-seeded — the rejection is swallowed to a logger.Debug line and the node stays on a stale config while the panel shows the client as cut off. The controller now marks a node-sync request (mTLS or a node-sync token) on a per-request copy of InboundService, and the guard steps aside for it on both add and update: the row was judged where the operator acted, and a node that refuses it only falls out of sync. Operator and admin-token saves are held to the guard as before. The security union is parameterised on its tlsSettings branch instead of copied, and tlsCertUsesFiles is the one file-vs-inline inference shared by the form schema and the adapter, so the mode the editor opens in and the pair of fields the save serializes cannot drift apart. |
||
|
|
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> |
||
|
|
fa1a19c03c |
style: adopt golangci-lint v2 and resolve all findings
Add .golangci.yml (v2): the standard linters plus bodyclose, errorlint, noctx, misspell, rowserrcheck, sqlclosecheck, unconvert, usestdlibvars, with gofumpt + goimports formatters. Enable the std-error-handling exclusion preset for idiomatic Close/Remove/Setenv ignores; scope-exclude SA1019 (parser.ParseDir in tools/openapigen) and ST1005 (intentional capitalized user-facing error copy that tests assert verbatim). No inline nolint directives were introduced. Resolve all 217 findings behavior-preserving: gofumpt/goimports formatting, explicit blank assignment on intentionally ignored errors, errors.Is/errors.As and %w wrapping, context-aware stdlib calls (CommandContext/QueryContext/NewRequestWithContext/Dialer), staticcheck simplifications, removed redundant conversions, http.StatusOK and http.MethodGet, inlined the go:fix intPtr helper, and deferred sql rows Close. Add a golangci CI job mirroring the existing Go jobs. |
||
|
|
439245d42b |
feat(inbounds): apply remark template to Export all inbound links
Export-all now renders links through the subscription engine via a new GET /panel/api/inbounds/allLinks endpoint, so the configured remark template (name-only display part) is applied per client -- matching the client info/QR pages. Previously it generated links client-side with a hardcoded inbound-email remark. Host-aware: managed Host endpoints win over the plain link, so HOST and per-host variants render; duplicate client JSON entries are deduped by email and the list is scoped to the logged-in user. |
||
|
|
58905d81a4 |
feat(node-sync): push global client usage to nodes for display and local enforcement
A client attached to several panels has one aggregated row on each master, but a node only ever saw its local share: the node UI under-reported usage, and the node kept serving a client whose cross-panel total had already exceeded its quota — the master's disable push doesn't kill established connections unless the node restarts xray itself. Masters now push their aggregated per-client counters to each node from NodeTrafficSyncJob (throttled, scoped to the clients that node hosts). The node stores them in the new client_global_traffics side table keyed by (masterGuid, email), overwritten on every push so a master-side reset propagates, and: - overlays max(local, pushed) onto UI read paths (slim inbound list, inbound detail, clients list, WS stats, per-email lookups). The full /panel/api/inbounds/list stays un-overlaid on purpose: it doubles as the traffic snapshot masters poll, and overlaying it would corrupt every master's delta accounting; - trips disableInvalidClients when any master's pushed total exceeds the client's quota, so the existing RestartXrayOnClientDisable flow disconnects the client locally; - clears the side rows on traffic reset, auto-renew, and client delete, keeping a renewed quota window clean. Supersedes #5204, which folded pushed globals into client_traffics and compensated with read-back baselines — that double-counted first-sight emails and could not work with several masters sharing one node. |
||
|
|
41645255f1 |
refactor: focused service files, leaf subpackages, and an internal/ layout (#5167)
* refactor(service): split client.go into focused files
client.go had grown to 4455 lines mixing ~10 responsibilities. Split it
verbatim into cohesive same-package files (no behavior change):
client.go foundation: ClientService, ClientWithAttachments,
ClientCreatePayload, ErrClientNotInInbound, sqlInChunk
client_locks.go inbound mutation locks, delete tombstones, compactOrphans
client_lookup.go read-only lookups (GetByID, List, EffectiveFlow, ...)
client_link.go inbound association sync (SyncInbound, DetachInbound, ...)
client_crud.go single-client CRUD + validation + protocol defaults
client_inbound_apply.go low-level inbound-settings mutators + by-email setters
client_bulk.go bulk attach/detach/adjust/delete/create + DelDepleted
client_traffic.go traffic-reset paths
client_groups.go client group management
client_paging.go paged listing, filtering, sorting, summary
Every declaration moved unchanged (verified: identical func/type/const/var
signature set before vs after). Imports redistributed per file via goimports.
go build ./..., go vet, and go test ./web/service/... all pass.
* refactor(service): split inbound.go into focused files
inbound.go was 4100 lines. Split it verbatim into cohesive same-package
files (no behavior change):
inbound.go core inbound CRUD + InboundService (keeps pkg doc)
inbound_protocol.go protocol / stream capability helpers
inbound_node.go node/runtime/remote coordination + online tracking
inbound_traffic.go traffic accounting, reset, client stats
inbound_client_ips.go per-client IP tracking
inbound_clients.go client lookups within inbounds + copy-clients
inbound_disable.go auto-disable invalid inbounds/clients
inbound_migration.go DB migrations
inbound_sublink.go subscription link providers
inbound_util.go generic slice/string helpers
Identical func/type/const/var signature set before vs after; package doc
comment preserved on inbound.go. Imports redistributed via goimports.
Build, vet, and go test ./web/service/... all pass.
* refactor(service): split tgbot.go into focused files
tgbot.go was 3738 lines dominated by a 1246-line answerCallback. Split it
verbatim into cohesive same-package files (no behavior change):
tgbot.go lifecycle, bot setup, caches, small utils
tgbot_router.go incoming update / command / callback dispatch
tgbot_send.go outbound messaging primitives
tgbot_client.go client views, actions, subscription links
tgbot_inbound.go inbound listing / pickers
tgbot_report.go server usage, exhausted, online, backups, notifications
Identical func/type/const/var signature set before vs after. Imports
redistributed via goimports. Build, vet, and go test ./web/service/... pass.
* refactor(client): dedupe single-field by-email setters
ResetClientIpLimitByEmail, ResetClientExpiryTimeByEmail, and
ResetClientTrafficLimitByEmail shared an identical ~50-line body that
resolves the inbound by email, confirms the client exists, rewrites a
single-client settings payload, and delegates to UpdateInboundClient.
Extract that into applyClientFieldByEmail(inboundSvc, email, mutate) and
reduce each setter to a 3-line wrapper. Behavior is unchanged: same checks
and error strings, same single-client payload contract, same totalGB guard.
SetClientTelegramUserID (resolves by traffic id, different error text) and
ToggleClientEnableByEmail/SetClientEnableByEmail (different return shape and
a pre-read of the old state) intentionally keep their own bodies.
* refactor(service): extract panel/ subpackage
Move the panel-administration leaf services out of the flat service
package into web/service/panel/ (package panel):
user.go UserService (auth / 2FA / LDAP)
panel.go PanelService (restart / self-update) + version helpers
panel_other.go non-unix RestartPanel
panel_unix.go unix RestartPanel
api_token.go ApiTokenService
websocket.go WebSocketService
panel_test.go version/shellQuote unit tests
These are leaves: they depend on core (SettingService, Release) but no
core file references them, so the extraction creates no import cycle.
Core references are now qualified (service.SettingService, service.Release);
callers in main.go, web/web.go, and web/controller/* updated to panel.*.
Build, vet, and go test ./web/... pass.
* refactor(service): extract integration/ subpackage
Move the external-provider integration leaves into web/service/integration/
(package integration):
warp.go WarpService (Cloudflare WARP)
nord.go NordService (NordVPN)
custom_geo.go CustomGeoService (custom geo asset management)
*_test.go custom_geo / panel-proxy tests
These depend on core (SettingService, ServerService, XraySettingService) but
no core file references them. xray_setting.go stays in core because it calls
the unexported SettingService.saveSetting. The shared isBlockedIP SSRF helper
(used by core url_safety.go and by custom_geo) now has a small copy in each
package rather than being exported. Core references qualified; callers in
web/web.go, web/job/*, and web/controller/* updated to integration.*.
Build, vet, and go test ./web/... pass.
* refactor(service): extract tgbot/ subpackage
Move the Telegram bot (6 files + test) into web/service/tgbot/ (package
tgbot). It is a leaf: it embeds five core services (Inbound/Client/Setting/
Server/Xray) and the core never references it, so no import cycle.
To support the package boundary without changing behavior:
- core exposes XrayProcess() *xray.Process so tgbot keeps calling the
exact same running-process methods it used via the package-level `p`;
- three core methods tgbot calls are exported: ClientService.checkIs-
EnabledByEmail -> CheckIsEnabledByEmail, InboundService.getAllEmails ->
GetAllEmails (callers updated in-package);
- tgbot's embedded-field types and the few core type refs (Status,
ClientCreatePayload, SanitizePublicHTTPURL) are now service-qualified.
Callers in main.go, web/web.go, web/job/*, and web/controller/* updated to
tgbot.*. Build, vet, and go test ./web/... pass.
* refactor(service): extract outbound/ subpackage
OutboundService (outbound.go) imports only neutral packages (config,
database, model, xray) and its production code is referenced by no core or
sibling service file — only by web/controller/xray_setting.go and
web/job/xray_traffic_job.go. Move it to web/service/outbound/ (package
outbound); no core qualification needed inside. Callers updated to outbound.*.
The one coupling was a tiny pure test helper, outboundsContainTag, used by
both outbound.go and the core outbound_subscription_test.go; it now has a
small copy in that test file rather than being shared across the boundary.
Build, vet, and go test ./web/... pass.
* refactor(util): move wireguard into its own subpackage
util/wireguard.go was the lone file of the root `util` package (24 lines,
one exported func GenerateWireguardKeypair), while every other util concern
lives in a focused subpackage (util/common, util/crypto, util/netsafe, ...).
Move it to util/wireguard/ (package wireguard) for consistency; its only
importer, web/service/integration/warp.go, is updated. The root `util`
package no longer exists.
* refactor(sub): drop redundant sub prefix from filenames
Inside package sub the subXxx.go prefix just repeats the package name
(like client_*.go did inside service). Rename for consistency; content and
type names are unchanged:
subController.go -> controller.go
subService.go -> service.go
subClashService.go -> clash_service.go
subJsonService.go -> json_service.go
(+ matching _test.go files)
* refactor(controller): rename xui.go -> spa.go
XUIController serves the panel's single-page-app shell; spa.go names that
role plainly (the other controller files are domain-named). File rename only
— the type stays XUIController. api_docs_test.go keys route base paths by
filename, so its "xui.go" case is updated to "spa.go".
* refactor: move backend packages under internal/
Adopt the idiomatic Go application layout: the backend packages now live
under internal/ (a boundary the toolchain enforces), signalling private
implementation instead of a library-style flat root. No runtime behavior
changes — only import paths and a few build/config paths move.
Moved: config, database, logger, mtproto, sub, util, web, xray -> internal/.
main.go stays at the repo root and tools/openapigen stays under tools/ (both
still import internal/* because the internal rule keys off the module root).
The module path github.com/mhsanaei/3x-ui/v3 is unchanged; 149 .go files had
their import prefix rewritten to .../internal/<pkg>.
Couplings the Go compiler can't see, updated to the new layout:
- frontend i18n imports of web/translation (react.ts, setup.components.ts)
- vite outDir + eslint/tsconfig ignore globs -> internal/web/dist
- Dockerfile COPY paths for web/dist and web/translation
- locale.go os.DirFS("web") disk fallback -> "internal/web"
- .gitignore and ci.yml go:embed stub for internal/web/dist
- api_docs_test.go repo-root relative walk (one level deeper)
- tools/openapigen filesystem package paths; ApiTokenView repointed to the
web/service/panel subpackage and codegen regenerated (clears a stale
type the ci.yml codegen check was failing on)
Verified: go build/vet/test (all packages), and frontend typecheck, lint,
vitest (478 tests), and production build into internal/web/dist.
* fix(config): keep test runs from writing logs into the source tree
GetLogFolder() returns a CWD-relative "./log" on Windows. Under `go test`
the working directory is each package's own folder, so InitLogger (called by
tests in web/job, web/service, xray, web/websocket) created stray log/
directories scattered through the source tree (e.g. internal/web/job/log/).
Redirect to a shared temp folder when testing.Testing() reports a test run.
Production behavior is unchanged: Windows still uses ./log next to the binary
and Linux /var/log/x-ui. The log files were always gitignored (*.log) and
never committed; this just stops the noise at the source.
* docs: move subscription-template guide out of root into docs/
sub_templates/ was a top-level folder holding only a README and no actual
templates (3x-ui ships none by design), referenced nowhere and unlinked from
any doc — it read like an empty placeholder cluttering the repo root.
Move the guide to docs/custom-subscription-templates.md (a proper docs home),
reword its intro to read as documentation rather than a folder note, link it
from the Features list in README.md, and drop the empty sub_templates/ folder.
* fix: update stale web/ path references after the internal/ move
The internal/ migration rewrote Go import paths but left some references to
the old top-level layout in docs, comments, and a few runtime disk paths.
Functional (dev-mode only): the disk-serving fallbacks that read the Vite
build from disk when running from source still pointed at web/dist/, which
moved to internal/web/dist/ — so `os.DirFS`/`os.Stat`/`os.ReadFile` in
internal/web/web.go and internal/sub/{sub,controller}.go are corrected.
Production was unaffected (it serves the embedded FS; verified by the Docker
build), but `go run` with a live frontend build silently fell back to embed.
Docs/comments: frontend/README.md, CONTRIBUTING.md, the claude-issue-bot and
release workflows, the openapigen -root help text, and assorted Go comments
now reference internal/web, internal/database, internal/sub, internal/xray,
etc. Package-name mentions (the "web" package), root paths (main.go,
frontend/, install scripts, /etc/x-ui), routes (/panel/api/xray), and the
historical "web/assets no longer exists" note were intentionally left as-is.
* refactor(web): remove the legacy /xui -> /panel redirect middleware
RedirectMiddleware existed only for backward compatibility with the old
`/xui` URL scheme (301-redirecting /xui and /xui/API to /panel and
/panel/api). That cutover was long ago, so drop the middleware, its
registration in initRouter, and the now-inaccurate "URL redirection"
mention in the middleware package doc. Old /xui URLs now 404 like any other
unknown path. HTTPS auto-redirect and auth redirects are unrelated and stay.
* build: fix .dockerignore for internal/ layout and exclude runtime dir
- web/dist -> internal/web/dist: the embedded frontend moved under internal/,
so the stale exclude no longer matched and the locally-built dist could be
sent to the build context (the frontend stage rebuilds it fresh anyway).
- exclude x-ui/: the local runtime directory (SQLite db, geo .dat files, xray
binaries, certs — ~150MB) was being shipped into the build context for no
reason. Verified the pattern excludes only the directory and still keeps
x-ui.sh, which the Dockerfile copies to /usr/bin/x-ui.
|