mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-22 19:07:14 +00:00
58669f6146
The four pull_request_target review jobs in claude-bot.yml (Senior Developer / QA / Tester / Arbiter and their shared rubric) are replaced by a single review job running the official code-review plugin - the same skill behind Anthropic's hosted Code Review and the review workflow /install-github-app generates. The hosted service needs a Team/Enterprise organisation, so the plugin runs in CI on the maintainer's subscription instead: inline findings on PR open and ready-for-review, plus manual (re-)review when the owner or a collaborator comments "@claude review". The official example triggers on pull_request, but GitHub withholds secrets from fork runs and essentially every 3x-ui pull request is from a fork, so the job keeps the lanes' pull_request_target posture: the workspace is the base revision and nothing from the pull request is checked out or executed. What the lanes uniquely knew is distilled into REVIEW.md, handed to the skill via --append-system-prompt and pinned by bot_context_test.go the way repo-context.md is: the runtime.Runtime dispatch rule, migration and upgrade safety, the four-step route contract chain including the unchecked docs copy, the i18n rule, the three link implementations, and the wire-format verification bar. The mention job now ignores "@claude review" comments on pull requests so the review trigger does not also wake the generic bot, and the lane-only rubric file goes with the lanes. The remaining prompts also lose their tone micro-rules (no emoji, no exclamation marks, no filler) and the workflow's comment banners are removed.
182 lines
9.9 KiB
Markdown
182 lines
9.9 KiB
Markdown
# Repository context for the Claude bot
|
|
|
|
Shared briefing for the jobs in `.github/workflows/claude-bot.yml`. It exists so
|
|
these facts live in ONE place next to the code instead of being restated in each
|
|
prompt, where they went stale silently. (Pull-request review is separate: its
|
|
code-review skill is briefed with `CLAUDE.md` and `REVIEW.md`, not this.)
|
|
|
|
`CLAUDE.md`, `frontend/CLAUDE.md` and `docs/architecture.md` outrank this file.
|
|
Where they disagree with it, they win and this file is the thing to fix.
|
|
`docs/architecture.md` carries a "Symptom -> File" index and the cron-job table,
|
|
which answer "which file owns X" in one hop; grepping blind wastes turns on a
|
|
question it already answers.
|
|
|
|
## Stack
|
|
|
|
3x-ui is an open-source web control panel for managing Xray-core servers.
|
|
|
|
- Backend: Go 1.26, module `github.com/mhsanaei/3x-ui/v3`, Gin and GORM.
|
|
- It runs Xray-core as a managed child process (`internal/xray/process.go`) and
|
|
imports `github.com/xtls/xray-core` for config types and the gRPC
|
|
stats/handler/router API. The release the panel BUNDLES is pinned in
|
|
`DockerInit.sh`; the version it COMPILES against is pinned in `go.mod`, and
|
|
the two are not always the same.
|
|
- MTProto inbounds run a SECOND managed child, the `mtg-multi` binary (a
|
|
multi-secret mtg fork, panel-side code in `internal/mtproto/`), one process
|
|
per inbound. Client, ad-tag and quota/expiry edits are hot-applied through the
|
|
fork's management API (`PUT /secrets`) so connections survive, with a process
|
|
restart as the fallback on older binaries.
|
|
- Storage: SQLite by default (`/etc/x-ui/x-ui.db` on Linux, the executable
|
|
directory on Windows) or PostgreSQL (`XUI_DB_TYPE` / `XUI_DB_DSN`). The SQLite
|
|
driver is CGo, so `CGO_ENABLED=0` builds fail.
|
|
- Frontend: React 19 + Ant Design 6 + Vite 8 + TypeScript in `frontend/`, built
|
|
into `internal/web/dist/` (gitignored) and embedded with `embed.FS`.
|
|
|
|
## Where things live
|
|
|
|
| area | path |
|
|
| --- | --- |
|
|
| entry point + `x-ui` CLI | `main.go` |
|
|
| env parsing | `internal/config/` |
|
|
| schema, migrations | `internal/database/`, `internal/database/model/` |
|
|
| Xray child process + config | `internal/xray/` |
|
|
| MTProto inbounds | `internal/mtproto/` |
|
|
| subscription server | `internal/sub/` |
|
|
| HTTP handlers | `internal/web/controller/` |
|
|
| business logic | `internal/web/service/` |
|
|
| cron jobs (schedules in `web.go startTask()`) | `internal/web/job/` |
|
|
| master/sub-node over mTLS | `internal/web/runtime/` |
|
|
| i18n | `internal/web/locale/`, `internal/web/translation/` |
|
|
| UI source | `frontend/src/` |
|
|
| install / upgrade | `install.sh`, `x-ui.sh`, `DockerInit.sh` |
|
|
|
|
## Hard rules a change must respect
|
|
|
|
- **Dispatch through `runtime.Runtime`.** Every state-changing inbound or client
|
|
operation goes through the interface in `internal/web/runtime/`, never
|
|
straight to `internal/xray/api.go`. A direct call passes every local test and
|
|
silently breaks every multi-node deployment; it is invisible in a single-box
|
|
reading of a diff.
|
|
- **Layering.** Controllers are thin — bind, validate, respond — with no GORM
|
|
queries, no Xray calls and no business rules. `internal/util/*` is leaf-only
|
|
and must not import service, controller or database. `internal/web/dist/` and
|
|
`frontend/src/generated/` are generated; a hand-edit is a violation.
|
|
- **Comments in committed Go/TS/TSX: 2 lines MAX per block**, spent on the *why*
|
|
a name cannot hold — an invariant, an issue number, a non-obvious constraint.
|
|
Exempt, never flag: `//go:build`, `//go:generate`, `//nolint:`,
|
|
`// Code generated ... DO NOT EDIT.`. HTML `<!-- -->` is fine.
|
|
- **The route contract chain**, which breaks in four distinct places:
|
|
1. a new `g.POST`/`g.GET` in `internal/web/controller/` needs a matching entry
|
|
in `frontend/src/pages/api-docs/endpoints.ts` — pinned BOTH ways by
|
|
`TestRouteRegistryContract` in `internal/web/routes_contract_test.go`, so a
|
|
renamed or removed route that leaves a stale entry fails too;
|
|
2. generated artefacts must be regenerated with `make gen`, or CI's `codegen`
|
|
job fails on a dirty `frontend/src/generated` or
|
|
`frontend/public/openapi.json`;
|
|
3. a NEW struct crossing the API boundary must be added to the `StructAllow`
|
|
allowlist in `tools/openapigen/main.go`, or it is SILENTLY dropped from the
|
|
schemas and `frontend/scripts/build-openapi.mjs` then fails — a guaranteed
|
|
CI break, not a style nit;
|
|
4. the step NOTHING checks — `frontend/public/openapi.json` must be copied to
|
|
`docs/public/openapi.json` and the MDX regenerated with
|
|
`cd docs && pnpm gen:api`, because `docs-ci.yml` fires only on `docs/**`.
|
|
Step 4 is the one that reaches production wrong.
|
|
- **i18n.** A new English key goes in EVERY locale JSON in
|
|
`internal/web/translation/` (13 files) AND must be referenced from
|
|
`frontend/src` or Go in the SAME change.
|
|
`frontend/src/test/i18n-dead-keys.test.ts` fails on a missing locale file and
|
|
on an orphan key alike.
|
|
- **Migrations.** Schema changes are GORM `AutoMigrate` PLUS hand-written
|
|
migrations in `internal/database/db.go`. There are no migration files and no
|
|
down-migrations, and everything has to work on SQLite AND PostgreSQL.
|
|
- **Tests.** Stdlib `testing` only (no testify), table-driven with `t.Run`
|
|
subtests and `t.Helper()` on helpers. An assertion must pin the exact value,
|
|
typed error or emitted string — `err != nil` and `len(x) > 0` are findings,
|
|
not nits. Prefer real dependencies: a throwaway DB via
|
|
`database.InitDB(filepath.Join(t.TempDir(), "x-ui.db"))` with `t.Cleanup`, and
|
|
`httptest` for HTTP. `internal/sub`'s `initSubDB(t)` is the template.
|
|
A test must FAIL without its fix; one that passes either way certifies
|
|
nothing and then gets cited as proof the fix works.
|
|
|
|
## The three link implementations
|
|
|
|
Link and subscription generation is implemented three times, independently:
|
|
|
|
| language | path | what it feeds |
|
|
| --- | --- | --- |
|
|
| Go | `internal/util/link/`, `internal/sub/` | what the panel serves |
|
|
| TS | `frontend/src/lib/xray/` | what the panel UI shows |
|
|
| TS | `docs/lib/xray/` | what the docs site shows |
|
|
|
|
A change to share-link or subscription output that touches one and not the
|
|
others is how they drift apart.
|
|
|
|
## Downstream programs that must accept what the panel emits
|
|
|
|
- **XTLS/Xray-core** — the Xray config the panel generates, and the VLESS/VMess
|
|
transport and security fields.
|
|
- **MetaCubeX/mihomo** — consumes the Clash YAML from `internal/sub/`.
|
|
- **SagerNet/sing-box** — parses the share links the panel emits.
|
|
- **mhsanaei/mtg-multi** — the MTProto sidecar whose TOML (`[secrets]`,
|
|
`[secret-ad-tags]`, `[secret-limits]`) and management API
|
|
(`PUT /secrets`, `POST /secrets/{name}/reset-quota`) `internal/mtproto/`
|
|
writes and calls.
|
|
|
|
## What CI runs
|
|
|
|
`.github/workflows/ci.yml`, on every pull request touching Go or frontend code.
|
|
It is paths-filtered, so a docs-only or workflow-only change produces no run.
|
|
|
|
| job | what it proves |
|
|
| --- | --- |
|
|
| `go-test` | `go test -shuffle=on -count=1` over every package except `frontend/node_modules` |
|
|
| `race` | the same set under `-race -shuffle=on` |
|
|
| `postgres-durable-first` | live PostgreSQL 16: the `PostgresCommitFailure` tests plus `TestHostAutoMigrateCreatesColumns_Postgres` and `TestMigrate_Postgres`. Both steps COUNT passes rather than assert on SKIP, so a renamed or deleted test fails the job |
|
|
| `govulncheck` | known vulnerabilities |
|
|
| `golangci` | `golangci-lint` |
|
|
| `fuzz-smoke` | 30s each on `FuzzParseLink` and `FuzzDecodeCertPin` |
|
|
| `codegen` | `npm run gen` then `git diff --exit-code` on the generated files |
|
|
| `frontend` | MSW worker drift, lint, format:check, typecheck, `npm test` (Vitest + headless-Chromium Storybook), build, build-storybook, `npm audit` |
|
|
|
|
**What CI does NOT prove.** These test families `t.Skip` unless an environment
|
|
variable is set, and CI sets only the PostgreSQL ones above:
|
|
|
|
| gate | covers |
|
|
| --- | --- |
|
|
| `XUI_TEST_PG_DSN` | PostgreSQL-specific paths |
|
|
| `XUI_DB_TYPE` + `XUI_DB_DSN` | dialect-dependent behaviour |
|
|
| `XRAY_E2E_BINARY` | the Xray gRPC end-to-end tests in `internal/xray/` |
|
|
| `XUI_SCALE_TEST` | scale tests in `internal/sub/`, `internal/web/job/`, `internal/web/service/` |
|
|
|
|
Mutation testing (`mutation.yml`) runs nightly and never on a pull request, so a
|
|
test that cannot fail is invisible to CI. `make verify` is the local gate.
|
|
|
|
## Support facts reporters get wrong
|
|
|
|
- Linux install: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
|
|
- Install generates a RANDOM username, password and web base path — never
|
|
admin/admin. The `x-ui` menu on the server shows or resets them.
|
|
- The installer service environment file is DISTRO-DEPENDENT:
|
|
`/etc/default/x-ui` (Debian/Ubuntu), `/etc/conf.d/x-ui` (Arch),
|
|
`/etc/sysconfig/x-ui` (RHEL/Fedora). Naming the wrong one means the reporter's
|
|
edit is silently never read by systemd — a common cause of "I set the variable
|
|
and nothing happened".
|
|
- Windows is supported. There the database sits next to the executable, not in
|
|
`/etc` — never quote the Linux path to a Windows user.
|
|
- SQLite to PostgreSQL: `x-ui migrate-db --dsn "postgres://..."`, then set
|
|
`XUI_DB_TYPE`/`XUI_DB_DSN` in that file and `systemctl restart x-ui`. The
|
|
source SQLite file is left in place.
|
|
- Docker image `ghcr.io/mhsanaei/3x-ui`; PostgreSQL profile
|
|
`docker compose --profile postgres up -d`. Fail2ban IP-limit enforcement needs
|
|
`NET_ADMIN` + `NET_RAW` (compose grants them; a bare `docker run` must add
|
|
`--cap-add=NET_ADMIN --cap-add=NET_RAW`).
|
|
- Never state that a `XUI_*` variable does not exist without grepping
|
|
`internal/config/` and `internal/tunnelmonitor/` first. The
|
|
`XUI_TUNNEL_HEALTH_*` family is the usual answer to "the panel restarts Xray
|
|
every few minutes".
|
|
- Security per inbound is none / tls / reality. XTLS is a VLESS *flow*
|
|
(`xtls-rprx-vision`), not a security setting — never tell anyone to pick XTLS
|
|
in the security dropdown.
|
|
- Never hardcode a version. For "is this already fixed" use
|
|
`gh release list -L 10`, `gh search commits`, and `git log -S`.
|