# Contributing 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 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 and macOS already ship one; for Windows see below. ### Windows: MinGW-w64 `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. 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 modern Windows defaults.) 2. Extract it somewhere stable, e.g. `C:\mingw64\`. 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 (when a Unix shell is also useful)** 1. Install MSYS2 from . 2. Open the **MSYS2 UCRT64** shell from the Start menu and update once: ```bash pacman -Syu ``` 3. Install the UCRT64 toolchain: ```bash pacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-pkg-config ``` 4. Add `C:\msys64\ucrt64\bin` to the Windows `PATH`. 5. Verify with `gcc --version` in a fresh terminal. 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 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. 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 ```bash git clone https://github.com/MHSanaei/3x-ui.git cd 3x-ui cp .env.example .env mkdir x-ui go mod download cd frontend npm install npm run build cd .. ``` `.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 XUI_DB_FOLDER=x-ui XUI_LOG_FOLDER=x-ui XUI_BIN_FOLDER=x-ui ``` 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 ```bash go run . ``` 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 absent): ```jsonc { "version": "0.2.0", "configurations": [ { "name": "Run 3x-ui (Debug)", "type": "go", "request": "launch", "mode": "auto", "program": "${workspaceFolder}", "cwd": "${workspaceFolder}", "env": { "XUI_DEBUG": "true", "XUI_DB_FOLDER": "x-ui", "XUI_LOG_FOLDER": "x-ui", "XUI_BIN_FOLDER": "x-ui" }, "console": "integratedTerminal" } ] } ``` ## Working on the frontend 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 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 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 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 | 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 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 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 entry and adjust the title and the imported `