diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ea49667..a09ae517 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,33 +1,33 @@ # Contributing -Thanks for taking the time to contribute to 3x-ui. This guide gets a development panel running on your machine in a few minutes. +Thanks for taking the time to contribute to 3x-ui. This guide gets a development panel running locally and explains the conventions the project follows so changes land cleanly. ## Prerequisites -- **Go 1.26+** (the version in `go.mod`) -- **Node.js 22+** and npm (for the Vue frontend) +- **Go 1.26+** (the version pinned in `go.mod`) +- **Node.js 22+** and npm 10+ (for the React frontend) - **Git** -- **A C compiler** — required by the CGo SQLite driver (`github.com/mattn/go-sqlite3`). Linux/macOS already ship one; on Windows see below. +- **A C compiler** — required by the CGo SQLite driver (`github.com/mattn/go-sqlite3`). Linux and macOS already ship one; for Windows see below. ### Windows: MinGW-w64 -`go build` on Windows will fail with `cgo: C compiler "gcc" not found` until you install a GCC toolchain. Two options — pick whichever fits. +`go build` on Windows fails with `cgo: C compiler "gcc" not found` until a GCC toolchain is installed. Two options — pick whichever fits. **Option A — standalone zip (fastest, no package manager)** -1. Grab the latest build from ****. For most setups you want a release named like: +1. Download the latest build from . For most setups, pick a release named: ``` x86_64--release-posix-seh-ucrt-rt_-rev.7z ``` - (64-bit, POSIX threads, SEH exceptions, UCRT runtime — matches the modern Windows defaults.) + (64-bit, POSIX threads, SEH exceptions, UCRT runtime — matches modern Windows defaults.) 2. Extract it somewhere stable, e.g. `C:\mingw64\`. -3. Add `C:\mingw64\bin` to your **Windows** `PATH` (System Properties → Environment Variables → Path → New). +3. Add `C:\mingw64\bin` to the **Windows** `PATH` (System Properties → Environment Variables → Path → New). 4. Open a fresh terminal and confirm: ```powershell gcc --version ``` -**Option B — MSYS2 (if you also want a Unix-y shell)** +**Option B — MSYS2 (when a Unix shell is also useful)** 1. Install MSYS2 from . 2. Open the **MSYS2 UCRT64** shell from the Start menu and update once: @@ -38,14 +38,14 @@ Thanks for taking the time to contribute to 3x-ui. This guide gets a development ```bash pacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-pkg-config ``` -4. Add `C:\msys64\ucrt64\bin` to your Windows `PATH`. +4. Add `C:\msys64\ucrt64\bin` to the Windows `PATH`. 5. Verify with `gcc --version` in a fresh terminal. -After either, `go build ./...` and `go run .` work normally. +After either path, `go build ./...` and `go run .` work normally. -> Why MinGW-w64 over MSVC: `mattn/go-sqlite3` officially supports GCC, builds are faster on Windows, and the toolchain doesn't lock you into a Visual Studio install. If you already have Visual Studio Build Tools installed it works too — just make sure `CC=cl` is **not** set in your environment. +> **Why MinGW-w64 over MSVC:** `mattn/go-sqlite3` officially supports GCC, builds are faster on Windows, and the toolchain does not require a Visual Studio install. If Visual Studio Build Tools are already present that works too — just make sure `CC=cl` is **not** set in the environment. -The Linux SQLite cross-build from Windows (or vice versa) needs an extra cross-compiler — out of scope here; build natively on the target OS. +Cross-building the Linux SQLite target from Windows (or vice versa) requires a separate cross-compiler and is out of scope here; build natively on the target OS. ## First-time setup @@ -65,7 +65,7 @@ npm run build cd .. ``` -`.env.example` ships with sane defaults that point the database, logs, and xray binary at the local `x-ui/` folder so nothing escapes the project directory: +`.env.example` ships with defaults that keep the database, logs, and xray binary inside the local `x-ui/` folder so nothing escapes the project directory: ``` XUI_DEBUG=true @@ -74,7 +74,7 @@ XUI_LOG_FOLDER=x-ui XUI_BIN_FOLDER=x-ui ``` -You need to drop the xray binary (`xray-windows-amd64.exe` on Windows, `xray-linux-amd64` on Linux, etc.) plus the matching `geoip.dat` / `geosite.dat` files into `x-ui/`. The easiest source is a [released Xray-core build](https://github.com/XTLS/Xray-core/releases). On Windows you also want `wintun.dll` if you plan to test TUN inbounds. +Drop the xray binary (`xray-windows-amd64.exe` on Windows, `xray-linux-amd64` on Linux, etc.) plus the matching `geoip.dat` and `geosite.dat` files into `x-ui/`. The easiest source is a [released Xray-core build](https://github.com/XTLS/Xray-core/releases). On Windows, `wintun.dll` is also required for testing TUN inbounds. ## Running @@ -82,11 +82,11 @@ You need to drop the xray binary (`xray-windows-amd64.exe` on Windows, `xray-lin go run . ``` -Open [http://localhost:2053](http://localhost:2053) and log in with `admin` / `admin`. You will be prompted to change the credentials on first login. +Open [http://localhost:2053](http://localhost:2053) and log in with `admin` / `admin`. Credentials must be changed on first login. ### Inside VS Code -The repo ships a launch profile in `.vscode/launch.json` (gitignored — copy from the snippet below if it is missing): +The repo ships a launch profile in `.vscode/launch.json` (gitignored — copy from the snippet below if absent): ```jsonc { @@ -113,93 +113,97 @@ The repo ships a launch profile in `.vscode/launch.json` (gitignored — copy fr ## Working on the frontend -The panel UI is a Vue 3 + Ant Design Vue 4 app under `frontend/`. A few things worth knowing before you dive in. +The panel UI is a **React 19 + Ant Design 6 + TypeScript** app under `frontend/`, built with Vite 8. The sections below cover the architecture, the conventions, and the two dev workflows. -### Architecture in one paragraph +### Architecture -It's a **multi-page app**, not a SPA. Every panel route (`/panel`, `/panel/inbounds`, `/panel/clients`, `/panel/xray`, `/panel/settings`, `/panel/sub`, `/panel/api-docs`, plus `login`) has its own HTML entry under `frontend/*.html` and its own bootstrap in `src/entries/.js`. Vite builds them into `web/dist/`, and the Go binary embeds that directory at compile time with `embed.FS`. Each navigation triggers a real document load — but each page's bundle is small, so it stays snappy. There's no Vue Router and no central store; Vuex/Pinia were rejected as overkill for the panel's surface area. +The frontend is a **multi-page application**, not a SPA. Every panel route (`/panel`, `/panel/inbounds`, `/panel/clients`, `/panel/xray`, `/panel/settings`, `/panel/nodes`, `/panel/api-docs`, `/panel/sub`, plus `login`) has its own HTML entry in `frontend/*.html` and its own bootstrap in `src/entries/.tsx`. Vite emits each entry into `web/dist/`, and the Go binary embeds that directory at compile time via `embed.FS`. Each panel navigation is a real document load, but every per-page bundle is small enough to keep the experience responsive. There is no React Router and no global store; the surface area does not justify either. ### State and data flow -- **No global store.** State lives where it's used. Cross-page data (settings, current user, theme) is re-fetched on each page load — the backend is on the same box and responses are cheap. -- **Composables** in `src/composables/` carry reactive logic worth sharing inside a page (theme switching, status polling, node lists). Reach for one before adding a new global. -- **Domain classes** in `src/models/` (`Inbound`, `DBInbound`, `Outbound`, `Status`, …) own the protocol-specific logic — link generation, settings JSON shape, TLS/Reality stream handling. The Vue components stay dumb; they ask the model "what's my link?" and render the answer. -- **HTTP** goes through `src/utils/index.js`'s `HttpUtil`, which is a thin Axios wrapper with CSRF, response toast handling, and a `silent: true` opt-out for bulk operations that would otherwise spam toasts. +- **No global store.** State lives in the page that owns it. Cross-page data (settings, current user, theme) is re-fetched on each page load — the backend is local and responses are inexpensive. +- **Hooks** in `src/hooks/` encapsulate reactive logic worth sharing inside a page (`useTheme`, `useStatus`, `useNodes`, `useWebSocket`, `useDatepicker`, …). Prefer extending an existing hook over introducing a new global. +- **Domain models** in `src/models/` (`Inbound`, `DBInbound`, `Outbound`, `Status`, …) own the protocol-specific logic — link generation, settings JSON shape, TLS/Reality stream handling. React components stay declarative; they ask the model "what is my link?" and render the answer. +- **HTTP** goes through `src/utils/index.js`'s `HttpUtil`, a thin Axios wrapper that handles CSRF, response toasts, and a `silent: true` opt-out for bulk operations that would otherwise spam toasts. The Axios setup itself lives in `src/api/axios-init.js`. ### i18n -Locale strings live in `web/translation/.json`, not under `frontend/`. The Go side embeds the same JSON and serves it to both backend templates and `vue-i18n`. When you add a new English key, add it to **every** non-English locale too — missing keys don't fail the build, they just render the raw key in the UI. +Locale strings live in `web/translation/.json`, **not** under `frontend/`. The Go binary embeds the same JSON and serves it to both backend templates and `react-i18next` (initialized in `src/i18n/react.ts`). When a new English key is added it must also land in **every** non-English locale — missing keys do not break the build, they just render the raw key in the UI. ### Two dev workflows -| When you want… | Use | -|----------------|-----| -| To iterate on UI tweaks fast | `cd frontend && npm run dev` (Vite on `:5173`, proxies `/panel/*` and `/api/*` to the Go panel on `:2053`). Start the Go panel first. | -| To test what users actually see | `cd frontend && npm run build`, then `go run .`. The Go binary serves the built bundle either embedded (release mode) or from disk (debug mode). | +| Goal | Command | +|------|---------| +| Iterate on UI changes with HMR | `cd frontend && npm run dev` (Vite on `:5173`, proxies `/panel/*` and `/api/*` to the Go panel on `:2053`). Start the Go panel first. | +| Verify what end users actually see | `cd frontend && npm run build`, then `go run .`. The Go binary serves the built bundle — embedded in release mode, off disk in debug mode. | -The Vite dev proxy auto-rewrites the sidebar's production-style links (`/panel`, `/panel/inbounds`, `/panel/clients`, etc.) to the matching Vite-served HTML, so the navigation feels identical to prod without round-tripping through Go. The route allowlist lives in `MIGRATED_ROUTES` in `vite.config.js` — if you add a new page, register it there too. +The Vite dev proxy rewrites the sidebar's production-style links (`/panel`, `/panel/inbounds`, `/panel/clients`, …) to the matching Vite-served HTML, so navigation behaves identically to production without round-tripping through Go. The allowlist lives in `MIGRATED_ROUTES` in `vite.config.js` — register every new page there. -> **`XUI_DEBUG=true` gotcha** — in debug mode the panel serves HTML out of the embedded FS (frozen at the last `go build` / `go run`) but JS/CSS off disk. Re-running `npm run build` without restarting Go leaves the embedded HTML pointing at the *old* hashed asset names → blank page with 404s in the browser console. Always restart `go run .` after a frontend rebuild. +> **`XUI_DEBUG=true` gotcha** — in debug mode the panel serves HTML from the embedded FS (frozen at the last `go build` / `go run`) but JS/CSS off disk. Re-running `npm run build` without restarting Go leaves the embedded HTML pointing at the *old* hashed asset names, producing a blank page with 404s in the console. Always restart `go run .` after a frontend rebuild. ### Adding a new page -1. Create `frontend/.html` (copy an existing one and adjust the title + the imported entry). -2. Create `src/entries/.js` — `createApp(Page).use(antd).use(i18n).mount('#app')`. -3. Create the page component under `src/pages//.vue` (kebab-case folder, PascalCase component). +1. Create `frontend/.html` (copy an existing entry and adjust the title and the imported `