mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-07 13:44:24 +00:00
fix
This commit is contained in:
parent
31efd0ad80
commit
5fc6de7d4f
4 changed files with 55 additions and 212 deletions
74
AGENTS.md
74
AGENTS.md
|
|
@ -1,74 +0,0 @@
|
||||||
# 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
CLAUDE.md
110
CLAUDE.md
|
|
@ -1,110 +0,0 @@
|
||||||
# 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`.
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/gin-gonic/gin v1.12.0
|
github.com/gin-gonic/gin v1.12.0
|
||||||
github.com/go-ldap/ldap/v3 v3.4.13
|
github.com/go-ldap/ldap/v3 v3.4.13
|
||||||
github.com/goccy/go-json v0.10.6
|
github.com/goccy/go-json v0.10.6
|
||||||
|
github.com/goccy/go-yaml v1.19.2
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
|
@ -48,7 +49,6 @@ require (
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.30.2 // indirect
|
github.com/go-playground/validator/v10 v10.30.2 // indirect
|
||||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
|
|
||||||
|
|
@ -3,50 +3,58 @@
|
||||||
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subEnable"}}</template>
|
<template #title>{{ i18n "pages.settings.subEnable"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subEnableDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subEnableDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subEnable"></a-switch>
|
<a-switch v-model="allSetting.subEnable"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>JSON Subscription</template>
|
<template #title>JSON Subscription</template>
|
||||||
<template #description>{{ i18n "pages.settings.subJsonEnable"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subJsonEnable"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subJsonEnable"></a-switch>
|
<a-switch v-model="allSetting.subJsonEnable"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>Clash / Mihomo Subscription</template>
|
<template #title>Clash / Mihomo Subscription</template>
|
||||||
<template #description>Enable direct Clash Party and Mihomo YAML subscriptions.</template>
|
<template #description>Enable direct Clash and Mihomo YAML
|
||||||
|
subscriptions.</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subClashEnable"></a-switch>
|
<a-switch v-model="allSetting.subClashEnable"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subListen"}}</template>
|
<template #title>{{ i18n "pages.settings.subListen"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subListenDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subListenDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subListen"></a-input>
|
<a-input type="text" v-model="allSetting.subListen"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subDomain"}}</template>
|
<template #title>{{ i18n "pages.settings.subDomain"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subDomainDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subDomainDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subDomain"></a-input>
|
<a-input type="text" v-model="allSetting.subDomain"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subPort"}}</template>
|
<template #title>{{ i18n "pages.settings.subPort"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subPortDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subPortDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input-number v-model="allSetting.subPort" :min="1" :min="65535"
|
<a-input-number v-model="allSetting.subPort" :min="1"
|
||||||
|
:min="65535"
|
||||||
:style="{ width: '100%' }"></a-input-number>
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subPath"}}</template>
|
<template #title>{{ i18n "pages.settings.subPath"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subPathDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subPathDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subPath"
|
<a-input type="text" v-model="allSetting.subPath"
|
||||||
@input="allSetting.subPath = ((typeof $event === 'string' ? $event : ($event && $event.target ? $event.target.value : '')) || '').replace(/[:*]/g, '')"
|
@input="allSetting.subPath = ((typeof $event === 'string' ? $event : ($event && $event.target ? $event.target.value : '')) || '').replace(/[:*]/g, '')"
|
||||||
|
|
@ -56,9 +64,11 @@
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subURI"}}</template>
|
<template #title>{{ i18n "pages.settings.subURI"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subURIDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subURIDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" placeholder="(http|https)://domain[:port]/path/"
|
<a-input type="text"
|
||||||
|
placeholder="(http|https)://domain[:port]/path/"
|
||||||
v-model="allSetting.subURI"></a-input>
|
v-model="allSetting.subURI"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
|
@ -66,14 +76,16 @@
|
||||||
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subEncrypt"}}</template>
|
<template #title>{{ i18n "pages.settings.subEncrypt"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subEncryptDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subEncryptDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subEncrypt"></a-switch>
|
<a-switch v-model="allSetting.subEncrypt"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subShowInfo"}}</template>
|
<template #title>{{ i18n "pages.settings.subShowInfo"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subShowInfoDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subShowInfoDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subShowInfo"></a-switch>
|
<a-switch v-model="allSetting.subShowInfo"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -81,59 +93,72 @@
|
||||||
<a-divider>{{ i18n "pages.xray.basicTemplate"}}</a-divider>
|
<a-divider>{{ i18n "pages.xray.basicTemplate"}}</a-divider>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
|
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subTitleDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subTitle"></a-input>
|
<a-input type="text" v-model="allSetting.subTitle"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subSupportUrl"}}</template>
|
<template #title>{{ i18n "pages.settings.subSupportUrl"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subSupportUrlDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subSupportUrlDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subSupportUrl" placeholder="https://example.com"></a-input>
|
<a-input type="text" v-model="allSetting.subSupportUrl"
|
||||||
|
placeholder="https://example.com"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subProfileUrl"}}</template>
|
<template #title>{{ i18n "pages.settings.subProfileUrl"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subProfileUrlDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subProfileUrlDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subProfileUrl" placeholder="https://example.com"></a-input>
|
<a-input type="text" v-model="allSetting.subProfileUrl"
|
||||||
|
placeholder="https://example.com"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subAnnounce"}}</template>
|
<template #title>{{ i18n "pages.settings.subAnnounce"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subAnnounceDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subAnnounceDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-textarea v-model="allSetting.subAnnounce"></a-textarea>
|
<a-textarea v-model="allSetting.subAnnounce"></a-textarea>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-divider>{{ i18n "pages.xray.advancedTemplate"}} (Happ)</a-divider>
|
<a-divider>{{ i18n "pages.xray.advancedTemplate"}} (Happ)</a-divider>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subEnableRouting"}}</template>
|
<template #title>{{ i18n
|
||||||
<template #description>{{ i18n "pages.settings.subEnableRoutingDesc"}}</template>
|
"pages.settings.subEnableRouting"}}</template>
|
||||||
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subEnableRoutingDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="allSetting.subEnableRouting"></a-switch>
|
<a-switch v-model="allSetting.subEnableRouting"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subRoutingRules"}}</template>
|
<template #title>{{ i18n
|
||||||
<template #description>{{ i18n "pages.settings.subRoutingRulesDesc"}}</template>
|
"pages.settings.subRoutingRules"}}</template>
|
||||||
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subRoutingRulesDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-textarea v-model="allSetting.subRoutingRules" placeholder="happ://routing/add/..."></a-textarea>
|
<a-textarea v-model="allSetting.subRoutingRules"
|
||||||
|
placeholder="happ://routing/add/..."></a-textarea>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subCertPath"}}</template>
|
<template #title>{{ i18n "pages.settings.subCertPath"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subCertPathDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subCertPathDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subCertFile"></a-input>
|
<a-input type="text" v-model="allSetting.subCertFile"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subKeyPath"}}</template>
|
<template #title>{{ i18n "pages.settings.subKeyPath"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subKeyPathDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subKeyPathDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" v-model="allSetting.subKeyFile"></a-input>
|
<a-input type="text" v-model="allSetting.subKeyFile"></a-input>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -142,9 +167,11 @@
|
||||||
<a-collapse-panel key="4" header='{{ i18n "pages.settings.intervals"}}'>
|
<a-collapse-panel key="4" header='{{ i18n "pages.settings.intervals"}}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subUpdates"}}</template>
|
<template #title>{{ i18n "pages.settings.subUpdates"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subUpdatesDesc"}}</template>
|
<template #description>{{ i18n
|
||||||
|
"pages.settings.subUpdatesDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input-number :min="1" v-model="allSetting.subUpdates" :style="{ width: '100%' }"></a-input-number>
|
<a-input-number :min="1" v-model="allSetting.subUpdates"
|
||||||
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue