Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# AGENTS.md

This file provides guidance to agents when working with code in this repository.

## Critical Architecture Patterns

**Telegram Bot Restart Pattern**: MUST call `service.StopBot()` before any server restart (SIGHUP or shutdown) to prevent Telegram bot 409 conflicts. This is critical in `main.go` signal handlers (lines 82-84, 120-122).

**Embedded Assets**: All web resources (HTML, CSS, JS, translations in `web/translation/`) are embedded at compile time using `//go:embed`. Changes to these files require full recompilation - no hot-reload available.

**Dual Server Design**: Main web panel and subscription server run concurrently, both managed by `web/global` package. Subscription server uses separate port.

**Database Seeder System**: Uses `HistoryOfSeeders` model to track one-time migrations (e.g., password bcrypt migration). Check this table before running migrations to prevent re-execution.

**Xray Integration**: Panel dynamically generates `config.json` from inbound/outbound settings and communicates via gRPC API (`xray/api.go`) for real-time traffic stats. Xray binary is platform-specific (`xray-{os}-{arch}`) and managed by installer scripts.

**Signal-Based Restart**: SIGHUP triggers graceful restart. Always stop Telegram bot first via `service.StopBot()`, then restart both web and sub servers.

## Build & Development Commands

```bash
# Build (creates bin/3x-ui.exe)
go build -o bin/3x-ui.exe ./main.go

# Run with debug logging
XUI_DEBUG=true go run ./main.go

# Test all packages
go test ./...

# Vet code
go vet ./...
```

**Production Build**: Uses CGO_ENABLED=1 with static linking via Bootlin musl toolchains for cross-platform builds (see `.github/workflows/release.yml`).

## Configuration & Environment

**Environment Variables**:
- `XUI_DEBUG=true` - Enable detailed debug logging
- `XUI_LOG_LEVEL` - Set log level (debug/info/notice/warning/error)
- `XUI_MAIN_FOLDER` - Override default installation folder
- `XUI_BIN_FOLDER` - Override binary folder (default: "bin")
- `XUI_DB_FOLDER` - Override database folder (default: `/etc/x-ui` on Linux)
- `XUI_LOG_FOLDER` - Override log folder (default: `/var/log/x-ui` on Linux)

**Database Path**: `config.GetDBPath()` returns `/etc/x-ui/x-ui.db` on Linux, current directory on Windows. GORM models auto-migrate on startup.

**Listen Address**: If inbound listen field is empty, defaults to `0.0.0.0` for proper dual-stack IPv4/IPv6 binding (see `database/model/model.go` lines 85-87).

## Project-Specific Patterns

**IP Limitation**: Implements "last IP wins" strategy. When client exceeds LimitIP, oldest connections are automatically disconnected via Xray API to allow newest IPs.

**Session Management**: Uses `gin-contrib/sessions` with cookie-based store for authentication.

**Internationalization**: Translation files in `web/translation/translate.*.toml`. Access via `I18nWeb(c, "key")` in controllers using `locale.I18nType` enum.

**Job Scheduling**: Uses `robfig/cron/v3` for periodic tasks (traffic monitoring, CPU checks, LDAP sync, IP tracking). Jobs registered in `web/web.go` during server initialization.

**Service Layer Pattern**: Services inject dependencies (like `xray.XrayAPI`) and operate on GORM models. Example: `InboundService` in `web/service/inbound.go`.

**Controller Pattern**: Controllers use Gin context (`*gin.Context`) and inherit from `BaseController`. Check auth via `checkLogin` middleware.

**Xray Binary Management**: Download platform-specific Xray binary to bin folder during installation. GeoIP/GeoSite rules downloaded from external repositories (Loyalsoldier, chocolate4u, runetfreedom).

## Gotchas

1. **Bot Restart**: Always stop Telegram bot before server restart to avoid 409 conflict
2. **Embedded Assets**: Changes to HTML/CSS/JS require recompilation
3. **Password Migration**: Seeder system tracks bcrypt migration - check `HistoryOfSeeders` table
4. **Port Binding**: Subscription server uses different port from main panel
5. **Xray Binary**: Must match OS/arch exactly - managed by installer scripts
6. **No Test Files**: Project currently has no `_test.go` files, though `go test ./...` is available
110 changes: 110 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development commands

- Build the app: `go build -o bin/3x-ui.exe ./main.go`
- Run locally with debug logging: `XUI_DEBUG=true go run ./main.go`
- Run tests: `go test ./...`
- Run vet: `go vet ./...`
- Run a single package test suite: `go test ./path/to/package`
- Run a single test: `go test ./path/to/package -run TestName`
- Show CLI help / subcommands: `go run ./main.go --help`
- Show version: `go run ./main.go -v`

VS Code tasks mirror the common Go workflows:
- `go: build`
- `go: run`
- `go: test`
- `go: vet`

## Runtime shape

This is a Go monolith for managing Xray-core, with two Gin-based HTTP servers started from `main.go`:
- the main panel server in `web/`
- the subscription server in `sub/`

`main.go` initializes the SQLite database, starts both servers, and handles process signals:
- `SIGHUP` restarts the panel + subscription servers
- `SIGUSR1` restarts xray-core only

Important: before full shutdown or SIGHUP restart, the Telegram bot is stopped explicitly via `service.StopBot()` to avoid Telegram 409 conflicts.

## High-level architecture

### Database and settings

- `database/db.go` initializes GORM with SQLite, runs auto-migrations, seeds the default admin user, and runs one-time seeders.
- Models live in `database/model/model.go`.
- App configuration is heavily database-backed through the `settings` table rather than static config files.
- `HistoryOfSeeders` is used to track one-time migrations such as password hashing changes.

### Web panel

- `web/web.go` builds the main Gin engine, session middleware, gzip, i18n, static asset serving, template loading, websocket hub setup, and background cron jobs.
- Controllers are in `web/controller/`.
- Business logic lives in `web/service/`.
- Background tasks live in `web/job/`.
- The websocket hub is in `web/websocket/` and is wired from `web/web.go`.

### Subscription server

- `sub/sub.go` starts a separate Gin server for subscription links and JSON subscriptions.
- It has its own listen/port/cert settings and can run independently of the main panel routes.
- It reuses embedded templates/assets from `web/` and applies subscription-specific path/domain settings from the database.

### Xray integration

- `xray/` is the bridge to xray-core.
- `xray/process.go` writes `config.json`, launches the platform-specific xray binary, tracks process state, and handles stop/restart behavior.
- `xray/api.go`, `xray/traffic.go`, and related files handle API access and traffic/stat collection.
- The panel treats xray-core as a managed subprocess and periodically monitors/restarts it from cron jobs in `web/web.go`.

### Frontend delivery model

- The UI is server-rendered HTML templates plus embedded static assets under `web/html/` and `web/assets/`.
- In production, templates/assets are embedded with `go:embed` in `web/web.go`.
- In debug mode (`XUI_DEBUG=true`), templates and assets are loaded from disk, so edits under `web/html/` and `web/assets/` are reflected without rebuilding embedded resources.
- Internationalization files live in `web/translation/*.toml` and are initialized by `web/locale`.

## Background jobs and long-running behavior

`web/web.go` registers the operational jobs that keep the panel in sync with xray-core. These include:
- xray process health checks
- deferred/statistical traffic collection
- client IP checks / log maintenance
- periodic traffic reset jobs
- optional LDAP sync
- optional Telegram notification and CPU alert jobs

When changing settings or services that affect runtime behavior, check whether a cron job, websocket update, or xray restart path also needs to change.

## Repo-specific conventions and gotchas

- Default credentials are seeded as `admin` / `admin`, but stored hashed in the DB.
- The app uses DB settings extensively; many behavior changes require updating `SettingService`, not just editing route/controller code.
- The `Inbound` model stores much of the Xray config as JSON strings (`Settings`, `StreamSettings`, `Sniffing`), then converts those into xray config structs.
- The main panel and subscription server have separate listen/port/cert/base-path concepts. Keep them distinct when changing routing or TLS behavior.
- Session handling uses `gin-contrib/sessions` with a cookie store and secret loaded from settings.
- The subscription server intentionally runs Gin in release mode and discards Gin default writers.
- There are currently no `*_test.go` files in the repo, so `go test ./...` mainly validates buildability of packages.

## Important files to orient quickly

- `main.go` — process entrypoint, CLI subcommands, signal handling
- `web/web.go` — main server wiring, embedded assets/templates, cron jobs
- `sub/sub.go` — subscription server wiring
- `database/db.go` — DB init, migrations, seeders
- `database/model/model.go` — core persistent models
- `web/service/setting.go` — central behavior/settings access point
- `web/service/inbound.go` and `web/service/xray.go` — panel logic tied to xray config/runtime
- `xray/process.go` — xray subprocess management

## Existing repo guidance carried forward

From `.github/copilot-instructions.md` and current code structure:
- Treat the project as a Go + Gin + SQLite application with embedded web assets.
- Remember the dual-server design: main panel plus subscription server.
- Preserve the Telegram bot shutdown-before-restart behavior.
- If working on deployment or container behavior, note that Docker support exists via `Dockerfile`, `DockerInit.sh`, `DockerEntrypoint.sh`, and `docker-compose.yml`.
13 changes: 11 additions & 2 deletions sub/sub.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,21 @@ func (s *Server) initRouter() (*gin.Engine, error) {
return nil, err
}

// Determine if JSON subscription endpoint is enabled
ClashPath, err := s.settingService.GetSubClashPath()
if err != nil {
return nil, err
}

subJsonEnable, err := s.settingService.GetSubJsonEnable()
if err != nil {
return nil, err
}

subClashEnable, err := s.settingService.GetSubClashEnable()
if err != nil {
return nil, err
}

// Set base_path based on LinksPath for template rendering
// Ensure LinksPath ends with "/" for proper asset URL generation
basePath := LinksPath
Expand Down Expand Up @@ -255,7 +264,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
g := engine.Group("/")

s.sub = NewSUBController(
g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
g, LinksPath, JsonPath, ClashPath, subJsonEnable, subClashEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl,
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)

Expand Down
Loading
Loading