Showing preview only (3,480K chars total). Download the full file or copy to clipboard to get everything.
Repository: MHSanaei/3x-ui
Branch: main
Commit: 38d87230d326
Files: 206
Total size: 3.3 MB
Directory structure:
gitextract_ww701i7l/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yaml
│ │ ├── feature_request.yaml
│ │ └── question.yaml
│ ├── copilot-instructions.md
│ ├── dependabot.yml
│ ├── pull_request_template.yml
│ └── workflows/
│ ├── cleanup_caches.yml
│ ├── docker.yml
│ └── release.yml
├── .gitignore
├── CONTRIBUTING.md
├── DockerEntrypoint.sh
├── DockerInit.sh
├── Dockerfile
├── LICENSE
├── README.ar_EG.md
├── README.es_ES.md
├── README.fa_IR.md
├── README.md
├── README.ru_RU.md
├── README.zh_CN.md
├── config/
│ ├── config.go
│ ├── name
│ └── version
├── database/
│ ├── db.go
│ └── model/
│ └── model.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── install.sh
├── logger/
│ └── logger.go
├── main.go
├── sub/
│ ├── default.json
│ ├── sub.go
│ ├── subController.go
│ ├── subJsonService.go
│ └── subService.go
├── update.sh
├── util/
│ ├── common/
│ │ ├── err.go
│ │ ├── format.go
│ │ └── multi_error.go
│ ├── crypto/
│ │ └── crypto.go
│ ├── json_util/
│ │ └── json.go
│ ├── ldap/
│ │ └── ldap.go
│ ├── random/
│ │ └── random.go
│ ├── reflect_util/
│ │ └── reflect.go
│ └── sys/
│ ├── psutil.go
│ ├── sys_darwin.go
│ ├── sys_linux.go
│ └── sys_windows.go
├── web/
│ ├── assets/
│ │ ├── codemirror/
│ │ │ ├── fold/
│ │ │ │ ├── brace-fold.js
│ │ │ │ ├── foldcode.js
│ │ │ │ ├── foldgutter.css
│ │ │ │ └── foldgutter.js
│ │ │ ├── hint/
│ │ │ │ └── javascript-hint.js
│ │ │ ├── javascript.js
│ │ │ ├── jshint.js
│ │ │ ├── jsonlint.js
│ │ │ └── lint/
│ │ │ ├── javascript-lint.js
│ │ │ ├── lint.css
│ │ │ └── lint.js
│ │ └── js/
│ │ ├── axios-init.js
│ │ ├── model/
│ │ │ ├── dbinbound.js
│ │ │ ├── inbound.js
│ │ │ ├── outbound.js
│ │ │ ├── reality_targets.js
│ │ │ └── setting.js
│ │ ├── subscription.js
│ │ ├── util/
│ │ │ └── index.js
│ │ └── websocket.js
│ ├── controller/
│ │ ├── api.go
│ │ ├── base.go
│ │ ├── inbound.go
│ │ ├── index.go
│ │ ├── server.go
│ │ ├── setting.go
│ │ ├── util.go
│ │ ├── websocket.go
│ │ ├── xray_setting.go
│ │ └── xui.go
│ ├── entity/
│ │ └── entity.go
│ ├── global/
│ │ ├── global.go
│ │ └── hashStorage.go
│ ├── html/
│ │ ├── common/
│ │ │ └── page.html
│ │ ├── component/
│ │ │ ├── aClientTable.html
│ │ │ ├── aCustomStatistic.html
│ │ │ ├── aPersianDatepicker.html
│ │ │ ├── aSettingListItem.html
│ │ │ ├── aSidebar.html
│ │ │ ├── aTableSortable.html
│ │ │ └── aThemeSwitch.html
│ │ ├── form/
│ │ │ ├── client.html
│ │ │ ├── inbound.html
│ │ │ ├── outbound.html
│ │ │ ├── protocol/
│ │ │ │ ├── dokodemo.html
│ │ │ │ ├── http.html
│ │ │ │ ├── shadowsocks.html
│ │ │ │ ├── socks.html
│ │ │ │ ├── trojan.html
│ │ │ │ ├── tun.html
│ │ │ │ ├── vless.html
│ │ │ │ ├── vmess.html
│ │ │ │ └── wireguard.html
│ │ │ ├── reality_settings.html
│ │ │ ├── sniffing.html
│ │ │ ├── stream/
│ │ │ │ ├── external_proxy.html
│ │ │ │ ├── stream_finalmask.html
│ │ │ │ ├── stream_grpc.html
│ │ │ │ ├── stream_httpupgrade.html
│ │ │ │ ├── stream_kcp.html
│ │ │ │ ├── stream_settings.html
│ │ │ │ ├── stream_sockopt.html
│ │ │ │ ├── stream_tcp.html
│ │ │ │ ├── stream_ws.html
│ │ │ │ └── stream_xhttp.html
│ │ │ └── tls_settings.html
│ │ ├── inbounds.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── modals/
│ │ │ ├── client_bulk_modal.html
│ │ │ ├── client_modal.html
│ │ │ ├── dns_presets_modal.html
│ │ │ ├── inbound_info_modal.html
│ │ │ ├── inbound_modal.html
│ │ │ ├── prompt_modal.html
│ │ │ ├── qrcode_modal.html
│ │ │ ├── text_modal.html
│ │ │ ├── two_factor_modal.html
│ │ │ ├── warp_modal.html
│ │ │ ├── xray_balancer_modal.html
│ │ │ ├── xray_dns_modal.html
│ │ │ ├── xray_fakedns_modal.html
│ │ │ ├── xray_outbound_modal.html
│ │ │ ├── xray_reverse_modal.html
│ │ │ └── xray_rule_modal.html
│ │ ├── settings/
│ │ │ ├── panel/
│ │ │ │ ├── general.html
│ │ │ │ ├── security.html
│ │ │ │ ├── subscription/
│ │ │ │ │ ├── general.html
│ │ │ │ │ ├── json.html
│ │ │ │ │ └── subpage.html
│ │ │ │ └── telegram.html
│ │ │ └── xray/
│ │ │ ├── advanced.html
│ │ │ ├── balancers.html
│ │ │ ├── basics.html
│ │ │ ├── dns.html
│ │ │ ├── outbounds.html
│ │ │ ├── reverse.html
│ │ │ └── routing.html
│ │ ├── settings.html
│ │ └── xray.html
│ ├── job/
│ │ ├── check_client_ip_job.go
│ │ ├── check_cpu_usage.go
│ │ ├── check_hash_storage.go
│ │ ├── check_xray_running_job.go
│ │ ├── clear_logs_job.go
│ │ ├── ldap_sync_job.go
│ │ ├── periodic_traffic_reset_job.go
│ │ ├── stats_notify_job.go
│ │ └── xray_traffic_job.go
│ ├── locale/
│ │ └── locale.go
│ ├── middleware/
│ │ ├── domainValidator.go
│ │ └── redirect.go
│ ├── network/
│ │ ├── auto_https_conn.go
│ │ └── auto_https_listener.go
│ ├── service/
│ │ ├── config.json
│ │ ├── inbound.go
│ │ ├── outbound.go
│ │ ├── panel.go
│ │ ├── server.go
│ │ ├── setting.go
│ │ ├── tgbot.go
│ │ ├── user.go
│ │ ├── warp.go
│ │ ├── xray.go
│ │ └── xray_setting.go
│ ├── session/
│ │ └── session.go
│ ├── translation/
│ │ ├── translate.ar_EG.toml
│ │ ├── translate.en_US.toml
│ │ ├── translate.es_ES.toml
│ │ ├── translate.fa_IR.toml
│ │ ├── translate.id_ID.toml
│ │ ├── translate.ja_JP.toml
│ │ ├── translate.pt_BR.toml
│ │ ├── translate.ru_RU.toml
│ │ ├── translate.tr_TR.toml
│ │ ├── translate.uk_UA.toml
│ │ ├── translate.vi_VN.toml
│ │ ├── translate.zh_CN.toml
│ │ └── translate.zh_TW.toml
│ ├── web.go
│ └── websocket/
│ ├── hub.go
│ └── notifier.go
├── windows_files/
│ └── readme.txt
├── x-ui.rc
├── x-ui.service.arch
├── x-ui.service.debian
├── x-ui.service.rhel
├── x-ui.sh
└── xray/
├── api.go
├── client_traffic.go
├── config.go
├── inbound.go
├── log_writer.go
├── process.go
└── traffic.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: MHSanaei
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: mhsanaei
custom: https://nowpayments.io/donation/hsanaei
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: Bug report
description: Create a report to help us improve
title: "Bug report"
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thank you for reporting a bug! Please fill out the following information.
- type: textarea
id: what-happened
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: My problem is...
validations:
required: true
- type: textarea
id: how-repeat-problem
attributes:
label: How to repeat the problem?
description: Sequence of actions that allow you to reproduce the bug
placeholder: |
1. Open `Inbounds` page
2. ...
validations:
required: true
- type: textarea
id: expected-action
attributes:
label: Expected action
description: What's going to happen
placeholder: Must be...
validations:
required: false
- type: textarea
id: received-action
attributes:
label: Received action
description: What's really happening
placeholder: It's actually happening...
validations:
required: false
- type: input
id: xui-version
attributes:
label: 3x-ui Version
description: Which version of 3x-ui are you using?
placeholder: 2.X.X
validations:
required: true
- type: input
id: xray-version
attributes:
label: Xray-core Version
description: Which version of Xray-core are you using?
placeholder: 2.X.X
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please check all the checkboxes
options:
- label: This bug report is written entirely in English.
required: true
- label: This bug report is new and no one has reported it before me.
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yaml
================================================
name: Feature request
description: Suggest an idea for this project
title: "Feature request"
labels: ["enhancement"]
body:
- type: textarea
id: is-related-problem
attributes:
label: Is your feature request related to a problem?
description: A clear and concise description of what the problem is.
placeholder: I'm always frustrated when...
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please check all the checkboxes
options:
- label: This feature report is written entirely in English.
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/question.yaml
================================================
name: Question
description: Describe this issue template's purpose here.
title: "Question"
labels: ["question"]
body:
- type: textarea
id: question
attributes:
label: Question
placeholder: I have a question, ..., how can I solve it?
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please check all the checkboxes
options:
- label: This question is written entirely in English.
required: true
================================================
FILE: .github/copilot-instructions.md
================================================
# 3X-UI Development Guide
## Project Overview
3X-UI is a web-based control panel for managing Xray-core servers. It's a Go application using Gin web framework with embedded static assets and SQLite database. The panel manages VPN/proxy inbounds, monitors traffic, and provides Telegram bot integration.
## Architecture
### Core Components
- **main.go**: Entry point that initializes database, web server, and subscription server. Handles graceful shutdown via SIGHUP/SIGTERM signals
- **web/**: Primary web server with Gin router, HTML templates, and static assets embedded via `//go:embed`
- **xray/**: Xray-core process management and API communication for traffic monitoring
- **database/**: GORM-based SQLite database with models in `database/model/`
- **sub/**: Subscription server running alongside main web server (separate port)
- **web/service/**: Business logic layer containing InboundService, SettingService, TgBot, etc.
- **web/controller/**: HTTP handlers using Gin context (`*gin.Context`)
- **web/job/**: Cron-based background jobs for traffic monitoring, CPU checks, LDAP sync
### Key Architectural Patterns
1. **Embedded Resources**: All web assets (HTML, CSS, JS, translations) are embedded at compile time using `embed.FS`:
- `web/assets` → `assetsFS`
- `web/html` → `htmlFS`
- `web/translation` → `i18nFS`
2. **Dual Server Design**: Main web panel + subscription server run concurrently, managed by `web/global` package
3. **Xray Integration**: Panel generates `config.json` for Xray binary, communicates via gRPC API for real-time traffic stats
4. **Signal-Based Restart**: SIGHUP triggers graceful restart. **Critical**: Always call `service.StopBot()` before restart to prevent Telegram bot 409 conflicts
5. **Database Seeders**: Uses `HistoryOfSeeders` model to track one-time migrations (e.g., password bcrypt migration)
## Development Workflows
### Building & Running
```bash
# Build (creates bin/3x-ui.exe)
go run tasks.json → "go: build" task
# Run with debug logging
XUI_DEBUG=true go run ./main.go
# Or use task: "go: run"
# Test
go test ./...
```
### Command-Line Operations
The main.go accepts flags for admin tasks:
- `-reset` - Reset all panel settings to defaults
- `-show` - Display current settings (port, paths)
- Use these by running the binary directly, not via web interface
### Database Management
- DB path: Configured via `config.GetDBPath()`, typically `/etc/x-ui/x-ui.db`
- Models: Located in `database/model/model.go` - Auto-migrated on startup
- Seeders: Use `HistoryOfSeeders` to prevent re-running migrations
- Default credentials: admin/admin (hashed with bcrypt)
### Telegram Bot Development
- Bot instance in `web/service/tgbot.go` (3700+ lines)
- Uses `telego` library with long polling
- **Critical Pattern**: Must call `service.StopBot()` before any server restart to prevent 409 bot conflicts
- Bot handlers use `telegohandler.BotHandler` for routing
- i18n via embedded `i18nFS` passed to bot startup
## Code Conventions
### Service Layer Pattern
Services inject dependencies (like xray.XrayAPI) and operate on GORM models:
```go
type InboundService struct {
xrayApi xray.XrayAPI
}
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
// Business logic here
}
```
### Controller Pattern
Controllers use Gin context and inherit from BaseController:
```go
func (a *InboundController) getInbounds(c *gin.Context) {
// Use I18nWeb(c, "key") for translations
// Check auth via checkLogin middleware
}
```
### Configuration Management
- Environment vars: `XUI_DEBUG`, `XUI_LOG_LEVEL`, `XUI_MAIN_FOLDER`
- Config embedded files: `config/version`, `config/name`
- Use `config.GetLogLevel()`, `config.GetDBPath()` helpers
### Internationalization
- Translation files: `web/translation/translate.*.toml`
- Access via `I18nWeb(c, "pages.login.loginAgain")` in controllers
- Use `locale.I18nType` enum (Web, Api, etc.)
## External Dependencies & Integration
### Xray-core
- Binary management: Download platform-specific binary (`xray-{os}-{arch}`) to bin folder
- Config generation: Panel creates `config.json` dynamically from inbound/outbound settings
- Process control: Start/stop via `xray/process.go`
- gRPC API: Real-time stats via `xray/api.go` using `google.golang.org/grpc`
### Critical External Paths
- Xray binary: `{bin_folder}/xray-{os}-{arch}`
- Xray config: `{bin_folder}/config.json`
- GeoIP/GeoSite: `{bin_folder}/geoip.dat`, `geosite.dat`
- Logs: `{log_folder}/3xipl.log`, `3xipl-banned.log`
### Job Scheduling
Uses `robfig/cron/v3` for periodic tasks:
- Traffic monitoring: `xray_traffic_job.go`
- CPU alerts: `check_cpu_usage.go`
- IP tracking: `check_client_ip_job.go`
- LDAP sync: `ldap_sync_job.go`
Jobs registered in `web/web.go` during server initialization
## Deployment & Scripts
### Installation Script Pattern
Both `install.sh` and `x-ui.sh` follow these patterns:
- Multi-distro support via `$release` variable (ubuntu, debian, centos, arch, etc.)
- Port detection with `is_port_in_use()` using ss/netstat/lsof
- Systemd service management with distro-specific unit files (`.service.debian`, `.service.arch`, `.service.rhel`)
### Docker Build
Multi-stage Dockerfile:
1. **Builder**: CGO-enabled build, runs `DockerInit.sh` to download Xray binary
2. **Final**: Alpine-based with fail2ban pre-configured
### Key File Locations (Production)
- Binary: `/usr/local/x-ui/`
- Database: `/etc/x-ui/x-ui.db`
- Logs: `/var/log/x-ui/`
- Service: `/etc/systemd/system/x-ui.service.*`
## Testing & Debugging
- Set `XUI_DEBUG=true` for detailed logging
- Check Xray process: `x-ui.sh` script provides menu for status/logs
- Database inspection: Direct SQLite access to x-ui.db
- Traffic debugging: Check `3xipl.log` for IP limit tracking
- Telegram bot: Logs show bot initialization and command handling
## Common Gotchas
1. **Bot Restart**: Always stop Telegram bot before server restart to avoid 409 conflict
2. **Embedded Assets**: Changes to HTML/CSS require recompilation (not hot-reload)
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. **Session Management**: Uses `gin-contrib/sessions` with cookie store
7. **IP Limitation**: Implements "last IP wins" - when client exceeds LimitIP, oldest connections are automatically disconnected via Xray API to allow newest IPs
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
================================================
FILE: .github/pull_request_template.yml
================================================
## What is the pull request?
<!-- Briefly describe the changes introduced by this pull request -->
## Which part of the application is affected by the change?
- [ ] Frontend
- [ ] Backend
## Type of Changes
- [ ] Bug fix
- [ ] New feature
- [ ] Refactoring
- [ ] Other
## Screenshots
<!-- Add screenshots to illustrate the changes -->
<!-- Remove this section if it is not applicable. -->
================================================
FILE: .github/workflows/cleanup_caches.yml
================================================
name: Cleanup Caches
on:
schedule:
- cron: '0 3 * * 0' # every Sunday
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Delete caches older than 3 days
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CUTOFF_DATE=$(date -d "3 days ago" -Ins --utc | sed 's/+0000/Z/')
echo "Deleting caches older than: $CUTOFF_DATE"
CACHE_IDS=$(gh api --paginate repos/${{ github.repository }}/actions/caches \
--jq ".actions_caches[] | select(.last_accessed_at < \"$CUTOFF_DATE\") | .id" 2>/dev/null)
if [ -z "$CACHE_IDS" ]; then
echo "No old caches found to delete."
else
echo "$CACHE_IDS" | while read CACHE_ID; do
echo "Deleting cache: $CACHE_ID"
gh api -X DELETE repos/${{ github.repository }}/actions/caches/$CACHE_ID
done
echo "Old caches deleted successfully."
fi
================================================
FILE: .github/workflows/docker.yml
================================================
name: Release 3X-UI for Docker
permissions:
contents: read
packages: write
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: |
hsanaeii/3x-ui
ghcr.io/mhsanaei/3x-ui
tags: |
type=ref,event=branch
type=ref,event=tag
type=semver,pattern={{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
push: true
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6,linux/386
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release 3X-UI
on:
workflow_dispatch:
push:
branches:
- '**'
tags:
- "v*.*.*"
paths:
- '**.js'
- '**.css'
- '**.html'
- '**.sh'
- '**.go'
- 'go.mod'
- 'go.sum'
- 'x-ui.service.debian'
- 'x-ui.service.arch'
- 'x-ui.service.rhel'
pull_request:
jobs:
analyze:
name: Analyze Go code
permissions:
contents: read
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
- name: Check formatting
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "These files are not gofmt-formatted:"
echo "$unformatted"
exit 1
fi
- name: Run go vet
run: go vet ./...
- name: Run staticcheck
uses: dominikh/staticcheck-action@v1
with:
version: "latest"
install-go: false
- name: Run tests
run: go test -race -shuffle=on ./...
build:
needs: analyze
permissions:
contents: write
strategy:
matrix:
platform:
- amd64
- arm64
- armv7
- armv6
- 386
- armv5
- s390x
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Build 3X-UI
run: |
export CGO_ENABLED=1
export GOOS=linux
export GOARCH=${{ matrix.platform }}
# Use Bootlin prebuilt cross-toolchains (musl 1.2.5 in stable series)
case "${{ matrix.platform }}" in
amd64) BOOTLIN_ARCH="x86-64" ;;
arm64) BOOTLIN_ARCH="aarch64" ;;
armv7) BOOTLIN_ARCH="armv7-eabihf"; export GOARCH=arm GOARM=7 ;;
armv6) BOOTLIN_ARCH="armv6-eabihf"; export GOARCH=arm GOARM=6 ;;
armv5) BOOTLIN_ARCH="armv5-eabi"; export GOARCH=arm GOARM=5 ;;
386) BOOTLIN_ARCH="x86-i686" ;;
s390x) BOOTLIN_ARCH="s390x-z13" ;;
esac
echo "Resolving Bootlin musl toolchain for arch=$BOOTLIN_ARCH (platform=${{ matrix.platform }})"
TARBALL_BASE="https://toolchains.bootlin.com/downloads/releases/toolchains/$BOOTLIN_ARCH/tarballs/"
TARBALL_URL=$(curl -fsSL "$TARBALL_BASE" | grep -oE "${BOOTLIN_ARCH}--musl--stable-[^\"]+\\.tar\\.xz" | sort -r | head -n1)
[ -z "$TARBALL_URL" ] && { echo "Failed to locate Bootlin musl toolchain for arch=$BOOTLIN_ARCH" >&2; exit 1; }
echo "Downloading: $TARBALL_URL"
cd /tmp
curl -fL -sS -o "$(basename "$TARBALL_URL")" "$TARBALL_BASE/$TARBALL_URL"
tar -xf "$(basename "$TARBALL_URL")"
TOOLCHAIN_DIR=$(find . -maxdepth 1 -type d -name "${BOOTLIN_ARCH}--musl--stable-*" | head -n1)
export PATH="$(realpath "$TOOLCHAIN_DIR")/bin:$PATH"
export CC=$(realpath "$(find "$TOOLCHAIN_DIR/bin" -name '*-gcc.br_real' -type f -executable | head -n1)")
[ -z "$CC" ] && { echo "No gcc.br_real found in $TOOLCHAIN_DIR/bin" >&2; exit 1; }
cd -
go build -ldflags "-w -s -linkmode external -extldflags '-static'" -o xui-release -v main.go
file xui-release
ldd xui-release || echo "Static binary confirmed"
mkdir x-ui
cp xui-release x-ui/
cp x-ui.service.debian x-ui/
cp x-ui.service.arch x-ui/
cp x-ui.service.rhel x-ui/
cp x-ui.sh x-ui/
mv x-ui/xui-release x-ui/x-ui
mkdir x-ui/bin
cd x-ui/bin
# Download dependencies
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v26.2.6/"
if [ "${{ matrix.platform }}" == "amd64" ]; then
wget -q ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip
rm -f Xray-linux-64.zip
elif [ "${{ matrix.platform }}" == "arm64" ]; then
wget -q ${Xray_URL}Xray-linux-arm64-v8a.zip
unzip Xray-linux-arm64-v8a.zip
rm -f Xray-linux-arm64-v8a.zip
elif [ "${{ matrix.platform }}" == "armv7" ]; then
wget -q ${Xray_URL}Xray-linux-arm32-v7a.zip
unzip Xray-linux-arm32-v7a.zip
rm -f Xray-linux-arm32-v7a.zip
elif [ "${{ matrix.platform }}" == "armv6" ]; then
wget -q ${Xray_URL}Xray-linux-arm32-v6.zip
unzip Xray-linux-arm32-v6.zip
rm -f Xray-linux-arm32-v6.zip
elif [ "${{ matrix.platform }}" == "386" ]; then
wget -q ${Xray_URL}Xray-linux-32.zip
unzip Xray-linux-32.zip
rm -f Xray-linux-32.zip
elif [ "${{ matrix.platform }}" == "armv5" ]; then
wget -q ${Xray_URL}Xray-linux-arm32-v5.zip
unzip Xray-linux-arm32-v5.zip
rm -f Xray-linux-arm32-v5.zip
elif [ "${{ matrix.platform }}" == "s390x" ]; then
wget -q ${Xray_URL}Xray-linux-s390x.zip
unzip Xray-linux-s390x.zip
rm -f Xray-linux-s390x.zip
fi
rm -f geoip.dat geosite.dat
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
mv xray xray-linux-${{ matrix.platform }}
cd ../..
- name: Package
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: x-ui-linux-${{ matrix.platform }}
path: ./x-ui-linux-${{ matrix.platform }}.tar.gz
- name: Upload files to GH release
uses: svenstaro/upload-release-action@v2
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref_name }}
file: x-ui-linux-${{ matrix.platform }}.tar.gz
asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
overwrite: true
prerelease: true
# =================================
# Windows Build
# =================================
build-windows:
name: Build for Windows
needs: analyze
permissions:
contents: write
strategy:
matrix:
platform:
- amd64
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >-
mingw-w64-x86_64-gcc
mingw-w64-x86_64-sqlite3
mingw-w64-x86_64-pkg-config
- name: Build 3X-UI for Windows (CGO)
shell: msys2 {0}
run: |
export PATH="/c/hostedtoolcache/windows/go/$(ls /c/hostedtoolcache/windows/go | sort -V | tail -n1)/x64/bin:$PATH"
export CGO_ENABLED=1
export GOOS=windows
export GOARCH=amd64
export CC=x86_64-w64-mingw32-gcc
which go
go version
gcc --version
go build -ldflags "-w -s" -o xui-release.exe -v main.go
- name: Copy and download resources
shell: pwsh
run: |
mkdir x-ui
Copy-Item xui-release.exe x-ui\x-ui.exe
mkdir x-ui\bin
cd x-ui\bin
# Download Xray for Windows
$Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.2.6/"
Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
Remove-Item "Xray-windows-64.zip"
Remove-Item geoip.dat, geosite.dat -ErrorAction SilentlyContinue
Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip.dat"
Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite.dat"
Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -OutFile "geoip_IR.dat"
Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -OutFile "geosite_IR.dat"
Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip_RU.dat"
Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite_RU.dat"
Rename-Item xray.exe xray-windows-amd64.exe
cd ..
Copy-Item -Path ..\windows_files\* -Destination . -Recurse
cd ..
- name: Package to Zip
shell: pwsh
run: |
Compress-Archive -Path .\x-ui -DestinationPath "x-ui-windows-amd64.zip"
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: x-ui-windows-amd64
path: ./x-ui-windows-amd64.zip
- name: Upload files to GH release
uses: svenstaro/upload-release-action@v2
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref_name }}
file: x-ui-windows-amd64.zip
asset_name: x-ui-windows-amd64.zip
overwrite: true
prerelease: true
================================================
FILE: .gitignore
================================================
# Ignore editor and IDE settings
.idea/
.vscode/
.cache/
.sync*
# Ignore log files
*.log
# Ignore temporary files
tmp/
*.tar.gz
# Ignore build and distribution directories
backup/
bin/
dist/
release/
node_modules/
# Ignore compiled binaries
main
# Ignore script and executable files
/release.sh
/x-ui
# Ignore OS specific files
.DS_Store
Thumbs.db
# Ignore Go build files
*.exe
x-ui.db
# Ignore Docker specific files
docker-compose.override.yml
# Ignore .env (Environment Variables) file
.env
================================================
FILE: CONTRIBUTING.md
================================================
## Local Development Setup
- Create a directory named `x-ui` in the project root
- Rename `.env.example` to `.env `
- Run `main.go`
================================================
FILE: DockerEntrypoint.sh
================================================
#!/bin/sh
# Start fail2ban
[ $XUI_ENABLE_FAIL2BAN == "true" ] && fail2ban-client -x start
# Run x-ui
exec /app/x-ui
================================================
FILE: DockerInit.sh
================================================
#!/bin/sh
case $1 in
amd64)
ARCH="64"
FNAME="amd64"
;;
i386)
ARCH="32"
FNAME="i386"
;;
armv8 | arm64 | aarch64)
ARCH="arm64-v8a"
FNAME="arm64"
;;
armv7 | arm | arm32)
ARCH="arm32-v7a"
FNAME="arm32"
;;
armv6)
ARCH="arm32-v6"
FNAME="armv6"
;;
*)
ARCH="64"
FNAME="amd64"
;;
esac
mkdir -p build/bin
cd build/bin
curl -sfLRO "https://github.com/XTLS/Xray-core/releases/download/v26.2.6/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}"
curl -sfLRO https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
curl -sfLRO https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
curl -sfLRo geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
curl -sfLRo geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
curl -sfLRo geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
curl -sfLRo geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
cd ../../
================================================
FILE: Dockerfile
================================================
# ========================================================
# Stage: Builder
# ========================================================
FROM golang:1.26-alpine AS builder
WORKDIR /app
ARG TARGETARCH
RUN apk --no-cache --update add \
build-base \
gcc \
curl \
unzip
COPY . .
ENV CGO_ENABLED=1
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN go build -ldflags "-w -s" -o build/x-ui main.go
RUN ./DockerInit.sh "$TARGETARCH"
# ========================================================
# Stage: Final Image of 3x-ui
# ========================================================
FROM alpine
ENV TZ=Asia/Tehran
WORKDIR /app
RUN apk add --no-cache --update \
ca-certificates \
tzdata \
fail2ban \
bash \
curl \
openssl
COPY --from=builder /app/build/ /app/
COPY --from=builder /app/DockerEntrypoint.sh /app/
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
# Configure fail2ban
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
&& cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
&& sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
&& sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
&& sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf
RUN chmod +x \
/app/DockerEntrypoint.sh \
/app/x-ui \
/usr/bin/x-ui
ENV XUI_ENABLE_FAIL2BAN="true"
EXPOSE 2053
VOLUME [ "/etc/x-ui" ]
CMD [ "./x-ui" ]
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: README.ar_EG.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — لوحة تحكم متقدمة مفتوحة المصدر تعتمد على الويب مصممة لإدارة خادم Xray-core. توفر واجهة سهلة الاستخدام لتكوين ومراقبة بروتوكولات VPN والوكيل المختلفة.
> [!IMPORTANT]
> هذا المشروع مخصص للاستخدام الشخصي والاتصال فقط، يرجى عدم استخدامه لأغراض غير قانونية، يرجى عدم استخدامه في بيئة الإنتاج.
كمشروع محسن من مشروع X-UI الأصلي، يوفر 3X-UI استقرارًا محسنًا ودعمًا أوسع للبروتوكولات وميزات إضافية.
## البدء السريع
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
للحصول على الوثائق الكاملة، يرجى زيارة [ويكي المشروع](https://github.com/MHSanaei/3x-ui/wiki).
## شكر خاص إلى
- [alireza0](https://github.com/alireza0/)
## الاعتراف
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (الترخيص: **GPL-3.0**): _قواعد توجيه v2ray/xray و v2ray/xray-clients المحسنة مع النطاقات الإيرانية المدمجة وتركيز على الأمان وحظر الإعلانات._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (الترخيص: **GPL-3.0**): _يحتوي هذا المستودع على قواعد توجيه V2Ray محدثة تلقائيًا بناءً على بيانات النطاقات والعناوين المحظورة في روسيا._
## دعم المشروع
**إذا كان هذا المشروع مفيدًا لك، فقد ترغب في إعطائه**:star2:
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## النجوم عبر الزمن
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: README.es_ES.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — panel de control avanzado basado en web de código abierto diseñado para gestionar el servidor Xray-core. Ofrece una interfaz fácil de usar para configurar y monitorear varios protocolos VPN y proxy.
> [!IMPORTANT]
> Este proyecto es solo para uso personal y comunicación, por favor no lo use para fines ilegales, por favor no lo use en un entorno de producción.
Como una versión mejorada del proyecto X-UI original, 3X-UI proporciona mayor estabilidad, soporte más amplio de protocolos y características adicionales.
## Inicio Rápido
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
Para documentación completa, visita la [Wiki del proyecto](https://github.com/MHSanaei/3x-ui/wiki).
## Un Agradecimiento Especial a
- [alireza0](https://github.com/alireza0/)
## Reconocimientos
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Licencia: **GPL-3.0**): _Reglas de enrutamiento mejoradas para v2ray/xray y v2ray/xray-clients con dominios iraníes incorporados y un enfoque en seguridad y bloqueo de anuncios._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Licencia: **GPL-3.0**): _Este repositorio contiene reglas de enrutamiento V2Ray actualizadas automáticamente basadas en datos de dominios y direcciones bloqueadas en Rusia._
## Apoyar el Proyecto
**Si este proyecto te es útil, puedes darle una**:star2:
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## Estrellas a lo Largo del Tiempo
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: README.fa_IR.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — یک پنل کنترل پیشرفته مبتنی بر وب با کد باز که برای مدیریت سرور Xray-core طراحی شده است. این پنل یک رابط کاربری آسان برای پیکربندی و نظارت بر پروتکلهای مختلف VPN و پراکسی ارائه میدهد.
> [!IMPORTANT]
> این پروژه فقط برای استفاده شخصی و ارتباطات است، لطفاً از آن برای اهداف غیرقانونی استفاده نکنید، لطفاً از آن در محیط تولید استفاده نکنید.
به عنوان یک نسخه بهبود یافته از پروژه اصلی X-UI، 3X-UI پایداری بهتر، پشتیبانی گستردهتر از پروتکلها و ویژگیهای اضافی را ارائه میدهد.
## شروع سریع
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
برای مستندات کامل، لطفاً به [ویکی پروژه](https://github.com/MHSanaei/3x-ui/wiki) مراجعه کنید.
## تشکر ویژه از
- [alireza0](https://github.com/alireza0/)
## قدردانی
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (مجوز: **GPL-3.0**): _قوانین مسیریابی بهبود یافته v2ray/xray و v2ray/xray-clients با دامنههای ایرانی داخلی و تمرکز بر امنیت و مسدود کردن تبلیغات._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (مجوز: **GPL-3.0**): _این مخزن شامل قوانین مسیریابی V2Ray بهروزرسانی شده خودکار بر اساس دادههای دامنهها و آدرسهای مسدود شده در روسیه است._
## پشتیبانی از پروژه
**اگر این پروژه برای شما مفید است، میتوانید به آن یک**:star2: بدهید
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## ستارهها در طول زمان
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: README.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — advanced, open-source web-based control panel designed for managing Xray-core server. It offers a user-friendly interface for configuring and monitoring various VPN and proxy protocols.
> [!IMPORTANT]
> This project is only for personal usage, please do not use it for illegal purposes, and please do not use it in a production environment.
As an enhanced fork of the original X-UI project, 3X-UI provides improved stability, broader protocol support, and additional features.
## Quick Start
```bash
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
For full documentation, please visit the [project Wiki](https://github.com/MHSanaei/3x-ui/wiki).
## A Special Thanks to
- [alireza0](https://github.com/alireza0/)
## Acknowledgment
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _This repository contains automatically updated V2Ray routing rules based on data on blocked domains and addresses in Russia._
## Support project
**If this project is helpful to you, you may wish to give it a**:star2:
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## Stargazers over Time
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: README.ru_RU.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — продвинутая панель управления с открытым исходным кодом на основе веб-интерфейса, разработанная для управления сервером Xray-core. Предоставляет удобный интерфейс для настройки и мониторинга различных VPN и прокси-протоколов.
> [!IMPORTANT]
> Этот проект предназначен только для личного использования, пожалуйста, не используйте его в незаконных целях и в производственной среде.
Как улучшенная версия оригинального проекта X-UI, 3X-UI обеспечивает повышенную стабильность, более широкую поддержку протоколов и дополнительные функции.
## Быстрый старт
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
Полную документацию смотрите в [вики проекта](https://github.com/MHSanaei/3x-ui/wiki).
## Особая благодарность
- [alireza0](https://github.com/alireza0/)
## Благодарности
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Лицензия: **GPL-3.0**): _Улучшенные правила маршрутизации для v2ray/xray и v2ray/xray-clients со встроенными иранскими доменами и фокусом на безопасность и блокировку рекламы._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Лицензия: **GPL-3.0**): _Этот репозиторий содержит автоматически обновляемые правила маршрутизации V2Ray на основе данных о заблокированных доменах и адресах в России._
## Поддержка проекта
**Если этот проект полезен для вас, вы можете поставить ему**:star2:
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## Звезды с течением времени
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: README.zh_CN.md
================================================
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
<img alt="3x-ui" src="./media/3x-ui-light.png">
</picture>
</p>
[](https://github.com/MHSanaei/3x-ui/releases)
[](https://github.com/MHSanaei/3x-ui/actions)
[](#)
[](https://github.com/MHSanaei/3x-ui/releases/latest)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v2)
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v2)
**3X-UI** — 一个基于网页的高级开源控制面板,专为管理 Xray-core 服务器而设计。它提供了用户友好的界面,用于配置和监控各种 VPN 和代理协议。
> [!IMPORTANT]
> 本项目仅用于个人使用和通信,请勿将其用于非法目的,请勿在生产环境中使用。
作为原始 X-UI 项目的增强版本,3X-UI 提供了更好的稳定性、更广泛的协议支持和额外的功能。
## 快速开始
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
完整文档请参阅 [项目Wiki](https://github.com/MHSanaei/3x-ui/wiki)。
## 特别感谢
- [alireza0](https://github.com/alireza0/)
## 致谢
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (许可证: **GPL-3.0**): _增强的 v2ray/xray 和 v2ray/xray-clients 路由规则,内置伊朗域名,专注于安全性和广告拦截。_
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (许可证: **GPL-3.0**): _此仓库包含基于俄罗斯被阻止域名和地址数据自动更新的 V2Ray 路由规则。_
## 支持项目
**如果这个项目对您有帮助,您可以给它一个**:star2:
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
</a>
</br>
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
</a>
## 随时间变化的星标数
[](https://starchart.cc/MHSanaei/3x-ui)
================================================
FILE: config/config.go
================================================
// Package config provides configuration management utilities for the 3x-ui panel,
// including version information, logging levels, database paths, and environment variable handling.
package config
import (
_ "embed"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
)
//go:embed version
var version string
//go:embed name
var name string
// LogLevel represents the logging level for the application.
type LogLevel string
// Logging level constants
const (
Debug LogLevel = "debug"
Info LogLevel = "info"
Notice LogLevel = "notice"
Warning LogLevel = "warning"
Error LogLevel = "error"
)
// GetVersion returns the version string of the 3x-ui application.
func GetVersion() string {
return strings.TrimSpace(version)
}
// GetName returns the name of the 3x-ui application.
func GetName() string {
return strings.TrimSpace(name)
}
// GetLogLevel returns the current logging level based on environment variables or defaults to Info.
func GetLogLevel() LogLevel {
if IsDebug() {
return Debug
}
logLevel := os.Getenv("XUI_LOG_LEVEL")
if logLevel == "" {
return Info
}
return LogLevel(logLevel)
}
// IsDebug returns true if debug mode is enabled via the XUI_DEBUG environment variable.
func IsDebug() bool {
return os.Getenv("XUI_DEBUG") == "true"
}
// GetBinFolderPath returns the path to the binary folder, defaulting to "bin" if not set via XUI_BIN_FOLDER.
func GetBinFolderPath() string {
binFolderPath := os.Getenv("XUI_BIN_FOLDER")
if binFolderPath == "" {
binFolderPath = "bin"
}
return binFolderPath
}
func getBaseDir() string {
exePath, err := os.Executable()
if err != nil {
return "."
}
exeDir := filepath.Dir(exePath)
exeDirLower := strings.ToLower(filepath.ToSlash(exeDir))
if strings.Contains(exeDirLower, "/appdata/local/temp/") || strings.Contains(exeDirLower, "/go-build") {
wd, err := os.Getwd()
if err != nil {
return "."
}
return wd
}
return exeDir
}
// GetDBFolderPath returns the path to the database folder based on environment variables or platform defaults.
func GetDBFolderPath() string {
dbFolderPath := os.Getenv("XUI_DB_FOLDER")
if dbFolderPath != "" {
return dbFolderPath
}
if runtime.GOOS == "windows" {
return getBaseDir()
}
return "/etc/x-ui"
}
// GetDBPath returns the full path to the database file.
func GetDBPath() string {
return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
}
// GetLogFolder returns the path to the log folder based on environment variables or platform defaults.
func GetLogFolder() string {
logFolderPath := os.Getenv("XUI_LOG_FOLDER")
if logFolderPath != "" {
return logFolderPath
}
if runtime.GOOS == "windows" {
return filepath.Join(".", "log")
}
return "/var/log/x-ui"
}
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Sync()
}
func init() {
if runtime.GOOS != "windows" {
return
}
if os.Getenv("XUI_DB_FOLDER") != "" {
return
}
oldDBFolder := "/etc/x-ui"
oldDBPath := fmt.Sprintf("%s/%s.db", oldDBFolder, GetName())
newDBFolder := GetDBFolderPath()
newDBPath := fmt.Sprintf("%s/%s.db", newDBFolder, GetName())
_, err := os.Stat(newDBPath)
if err == nil {
return // new exists
}
_, err = os.Stat(oldDBPath)
if os.IsNotExist(err) {
return // old does not exist
}
_ = copyFile(oldDBPath, newDBPath) // ignore error
}
================================================
FILE: config/name
================================================
x-ui
================================================
FILE: config/version
================================================
2.8.11
================================================
FILE: database/db.go
================================================
// Package database provides database initialization, migration, and management utilities
// for the 3x-ui panel using GORM with SQLite.
package database
import (
"bytes"
"errors"
"io"
"io/fs"
"log"
"os"
"path"
"slices"
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/mhsanaei/3x-ui/v2/database/model"
"github.com/mhsanaei/3x-ui/v2/util/crypto"
"github.com/mhsanaei/3x-ui/v2/xray"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var db *gorm.DB
const (
defaultUsername = "admin"
defaultPassword = "admin"
)
func initModels() error {
models := []any{
&model.User{},
&model.Inbound{},
&model.OutboundTraffics{},
&model.Setting{},
&model.InboundClientIps{},
&xray.ClientTraffic{},
&model.HistoryOfSeeders{},
}
for _, model := range models {
if err := db.AutoMigrate(model); err != nil {
log.Printf("Error auto migrating model: %v", err)
return err
}
}
return nil
}
// initUser creates a default admin user if the users table is empty.
func initUser() error {
empty, err := isTableEmpty("users")
if err != nil {
log.Printf("Error checking if users table is empty: %v", err)
return err
}
if empty {
hashedPassword, err := crypto.HashPasswordAsBcrypt(defaultPassword)
if err != nil {
log.Printf("Error hashing default password: %v", err)
return err
}
user := &model.User{
Username: defaultUsername,
Password: hashedPassword,
}
return db.Create(user).Error
}
return nil
}
// runSeeders migrates user passwords to bcrypt and records seeder execution to prevent re-running.
func runSeeders(isUsersEmpty bool) error {
empty, err := isTableEmpty("history_of_seeders")
if err != nil {
log.Printf("Error checking if users table is empty: %v", err)
return err
}
if empty && isUsersEmpty {
hashSeeder := &model.HistoryOfSeeders{
SeederName: "UserPasswordHash",
}
return db.Create(hashSeeder).Error
} else {
var seedersHistory []string
db.Model(&model.HistoryOfSeeders{}).Pluck("seeder_name", &seedersHistory)
if !slices.Contains(seedersHistory, "UserPasswordHash") && !isUsersEmpty {
var users []model.User
db.Find(&users)
for _, user := range users {
hashedPassword, err := crypto.HashPasswordAsBcrypt(user.Password)
if err != nil {
log.Printf("Error hashing password for user '%s': %v", user.Username, err)
return err
}
db.Model(&user).Update("password", hashedPassword)
}
hashSeeder := &model.HistoryOfSeeders{
SeederName: "UserPasswordHash",
}
return db.Create(hashSeeder).Error
}
}
return nil
}
// isTableEmpty returns true if the named table contains zero rows.
func isTableEmpty(tableName string) (bool, error) {
var count int64
err := db.Table(tableName).Count(&count).Error
return count == 0, err
}
// InitDB sets up the database connection, migrates models, and runs seeders.
func InitDB(dbPath string) error {
dir := path.Dir(dbPath)
err := os.MkdirAll(dir, fs.ModePerm)
if err != nil {
return err
}
var gormLogger logger.Interface
if config.IsDebug() {
gormLogger = logger.Default
} else {
gormLogger = logger.Discard
}
c := &gorm.Config{
Logger: gormLogger,
}
db, err = gorm.Open(sqlite.Open(dbPath), c)
if err != nil {
return err
}
if err := initModels(); err != nil {
return err
}
isUsersEmpty, err := isTableEmpty("users")
if err != nil {
return err
}
if err := initUser(); err != nil {
return err
}
return runSeeders(isUsersEmpty)
}
// CloseDB closes the database connection if it exists.
func CloseDB() error {
if db != nil {
sqlDB, err := db.DB()
if err != nil {
return err
}
return sqlDB.Close()
}
return nil
}
// GetDB returns the global GORM database instance.
func GetDB() *gorm.DB {
return db
}
// IsNotFound checks if the given error is a GORM record not found error.
func IsNotFound(err error) bool {
return err == gorm.ErrRecordNotFound
}
// IsSQLiteDB checks if the given file is a valid SQLite database by reading its signature.
func IsSQLiteDB(file io.ReaderAt) (bool, error) {
signature := []byte("SQLite format 3\x00")
buf := make([]byte, len(signature))
_, err := file.ReadAt(buf, 0)
if err != nil {
return false, err
}
return bytes.Equal(buf, signature), nil
}
// Checkpoint performs a WAL checkpoint on the SQLite database to ensure data consistency.
func Checkpoint() error {
// Update WAL
err := db.Exec("PRAGMA wal_checkpoint;").Error
if err != nil {
return err
}
return nil
}
// ValidateSQLiteDB opens the provided sqlite DB path with a throw-away connection
// and runs a PRAGMA integrity_check to ensure the file is structurally sound.
// It does not mutate global state or run migrations.
func ValidateSQLiteDB(dbPath string) error {
if _, err := os.Stat(dbPath); err != nil { // file must exist
return err
}
gdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{Logger: logger.Discard})
if err != nil {
return err
}
sqlDB, err := gdb.DB()
if err != nil {
return err
}
defer sqlDB.Close()
var res string
if err := gdb.Raw("PRAGMA integrity_check;").Scan(&res).Error; err != nil {
return err
}
if res != "ok" {
return errors.New("sqlite integrity check failed: " + res)
}
return nil
}
================================================
FILE: database/model/model.go
================================================
// Package model defines the database models and data structures used by the 3x-ui panel.
package model
import (
"fmt"
"github.com/mhsanaei/3x-ui/v2/util/json_util"
"github.com/mhsanaei/3x-ui/v2/xray"
)
// Protocol represents the protocol type for Xray inbounds.
type Protocol string
// Protocol constants for different Xray inbound protocols
const (
VMESS Protocol = "vmess"
VLESS Protocol = "vless"
Tunnel Protocol = "tunnel"
HTTP Protocol = "http"
Trojan Protocol = "trojan"
Shadowsocks Protocol = "shadowsocks"
Mixed Protocol = "mixed"
WireGuard Protocol = "wireguard"
)
// User represents a user account in the 3x-ui panel.
type User struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
Username string `json:"username"`
Password string `json:"password"`
}
// Inbound represents an Xray inbound configuration with traffic statistics and settings.
type Inbound struct {
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier
UserId int `json:"-"` // Associated user ID
Up int64 `json:"up" form:"up"` // Upload traffic in bytes
Down int64 `json:"down" form:"down"` // Download traffic in bytes
Total int64 `json:"total" form:"total"` // Total traffic limit in bytes
AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` // All-time traffic usage
Remark string `json:"remark" form:"remark"` // Human-readable remark
Enable bool `json:"enable" form:"enable" gorm:"index:idx_enable_traffic_reset,priority:1"` // Whether the inbound is enabled
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp
TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never;index:idx_enable_traffic_reset,priority:2"` // Traffic reset schedule
LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"` // Last traffic reset timestamp
ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` // Client traffic statistics
// Xray configuration fields
Listen string `json:"listen" form:"listen"`
Port int `json:"port" form:"port"`
Protocol Protocol `json:"protocol" form:"protocol"`
Settings string `json:"settings" form:"settings"`
StreamSettings string `json:"streamSettings" form:"streamSettings"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Sniffing string `json:"sniffing" form:"sniffing"`
}
// OutboundTraffics tracks traffic statistics for Xray outbound connections.
type OutboundTraffics struct {
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Up int64 `json:"up" form:"up" gorm:"default:0"`
Down int64 `json:"down" form:"down" gorm:"default:0"`
Total int64 `json:"total" form:"total" gorm:"default:0"`
}
// InboundClientIps stores IP addresses associated with inbound clients for access control.
type InboundClientIps struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"`
Ips string `json:"ips" form:"ips"`
}
// HistoryOfSeeders tracks which database seeders have been executed to prevent re-running.
type HistoryOfSeeders struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
SeederName string `json:"seederName"`
}
// GenXrayInboundConfig generates an Xray inbound configuration from the Inbound model.
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
listen := i.Listen
// Default to 0.0.0.0 (all interfaces) when listen is empty
// This ensures proper dual-stack IPv4/IPv6 binding in systems where bindv6only=0
if listen == "" {
listen = "0.0.0.0"
}
listen = fmt.Sprintf("\"%v\"", listen)
return &xray.InboundConfig{
Listen: json_util.RawMessage(listen),
Port: i.Port,
Protocol: string(i.Protocol),
Settings: json_util.RawMessage(i.Settings),
StreamSettings: json_util.RawMessage(i.StreamSettings),
Tag: i.Tag,
Sniffing: json_util.RawMessage(i.Sniffing),
}
}
// Setting stores key-value configuration settings for the 3x-ui panel.
type Setting struct {
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
Key string `json:"key" form:"key"`
Value string `json:"value" form:"value"`
}
// Client represents a client configuration for Xray inbounds with traffic limits and settings.
type Client struct {
ID string `json:"id"` // Unique client identifier
Security string `json:"security"` // Security method (e.g., "auto", "aes-128-gcm")
Password string `json:"password"` // Client password
Flow string `json:"flow"` // Flow control (XTLS)
Email string `json:"email"` // Client email identifier
LimitIP int `json:"limitIp"` // IP limit for this client
TotalGB int64 `json:"totalGB" form:"totalGB"` // Total traffic limit in GB
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp
Enable bool `json:"enable" form:"enable"` // Whether the client is enabled
TgID int64 `json:"tgId" form:"tgId"` // Telegram user ID for notifications
SubID string `json:"subId" form:"subId"` // Subscription identifier
Comment string `json:"comment" form:"comment"` // Client comment
Reset int `json:"reset" form:"reset"` // Reset period in days
CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp
UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp
}
================================================
FILE: docker-compose.yml
================================================
services:
3xui:
build:
context: .
dockerfile: ./Dockerfile
container_name: 3xui_app
# hostname: yourhostname <- optional
volumes:
- $PWD/db/:/etc/x-ui/
- $PWD/cert/:/root/cert/
environment:
XRAY_VMESS_AEAD_FORCED: "false"
XUI_ENABLE_FAIL2BAN: "true"
tty: true
network_mode: host
restart: unless-stopped
================================================
FILE: go.mod
================================================
module github.com/mhsanaei/3x-ui/v2
go 1.26.0
require (
github.com/gin-contrib/gzip v1.2.5
github.com/gin-contrib/sessions v1.0.4
github.com/gin-gonic/gin v1.12.0
github.com/go-ldap/ldap/v3 v3.4.12
github.com/goccy/go-json v0.10.5
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/joho/godotenv v1.5.1
github.com/mymmrac/telego v1.7.0
github.com/nicksnyder/go-i18n/v2 v2.6.1
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.2.4
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.26.2
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/valyala/fasthttp v1.69.0
github.com/xlzd/gotp v0.1.0
github.com/xtls/xray-core v1.260206.0
go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.48.0
golang.org/x/sys v0.41.0
golang.org/x/text v0.34.0
google.golang.org/grpc v1.79.1
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.1
)
require (
github.com/Azure/go-ntlmssp v0.1.0 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.4.0 // indirect
github.com/grbit/go-json v0.11.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.34 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pires/go-proxyproto v0.11.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/refraction-networking/utls v1.8.2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/sagernet/sing v0.8.1 // indirect
github.com/sagernet/sing-shadowsocks v0.2.9 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fastjson v1.6.10 // indirect
github.com/vishvananda/netlink v1.3.1 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.24.0 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.42.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect
lukechampine.com/blake3 v1.4.1 // indirect
)
================================================
FILE: go.sum
================================================
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/qnM3MKf4kroW75n+phO9Qp6nigJKZ1E=
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw=
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc=
github.com/grbit/go-json v0.11.0/go.mod h1:IYpHsdybQ386+6g3VE6AXQ3uTGa5mquBme5/ZWmtzek=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mymmrac/telego v1.7.0 h1:yRO/l00tFGG4nY66ufUKb4ARqv7qx9+LsjQv/b0NEyo=
github.com/mymmrac/telego v1.7.0/go.mod h1:pdLV346EgVuq7Xrh3kMggeBiazeHhsdEoK0RTEOPXRM=
github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ=
github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sagernet/sing v0.8.1 h1:Li+zg4xdiMsvdX4j50TPqmSG8LF/TB9US2qlAN40izU=
github.com/sagernet/sing v0.8.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-shadowsocks v0.2.9 h1:Paep5zCszRKsEn8587O0MnhFWKJwDW1Y4zOYYlIxMkM=
github.com/sagernet/sing-shadowsocks v0.2.9/go.mod h1:TE/Z6401Pi8tgr0nBZcM/xawAI6u3F6TTbz4nH/qw+8=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 h1:UXjrmniKlY+ZbIqpN91lejB3pszQQQRVu1vqH/p/aGM=
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
github.com/xtls/xray-core v1.260206.0 h1:gY8IV6u76CW93txL9QmacgZ0Udxr2Q3e9qUxXAhdHqI=
github.com/xtls/xray-core v1.260206.0/go.mod h1:GyFIgVGRJkt3eyV/NMcdxOKXcJPqGGpyupHzy16uJhU=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 h1:Lk6hARj5UPY47dBep70OD/TIMwikJ5fGUGX0Rm3Xigk=
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
================================================
FILE: install.sh
================================================
#!/bin/bash
red='\033[0;31m'
green='\033[0;32m'
blue='\033[0;34m'
yellow='\033[0;33m'
plain='\033[0m'
cur_dir=$(pwd)
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
xui_service="${XUI_SERVICE:=/etc/systemd/system}"
# check root
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
# Check OS and set release variable
if [[ -f /etc/os-release ]]; then
source /etc/os-release
release=$ID
elif [[ -f /usr/lib/os-release ]]; then
source /usr/lib/os-release
release=$ID
else
echo "Failed to check the system OS, please contact the author!" >&2
exit 1
fi
echo "The OS release is: $release"
arch() {
case "$(uname -m)" in
x86_64 | x64 | amd64) echo 'amd64' ;;
i*86 | x86) echo '386' ;;
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
armv7* | armv7 | arm) echo 'armv7' ;;
armv6* | armv6) echo 'armv6' ;;
armv5* | armv5) echo 'armv5' ;;
s390x) echo 's390x' ;;
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
esac
}
echo "Arch: $(arch)"
# Simple helpers
is_ipv4() {
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
}
is_ipv6() {
[[ "$1" =~ : ]] && return 0 || return 1
}
is_ip() {
is_ipv4 "$1" || is_ipv6 "$1"
}
is_domain() {
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
}
# Port helpers
is_port_in_use() {
local port="$1"
if command -v ss >/dev/null 2>&1; then
ss -ltn 2>/dev/null | awk -v p=":${port}$" '$4 ~ p {exit 0} END {exit 1}'
return
fi
if command -v netstat >/dev/null 2>&1; then
netstat -lnt 2>/dev/null | awk -v p=":${port} " '$4 ~ p {exit 0} END {exit 1}'
return
fi
if command -v lsof >/dev/null 2>&1; then
lsof -nP -iTCP:${port} -sTCP:LISTEN >/dev/null 2>&1 && return 0
fi
return 1
}
install_base() {
case "${release}" in
ubuntu | debian | armbian)
apt-get update && apt-get install -y -q cron curl tar tzdata socat ca-certificates
;;
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
dnf -y update && dnf install -y -q curl tar tzdata socat ca-certificates
;;
centos)
if [[ "${VERSION_ID}" =~ ^7 ]]; then
yum -y update && yum install -y curl tar tzdata socat ca-certificates
else
dnf -y update && dnf install -y -q curl tar tzdata socat ca-certificates
fi
;;
arch | manjaro | parch)
pacman -Syu && pacman -Syu --noconfirm curl tar tzdata socat ca-certificates
;;
opensuse-tumbleweed | opensuse-leap)
zypper refresh && zypper -q install -y curl tar timezone socat ca-certificates
;;
alpine)
apk update && apk add curl tar tzdata socat ca-certificates
;;
*)
apt-get update && apt-get install -y -q curl tar tzdata socat ca-certificates
;;
esac
}
gen_random_string() {
local length="$1"
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | fold -w "$length" | head -n 1)
echo "$random_string"
}
install_acme() {
echo -e "${green}Installing acme.sh for SSL certificate management...${plain}"
cd ~ || return 1
curl -s https://get.acme.sh | sh >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "${red}Failed to install acme.sh${plain}"
return 1
else
echo -e "${green}acme.sh installed successfully${plain}"
fi
return 0
}
setup_ssl_certificate() {
local domain="$1"
local server_ip="$2"
local existing_port="$3"
local existing_webBasePath="$4"
echo -e "${green}Setting up SSL certificate...${plain}"
# Check if acme.sh is installed
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
install_acme
if [ $? -ne 0 ]; then
echo -e "${yellow}Failed to install acme.sh, skipping SSL setup${plain}"
return 1
fi
fi
# Create certificate directory
local certPath="/root/cert/${domain}"
mkdir -p "$certPath"
# Issue certificate
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
if [ $? -ne 0 ]; then
echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
echo -e "${yellow}Please ensure port 80 is open and try again later with: x-ui${plain}"
rm -rf ~/.acme.sh/${domain} 2>/dev/null
rm -rf "$certPath" 2>/dev/null
return 1
fi
# Install certificate
~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \
--fullchain-file /root/cert/${domain}/fullchain.pem \
--reloadcmd "systemctl restart x-ui" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "${yellow}Failed to install certificate${plain}"
return 1
fi
# Enable auto-renew
~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
# Secure permissions: private key readable only by owner
chmod 600 $certPath/privkey.pem 2>/dev/null
chmod 644 $certPath/fullchain.pem 2>/dev/null
# Set certificate for panel
local webCertFile="/root/cert/${domain}/fullchain.pem"
local webKeyFile="/root/cert/${domain}/privkey.pem"
if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" >/dev/null 2>&1
echo -e "${green}SSL certificate installed and configured successfully!${plain}"
return 0
else
echo -e "${yellow}Certificate files not found${plain}"
return 1
fi
}
# Issue Let's Encrypt IP certificate with shortlived profile (~6 days validity)
# Requires acme.sh and port 80 open for HTTP-01 challenge
setup_ip_certificate() {
local ipv4="$1"
local ipv6="$2" # optional
echo -e "${green}Setting up Let's Encrypt IP certificate (shortlived profile)...${plain}"
echo -e "${yellow}Note: IP certificates are valid for ~6 days and will auto-renew.${plain}"
echo -e "${yellow}Default listener is port 80. If you choose another port, ensure external port 80 forwards to it.${plain}"
# Check for acme.sh
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
install_acme
if [ $? -ne 0 ]; then
echo -e "${red}Failed to install acme.sh${plain}"
return 1
fi
fi
# Validate IP address
if [[ -z "$ipv4" ]]; then
echo -e "${red}IPv4 address is required${plain}"
return 1
fi
if ! is_ipv4 "$ipv4"; then
echo -e "${red}Invalid IPv4 address: $ipv4${plain}"
return 1
fi
# Create certificate directory
local certDir="/root/cert/ip"
mkdir -p "$certDir"
# Build domain arguments
local domain_args="-d ${ipv4}"
if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
domain_args="${domain_args} -d ${ipv6}"
echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
fi
# Set reload command for auto-renewal (add || true so it doesn't fail during first install)
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null || true"
# Choose port for HTTP-01 listener (default 80, prompt override)
local WebPort=""
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
WebPort="${WebPort:-80}"
if ! [[ "${WebPort}" =~ ^[0-9]+$ ]] || ((WebPort < 1 || WebPort > 65535)); then
echo -e "${red}Invalid port provided. Falling back to 80.${plain}"
WebPort=80
fi
echo -e "${green}Using port ${WebPort} for standalone validation.${plain}"
if [[ "${WebPort}" -ne 80 ]]; then
echo -e "${yellow}Reminder: Let's Encrypt still connects on port 80; forward external port 80 to ${WebPort}.${plain}"
fi
# Ensure chosen port is available
while true; do
if is_port_in_use "${WebPort}"; then
echo -e "${yellow}Port ${WebPort} is in use.${plain}"
local alt_port=""
read -rp "Enter another port for acme.sh standalone listener (leave empty to abort): " alt_port
alt_port="${alt_port// /}"
if [[ -z "${alt_port}" ]]; then
echo -e "${red}Port ${WebPort} is busy; cannot proceed.${plain}"
return 1
fi
if ! [[ "${alt_port}" =~ ^[0-9]+$ ]] || ((alt_port < 1 || alt_port > 65535)); then
echo -e "${red}Invalid port provided.${plain}"
return 1
fi
WebPort="${alt_port}"
continue
else
echo -e "${green}Port ${WebPort} is free and ready for standalone validation.${plain}"
break
fi
done
# Issue certificate with shortlived profile
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
~/.acme.sh/acme.sh --issue \
${domain_args} \
--standalone \
--server letsencrypt \
--certificate-profile shortlived \
--days 6 \
--httpport ${WebPort} \
--force
if [ $? -ne 0 ]; then
echo -e "${red}Failed to issue IP certificate${plain}"
echo -e "${yellow}Please ensure port ${WebPort} is reachable (or forwarded from external port 80)${plain}"
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
rm -rf ~/.acme.sh/${ipv4} 2>/dev/null
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2>/dev/null
rm -rf ${certDir} 2>/dev/null
return 1
fi
echo -e "${green}Certificate issued successfully, installing...${plain}"
# Install certificate
# Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
# but the cert files are still installed. We check for files instead of exit code.
~/.acme.sh/acme.sh --installcert -d ${ipv4} \
--key-file "${certDir}/privkey.pem" \
--fullchain-file "${certDir}/fullchain.pem" \
--reloadcmd "${reloadCmd}" 2>&1 || true
# Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
echo -e "${red}Certificate files not found after installation${plain}"
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
rm -rf ~/.acme.sh/${ipv4} 2>/dev/null
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2>/dev/null
rm -rf ${certDir} 2>/dev/null
return 1
fi
echo -e "${green}Certificate files installed successfully${plain}"
# Enable auto-upgrade for acme.sh (ensures cron job runs)
~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
# Secure permissions: private key readable only by owner
chmod 600 ${certDir}/privkey.pem 2>/dev/null
chmod 644 ${certDir}/fullchain.pem 2>/dev/null
# Configure panel to use the certificate
echo -e "${green}Setting certificate paths for the panel...${plain}"
${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
if [ $? -ne 0 ]; then
echo -e "${yellow}Warning: Could not set certificate paths automatically${plain}"
echo -e "${yellow}Certificate files are at:${plain}"
echo -e " Cert: ${certDir}/fullchain.pem"
echo -e " Key: ${certDir}/privkey.pem"
else
echo -e "${green}Certificate paths configured successfully${plain}"
fi
echo -e "${green}IP certificate installed and configured successfully!${plain}"
echo -e "${green}Certificate valid for ~6 days, auto-renews via acme.sh cron job.${plain}"
echo -e "${yellow}acme.sh will automatically renew and reload x-ui before expiry.${plain}"
return 0
}
# Comprehensive manual SSL certificate issuance via acme.sh
ssl_cert_issue() {
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
local existing_port=$(${xui_folder}/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
# check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
echo "acme.sh could not be found. Installing now..."
cd ~ || return 1
curl -s https://get.acme.sh | sh
if [ $? -ne 0 ]; then
echo -e "${red}Failed to install acme.sh${plain}"
return 1
else
echo -e "${green}acme.sh installed successfully${plain}"
fi
fi
# get the domain here, and we need to verify it
local domain=""
while true; do
read -rp "Please enter your domain name: " domain
domain="${domain// /}" # Trim whitespace
if [[ -z "$domain" ]]; then
echo -e "${red}Domain name cannot be empty. Please try again.${plain}"
continue
fi
if ! is_domain "$domain"; then
echo -e "${red}Invalid domain format: ${domain}. Please enter a valid domain name.${plain}"
continue
fi
break
done
echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
# check if there already exists a certificate
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
if [ "${currentCert}" == "${domain}" ]; then
local certInfo=$(~/.acme.sh/acme.sh --list)
echo -e "${red}System already has certificates for this domain. Cannot issue again.${plain}"
echo -e "${yellow}Current certificate details:${plain}"
echo "$certInfo"
return 1
else
echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
fi
# create a directory for the certificate
certPath="/root/cert/${domain}"
if [ ! -d "$certPath" ]; then
mkdir -p "$certPath"
else
rm -rf "$certPath"
mkdir -p "$certPath"
fi
# get the port number for the standalone server
local WebPort=80
read -rp "Please choose which port to use (default is 80): " WebPort
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
echo -e "${yellow}Your input ${WebPort} is invalid, will use default port 80.${plain}"
WebPort=80
fi
echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}"
# Stop panel temporarily
echo -e "${yellow}Stopping panel temporarily...${plain}"
systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
# issue the certificate
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
if [ $? -ne 0 ]; then
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
rm -rf ~/.acme.sh/${domain}
systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
return 1
else
echo -e "${green}Issuing certificate succeeded, installing certificates...${plain}"
fi
# Setup reload command
reloadCmd="systemctl restart x-ui || rc-service x-ui restart"
echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}"
echo -e "${green}This command will run on every certificate issue and renew.${plain}"
read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; systemctl restart x-ui"
echo -e "${green}\t2.${plain} Input your own command"
echo -e "${green}\t0.${plain} Keep default reloadcmd"
read -rp "Choose an option: " choice
case "$choice" in
1)
echo -e "${green}Reloadcmd is: systemctl reload nginx ; systemctl restart x-ui${plain}"
reloadCmd="systemctl reload nginx ; systemctl restart x-ui"
;;
2)
echo -e "${yellow}It's recommended to put x-ui restart at the end${plain}"
read -rp "Please enter your custom reloadcmd: " reloadCmd
echo -e "${green}Reloadcmd is: ${reloadCmd}${plain}"
;;
*)
echo -e "${green}Keeping default reloadcmd${plain}"
;;
esac
fi
# install the certificate
~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \
--fullchain-file /root/cert/${domain}/fullchain.pem --reloadcmd "${reloadCmd}"
if [ $? -ne 0 ]; then
echo -e "${red}Installing certificate failed, exiting.${plain}"
rm -rf ~/.acme.sh/${domain}
systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
return 1
else
echo -e "${green}Installing certificate succeeded, enabling auto renew...${plain}"
fi
# enable auto-renew
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
ls -lah /root/cert/${domain}/
# Secure permissions: private key readable only by owner
chmod 600 $certPath/privkey.pem 2>/dev/null
chmod 644 $certPath/fullchain.pem 2>/dev/null
else
echo -e "${green}Auto renew succeeded, certificate details:${plain}"
ls -lah /root/cert/${domain}/
# Secure permissions: private key readable only by owner
chmod 600 $certPath/privkey.pem 2>/dev/null
chmod 644 $certPath/fullchain.pem 2>/dev/null
fi
# start panel
systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
# Prompt user to set panel paths after successful certificate installation
read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
local webCertFile="/root/cert/${domain}/fullchain.pem"
local webKeyFile="/root/cert/${domain}/privkey.pem"
if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
echo -e "${green}Certificate paths set for the panel${plain}"
echo -e "${green}Certificate File: $webCertFile${plain}"
echo -e "${green}Private Key File: $webKeyFile${plain}"
echo ""
echo -e "${green}Access URL: https://${domain}:${existing_port}/${existing_webBasePath}${plain}"
echo -e "${yellow}Panel will restart to apply SSL certificate...${plain}"
systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null
else
echo -e "${red}Error: Certificate or private key file not found for domain: $domain.${plain}"
fi
else
echo -e "${yellow}Skipping panel path setting.${plain}"
fi
return 0
}
# Reusable interactive SSL setup (domain or IP)
# Sets global `SSL_HOST` to the chosen domain/IP for Access URL usage
prompt_and_setup_ssl() {
local panel_port="$1"
local web_base_path="$2" # expected without leading slash
local server_ip="$3"
local ssl_choice=""
echo -e "${yellow}Choose SSL certificate setup method:${plain}"
echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)"
echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)"
echo -e "${green}3.${plain} Custom SSL Certificate (Path to existing files)"
echo -e "${blue}Note:${plain} Options 1 & 2 require port 80 open. Option 3 requires manual paths."
read -rp "Choose an option (default 2 for IP): " ssl_choice
ssl_choice="${ssl_choice// /}" # Trim whitespace
# Default to 2 (IP cert) if input is empty or invalid (not 1 or 3)
if [[ "$ssl_choice" != "1" && "$ssl_choice" != "3" ]]; then
ssl_choice="2"
fi
case "$ssl_choice" in
1)
# User chose Let's Encrypt domain option
echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
ssl_cert_issue
# Extract the domain that was used from the certificate
local cert_domain=$(~/.acme.sh/acme.sh --list 2>/dev/null | tail -1 | awk '{print $1}')
if [[ -n "${cert_domain}" ]]; then
SSL_HOST="${cert_domain}"
echo -e "${green}✓ SSL certificate configured successfully with domain: ${cert_domain}${plain}"
else
echo -e "${yellow}SSL setup may have completed, but domain extraction failed${plain}"
SSL_HOST="${server_ip}"
fi
;;
2)
# User chose Let's Encrypt IP certificate option
echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
# Ask for optional IPv6
local ipv6_addr=""
read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
ipv6_addr="${ipv6_addr// /}" # Trim whitespace
# Stop panel if running (port 80 needed)
if [[ $release == "alpine" ]]; then
rc-service x-ui stop >/dev/null 2>&1
else
systemctl stop x-ui >/dev/null 2>&1
fi
setup_ip_certificate "${server_ip}" "${ipv6_addr}"
if [ $? -eq 0 ]; then
SSL_HOST="${server_ip}"
echo -e "${green}✓ Let's Encrypt IP certificate configured successfully${plain}"
else
echo -e "${red}✗ IP certificate setup failed. Please check port 80 is open.${plain}"
SSL_HOST="${server_ip}"
fi
;;
3)
# User chose Custom Paths (User Provided) option
echo -e "${green}Using custom existing certificate...${plain}"
local custom_cert=""
local custom_key=""
local custom_domain=""
# 3.1 Request Domain to compose Panel URL later
read -rp "Please enter domain name certificate issued for: " custom_domain
custom_domain="${custom_domain// /}" # Убираем пробелы
# 3.2 Loop for Certificate Path
while true; do
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
# Strip quotes if present
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
break
elif [[ ! -f "$custom_cert" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_cert" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.3 Loop for Private Key Path
while true; do
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
# Strip quotes if present
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
break
elif [[ ! -f "$custom_key" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_key" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.4 Apply Settings via x-ui binary
${xui_folder}/x-ui cert -webCert "$custom_cert" -webCertKey "$custom_key" >/dev/null 2>&1
# Set SSL_HOST for composing Panel URL
if [[ -n "$custom_domain" ]]; then
SSL_HOST="$custom_domain"
else
SSL_HOST="${server_ip}"
fi
echo -e "${green}✓ Custom certificate paths applied.${plain}"
echo -e "${yellow}Note: You are responsible for renewing these files externally.${plain}"
systemctl restart x-ui >/dev/null 2>&1 || rc-service x-ui restart >/dev/null 2>&1
;;
*)
echo -e "${red}Invalid option. Skipping SSL setup.${plain}"
SSL_HOST="${server_ip}"
;;
esac
}
config_after_install() {
local existing_hasDefaultCredential=$(${xui_folder}/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}')
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}' | sed 's#^/##')
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
# Properly detect empty cert by checking if cert: line exists and has content after it
local existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
local URL_lists=(
"https://api4.ipify.org"
"https://ipv4.icanhazip.com"
"https://v4.api.ipinfo.io/ip"
"https://ipv4.myexternalip.com/raw"
"https://4.ident.me"
"https://check-host.net/ip"
)
local server_ip=""
for ip_address in "${URL_lists[@]}"; do
local response=$(curl -s -w "\n%{http_code}" --max-time 3 "${ip_address}" 2>/dev/null)
local http_code=$(echo "$response" | tail -n1)
local ip_result=$(echo "$response" | head -n-1 | tr -d '[:space:]')
if [[ "${http_code}" == "200" && -n "${ip_result}" ]]; then
server_ip="${ip_result}"
break
fi
done
if [[ ${#existing_webBasePath} -lt 4 ]]; then
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
local config_webBasePath=$(gen_random_string 18)
local config_username=$(gen_random_string 10)
local config_password=$(gen_random_string 10)
read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
read -rp "Please set up the panel port: " config_port
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
else
local config_port=$(shuf -i 1024-62000 -n 1)
echo -e "${yellow}Generated random port: ${config_port}${plain}"
fi
${xui_folder}/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green} SSL Certificate Setup (MANDATORY) ${plain}"
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${yellow}For security, SSL certificate is required for all panels.${plain}"
echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
echo ""
prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
# Display final credentials and access information
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green} Panel Installation Complete! ${plain}"
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green}Username: ${config_username}${plain}"
echo -e "${green}Password: ${config_password}${plain}"
echo -e "${green}Port: ${config_port}${plain}"
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
echo -e "${green}Access URL: https://${SSL_HOST}:${config_port}/${config_webBasePath}${plain}"
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${yellow}⚠ IMPORTANT: Save these credentials securely!${plain}"
echo -e "${yellow}⚠ SSL Certificate: Enabled and configured${plain}"
else
local config_webBasePath=$(gen_random_string 18)
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}"
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
# If the panel is already installed but no certificate is configured, prompt for SSL now
if [[ -z "${existing_cert}" ]]; then
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green} SSL Certificate Setup (RECOMMENDED) ${plain}"
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
echo ""
prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
echo -e "${green}Access URL: https://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
else
# If a cert already exists, just show the access URL
echo -e "${green}Access URL: https://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
fi
fi
else
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
local config_username=$(gen_random_string 10)
local config_password=$(gen_random_string 10)
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
${xui_folder}/x-ui setting -username "${config_username}" -password "${config_password}"
echo -e "Generated new random login credentials:"
echo -e "###############################################"
echo -e "${green}Username: ${config_username}${plain}"
echo -e "${green}Password: ${config_password}${plain}"
echo -e "###############################################"
else
echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
fi
# Existing install: if no cert configured, prompt user for SSL setup
# Properly detect empty cert by checking if cert: line exists and has content after it
existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
if [[ -z "$existing_cert" ]]; then
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green} SSL Certificate Setup (RECOMMENDED) ${plain}"
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
echo ""
prompt_and_setup_ssl "${existing_port}" "${existing_webBasePath}" "${server_ip}"
echo -e "${green}Access URL: https://${SSL_HOST}:${existing_port}/${existing_webBasePath}${plain}"
else
echo -e "${green}SSL certificate already configured. No action needed.${plain}"
fi
fi
${xui_folder}/x-ui migrate
}
install_x-ui() {
cd ${xui_folder%/x-ui}/
# Download resources
if [ $# == 0 ]; then
tag_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [[ ! -n "$tag_version" ]]; then
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
tag_version=$(curl -4 -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [[ ! -n "$tag_version" ]]; then
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
exit 1
fi
fi
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
if [[ $? -ne 0 ]]; then
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
exit 1
fi
else
tag_version=$1
tag_version_numeric=${tag_version#v}
min_version="2.3.5"
if [[ "$(printf '%s\n' "$min_version" "$tag_version_numeric" | sort -V | head -n1)" != "$min_version" ]]; then
echo -e "${red}Please use a newer version (at least v2.3.5). Exiting installation.${plain}"
exit 1
fi
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
echo -e "Beginning to install x-ui $1"
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz ${url}
if [[ $? -ne 0 ]]; then
echo -e "${red}Download x-ui $1 failed, please check if the version exists ${plain}"
exit 1
fi
fi
curl -4fLRo /usr/bin/x-ui-temp https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
if [[ $? -ne 0 ]]; then
echo -e "${red}Failed to download x-ui.sh${plain}"
exit 1
fi
# Stop x-ui service and remove old resources
if [[ -e ${xui_folder}/ ]]; then
if [[ $release == "alpine" ]]; then
rc-service x-ui stop
else
systemctl stop x-ui
fi
rm ${xui_folder}/ -rf
fi
# Extract resources and set permissions
tar zxvf x-ui-linux-$(arch).tar.gz
rm x-ui-linux-$(arch).tar.gz -f
cd x-ui
chmod +x x-ui
chmod +x x-ui.sh
# Check the system's architecture and rename the file accordingly
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
mv bin/xray-linux-$(arch) bin/xray-linux-arm
chmod +x bin/xray-linux-arm
fi
chmod +x x-ui bin/xray-linux-$(arch)
# Update x-ui cli and se set permission
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
chmod +x /usr/bin/x-ui
mkdir -p /var/log/x-ui
config_after_install
# Etckeeper compatibility
if [ -d "/etc/.git" ]; then
if [ -f "/etc/.gitignore" ]; then
if ! grep -q "x-ui/x-ui.db" "/etc/.gitignore"; then
echo "" >> "/etc/.gitignore"
echo "x-ui/x-ui.db" >> "/etc/.gitignore"
echo -e "${green}Added x-ui.db to /etc/.gitignore for etckeeper${plain}"
fi
else
echo "x-ui/x-ui.db" > "/etc/.gitignore"
echo -e "${green}Created /etc/.gitignore and added x-ui.db for etckeeper${plain}"
fi
fi
if [[ $release == "alpine" ]]; then
curl -4fLRo /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc
if [[ $? -ne 0 ]]; then
echo -e "${red}Failed to download x-ui.rc${plain}"
exit 1
fi
chmod +x /etc/init.d/x-ui
rc-update add x-ui
rc-service x-ui start
else
# Install systemd service file
service_installed=false
if [ -f "x-ui.service" ]; then
echo -e "${green}Found x-ui.service in extracted files, installing...${plain}"
cp -f x-ui.service ${xui_service}/ >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
service_installed=true
fi
fi
if [ "$service_installed" = false ]; then
case "${release}" in
ubuntu | debian | armbian)
if [ -f "x-ui.service.debian" ]; then
echo -e "${green}Found x-ui.service.debian in extracted files, installing...${plain}"
cp -f x-ui.service.debian ${xui_service}/x-ui.service >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
service_installed=true
fi
fi
;;
arch | manjaro | parch)
if [ -f "x-ui.service.arch" ]; then
echo -e "${green}Found x-ui.service.arch in extracted files, installing...${plain}"
cp -f x-ui.service.arch ${xui_service}/x-ui.service >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
service_installed=true
fi
fi
;;
*)
if [ -f "x-ui.service.rhel" ]; then
echo -e "${green}Found x-ui.service.rhel in extracted files, installing...${plain}"
cp -f x-ui.service.rhel ${xui_service}/x-ui.service >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
service_installed=true
fi
fi
;;
esac
fi
# If service file not found in tar.gz, download from GitHub
if [ "$service_installed" = false ]; then
echo -e "${yellow}Service files not found in tar.gz, downloading from GitHub...${plain}"
case "${release}" in
ubuntu | debian | armbian)
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian >/dev/null 2>&1
;;
arch | manjaro | parch)
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch >/dev/null 2>&1
;;
*)
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel >/dev/null 2>&1
;;
esac
if [[ $? -ne 0 ]]; then
echo -e "${red}Failed to install x-ui.service from GitHub${plain}"
exit 1
fi
service_installed=true
fi
if [ "$service_installed" = true ]; then
echo -e "${green}Setting up systemd unit...${plain}"
chown root:root ${xui_service}/x-ui.service >/dev/null 2>&1
chmod 644 ${xui_service}/x-ui.service >/dev/null 2>&1
systemctl daemon-reload
systemctl enable x-ui
systemctl start x-ui
else
echo -e "${red}Failed to install x-ui.service file${plain}"
exit 1
fi
fi
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
echo -e ""
echo -e "┌───────────────────────────────────────────────────────┐
│ ${blue}x-ui control menu usages (subcommands):${plain} │
│ │
│ ${blue}x-ui${plain} - Admin Management Script │
│ ${blue}x-ui start${plain} - Start │
│ ${blue}x-ui stop${plain} - Stop │
│ ${blue}x-ui restart${plain} - Restart │
│ ${blue}x-ui status${plain} - Current Status │
│ ${blue}x-ui settings${plain} - Current Settings │
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
│ ${blue}x-ui log${plain} - Check logs │
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
│ ${blue}x-ui update${plain} - Update │
│ ${blue}x-ui legacy${plain} - Legacy version │
│ ${blue}x-ui install${plain} - Install │
│ ${blue}x-ui uninstall${plain} - Uninstall │
└───────────────────────────────────────────────────────┘"
}
echo -e "${green}Running...${plain}"
install_base
install_x-ui $1
================================================
FILE: logger/logger.go
================================================
// Package logger provides logging functionality for the 3x-ui panel with
// dual-backend logging (console/syslog and file) and buffered log storage for web UI.
package logger
import (
"fmt"
"os"
"path/filepath"
"runtime"
"time"
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/op/go-logging"
)
const (
maxLogBufferSize = 10240 // Maximum log entries kept in memory
logFileName = "3xui.log" // Log file name
timeFormat = "2006/01/02 15:04:05" // Log timestamp format
)
var (
logger *logging.Logger
logFile *os.File
// logBuffer maintains recent log entries in memory for web UI retrieval
logBuffer []struct {
time string
level logging.Level
log string
}
)
// InitLogger initializes dual logging backends: console/syslog and file.
// Console logging uses the specified level, file logging always uses DEBUG level.
func InitLogger(level logging.Level) {
newLogger := logging.MustGetLogger("x-ui")
backends := make([]logging.Backend, 0, 2)
// Console/syslog backend with configurable level
if consoleBackend := initDefaultBackend(); consoleBackend != nil {
leveledBackend := logging.AddModuleLevel(consoleBackend)
leveledBackend.SetLevel(level, "x-ui")
backends = append(backends, leveledBackend)
}
// File backend with DEBUG level for comprehensive logging
if fileBackend := initFileBackend(); fileBackend != nil {
leveledBackend := logging.AddModuleLevel(fileBackend)
leveledBackend.SetLevel(logging.DEBUG, "x-ui")
backends = append(backends, leveledBackend)
}
multiBackend := logging.MultiLogger(backends...)
newLogger.SetBackend(multiBackend)
logger = newLogger
}
// initDefaultBackend creates the console/syslog logging backend.
// Windows: Uses stderr directly (no syslog support)
// Unix-like: Attempts syslog, falls back to stderr
func initDefaultBackend() logging.Backend {
var backend logging.Backend
includeTime := false
if runtime.GOOS == "windows" {
// Windows: Use stderr directly (no syslog support)
backend = logging.NewLogBackend(os.Stderr, "", 0)
includeTime = true
} else {
// Unix-like: Try syslog, fallback to stderr
if syslogBackend, err := logging.NewSyslogBackend(""); err != nil {
fmt.Fprintf(os.Stderr, "syslog backend disabled: %v\n", err)
backend = logging.NewLogBackend(os.Stderr, "", 0)
includeTime = os.Getppid() > 0
} else {
backend = syslogBackend
}
}
return logging.NewBackendFormatter(backend, newFormatter(includeTime))
}
// initFileBackend creates the file logging backend.
// Creates log directory and truncates log file on startup for fresh logs.
func initFileBackend() logging.Backend {
logDir := config.GetLogFolder()
if err := os.MkdirAll(logDir, 0o750); err != nil {
fmt.Fprintf(os.Stderr, "failed to create log folder %s: %v\n", logDir, err)
return nil
}
logPath := filepath.Join(logDir, logFileName)
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o660)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to open log file %s: %v\n", logPath, err)
return nil
}
// Close previous log file if exists
if logFile != nil {
_ = logFile.Close()
}
logFile = file
backend := logging.NewLogBackend(file, "", 0)
return logging.NewBackendFormatter(backend, newFormatter(true))
}
// newFormatter creates a log formatter with optional timestamp.
func newFormatter(withTime bool) logging.Formatter {
format := `%{level} - %{message}`
if withTime {
format = `%{time:` + timeFormat + `} %{level} - %{message}`
}
return logging.MustStringFormatter(format)
}
// CloseLogger closes the log file and cleans up resources.
// Should be called during application shutdown.
func CloseLogger() {
if logFile != nil {
_ = logFile.Close()
logFile = nil
}
}
// Debug logs a debug message and adds it to the log buffer.
func Debug(args ...any) {
logger.Debug(args...)
addToBuffer("DEBUG", fmt.Sprint(args...))
}
// Debugf logs a formatted debug message and adds it to the log buffer.
func Debugf(format string, args ...any) {
logger.Debugf(format, args...)
addToBuffer("DEBUG", fmt.Sprintf(format, args...))
}
// Info logs an info message and adds it to the log buffer.
func Info(args ...any) {
logger.Info(args...)
addToBuffer("INFO", fmt.Sprint(args...))
}
// Infof logs a formatted info message and adds it to the log buffer.
func Infof(format string, args ...any) {
logger.Infof(format, args...)
addToBuffer("INFO", fmt.Sprintf(format, args...))
}
// Notice logs a notice message and adds it to the log buffer.
func Notice(args ...any) {
logger.Notice(args...)
addToBuffer("NOTICE", fmt.Sprint(args...))
}
// Noticef logs a formatted notice message and adds it to the log buffer.
func Noticef(format string, args ...any) {
logger.Noticef(format, args...)
addToBuffer("NOTICE", fmt.Sprintf(format, args...))
}
// Warning logs a warning message and adds it to the log buffer.
func Warning(args ...any) {
logger.Warning(args...)
addToBuffer("WARNING", fmt.Sprint(args...))
}
// Warningf logs a formatted warning message and adds it to the log buffer.
func Warningf(format string, args ...any) {
logger.Warningf(format, args...)
addToBuffer("WARNING", fmt.Sprintf(format, args...))
}
// Error logs an error message and adds it to the log buffer.
func Error(args ...any) {
logger.Error(args...)
addToBuffer("ERROR", fmt.Sprint(args...))
}
// Errorf logs a formatted error message and adds it to the log buffer.
func Errorf(format string, args ...any) {
logger.Errorf(format, args...)
addToBuffer("ERROR", fmt.Sprintf(format, args...))
}
// addToBuffer adds a log entry to the in-memory ring buffer for web UI retrieval.
func addToBuffer(level string, newLog string) {
t := time.Now()
if len(logBuffer) >= maxLogBufferSize {
logBuffer = logBuffer[1:]
}
logLevel, _ := logging.LogLevel(level)
logBuffer = append(logBuffer, struct {
time string
level logging.Level
log string
}{
time: t.Format(timeFormat),
level: logLevel,
log: newLog,
})
}
// GetLogs retrieves up to c log entries from the buffer that are at or below the specified level.
func GetLogs(c int, level string) []string {
var output []string
logLevel, _ := logging.LogLevel(level)
for i := len(logBuffer) - 1; i >= 0 && len(output) <= c; i-- {
if logBuffer[i].level <= logLevel {
output = append(output, fmt.Sprintf("%s %s - %s", logBuffer[i].time, logBuffer[i].level, logBuffer[i].log))
}
}
return output
}
================================================
FILE: main.go
================================================
// Package main is the entry point for the 3x-ui web panel application.
// It initializes the database, web server, and handles command-line operations for managing the panel.
package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
_ "unsafe"
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/mhsanaei/3x-ui/v2/database"
"github.com/mhsanaei/3x-ui/v2/logger"
"github.com/mhsanaei/3x-ui/v2/sub"
"github.com/mhsanaei/3x-ui/v2/util/crypto"
"github.com/mhsanaei/3x-ui/v2/util/sys"
"github.com/mhsanaei/3x-ui/v2/web"
"github.com/mhsanaei/3x-ui/v2/web/global"
"github.com/mhsanaei/3x-ui/v2/web/service"
"github.com/joho/godotenv"
"github.com/op/go-logging"
)
// runWebServer initializes and starts the web server for the 3x-ui panel.
func runWebServer() {
log.Printf("Starting %v %v", config.GetName(), config.GetVersion())
switch config.GetLogLevel() {
case config.Debug:
logger.InitLogger(logging.DEBUG)
case config.Info:
logger.InitLogger(logging.INFO)
case config.Notice:
logger.InitLogger(logging.NOTICE)
case config.Warning:
logger.InitLogger(logging.WARNING)
case config.Error:
logger.InitLogger(logging.ERROR)
default:
log.Fatalf("Unknown log level: %v", config.GetLogLevel())
}
godotenv.Load()
err := database.InitDB(config.GetDBPath())
if err != nil {
log.Fatalf("Error initializing database: %v", err)
}
var server *web.Server
server = web.NewServer()
global.SetWebServer(server)
err = server.Start()
if err != nil {
log.Fatalf("Error starting web server: %v", err)
return
}
var subServer *sub.Server
subServer = sub.NewServer()
global.SetSubServer(subServer)
err = subServer.Start()
if err != nil {
log.Fatalf("Error starting sub server: %v", err)
return
}
sigCh := make(chan os.Signal, 1)
// Trap shutdown signals
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, sys.SIGUSR1)
for {
sig := <-sigCh
switch sig {
case syscall.SIGHUP:
logger.Info("Received SIGHUP signal. Restarting servers...")
// --- FIX FOR TELEGRAM BOT CONFLICT (409): Stop bot before restart ---
service.StopBot()
// --
err := server.Stop()
if err != nil {
logger.Debug("Error stopping web server:", err)
}
err = subServer.Stop()
if err != nil {
logger.Debug("Error stopping sub server:", err)
}
server = web.NewServer()
global.SetWebServer(server)
err = server.Start()
if err != nil {
log.Fatalf("Error restarting web server: %v", err)
return
}
log.Println("Web server restarted successfully.")
subServer = sub.NewServer()
global.SetSubServer(subServer)
err = subServer.Start()
if err != nil {
log.Fatalf("Error restarting sub server: %v", err)
return
}
log.Println("Sub server restarted successfully.")
case sys.SIGUSR1:
logger.Info("Received USR1 signal, restarting xray-core...")
err := server.RestartXray()
if err != nil {
logger.Error("Failed to restart xray-core:", err)
}
default:
// --- FIX FOR TELEGRAM BOT CONFLICT (409) on full shutdown ---
service.StopBot()
// ------------------------------------------------------------
server.Stop()
subServer.Stop()
log.Println("Shutting down servers.")
return
}
}
}
// resetSetting resets all panel settings to their default values.
func resetSetting() {
err := database.InitDB(config.GetDBPath())
if err != nil {
fmt.Println("Failed to initialize database:", err)
return
}
settingService := service.SettingService{}
err = settingService.ResetSettings()
if err != nil {
fmt.Println("Failed to reset settings:", err)
} else {
fmt.Println("Settings successfully reset.")
}
}
// showSetting displays the current panel settings if show is true.
func showSetting(show bool) {
if show {
settingService := service.SettingService{}
port, err := settingService.GetPort()
if err != nil {
fmt.Println("get current port failed, error info:", err)
}
webBasePath, err := settingService.GetBasePath()
if err != nil {
fmt.Println("get webBasePath failed, error info:", err)
}
certFile, err := settingService.GetCertFile()
if err != nil {
fmt.Println("get cert file failed, error info:", err)
}
keyFile, err := settingService.GetKeyFile()
if err != nil {
fmt.Println("get key file failed, error info:", err)
}
userService := service.UserService{}
userModel, err := userService.GetFirstUser()
if err != nil {
fmt.Println("get current user info failed, error info:", err)
}
if userModel.Username == "" || userModel.Password == "" {
fmt.Println("current username or password is empty")
}
fmt.Println("current panel settings as follows:")
if certFile == "" || keyFile == "" {
fmt.Println("Warning: Panel is not secure with SSL")
} else {
fmt.Println("Panel is secure with SSL")
}
hasDefaultCredential := func() bool {
return userModel.Username == "admin" && crypto.CheckPasswordHash(userModel.Password, "admin")
}()
fmt.Println("hasDefaultCredential:", hasDefaultCredential)
fmt.Println("port:", port)
fmt.Println("webBasePath:", webBasePath)
}
}
// updateTgbotEnableSts enables or disables the Telegram bot notifications based on the status parameter.
func updateTgbotEnableSts(status bool) {
settingService := service.SettingService{}
currentTgSts, err := settingService.GetTgbotEnabled()
if err != nil {
fmt.Println(err)
return
}
logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
if currentTgSts != status {
err := settingService.SetTgbotEnabled(status)
if err != nil {
fmt.Println(err)
return
} else {
logger.Infof("SetTgbotEnabled[%v] success", status)
}
}
}
// updateTgbotSetting updates Telegram bot settings including token, chat ID, and runtime schedule.
func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
err := database.InitDB(config.GetDBPath())
if err != nil {
fmt.Println("Error initializing database:", err)
return
}
settingService := service.SettingService{}
if tgBotToken != "" {
err := settingService.SetTgBotToken(tgBotToken)
if err != nil {
fmt.Printf("Error setting Telegram bot token: %v\n", err)
return
}
logger.Info("Successfully updated Telegram bot token.")
}
if tgBotRuntime != "" {
err := settingService.SetTgbotRuntime(tgBotRuntime)
if err != nil {
fmt.Printf("Error setting Telegram bot runtime: %v\n", err)
return
}
logger.Infof("Successfully updated Telegram bot runtime to [%s].", tgBotRuntime)
}
if tgBotChatid != "" {
err := settingService.SetTgBotChatId(tgBotChatid)
if err != nil {
fmt.Printf("Error setting Telegram bot chat ID: %v\n", err)
return
}
logger.Info("Successfully updated Telegram bot chat ID.")
}
}
// updateSetting updates various panel settings including port, credentials, base path, listen IP, and two-factor authentication.
func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool) {
err := database.InitDB(config.GetDBPath())
if err != nil {
fmt.Println("Database initialization failed:", err)
return
}
settingService := service.SettingService{}
userService := service.UserService{}
if port > 0 {
err := settingService.SetPort(port)
if err != nil {
fmt.Println("Failed to set port:", err)
} else {
fmt.Printf("Port set successfully: %v\n", port)
}
}
if username != "" || password != "" {
err := userService.UpdateFirstUser(username, password)
if err != nil {
fmt.Println("Failed to update username and password:", err)
} else {
fmt.Println("Username and password updated successfully")
}
}
if webBasePath != "" {
err := settingService.SetBasePath(webBasePath)
if err != nil {
fmt.Println("Failed to set base URI path:", err)
} else {
fmt.Println("Base URI path set successfully")
}
}
if resetTwoFactor {
err := settingService.SetTwoFactorEnable(false)
if err != nil {
fmt.Println("Failed to reset two-factor authentication:", err)
} else {
settingService.SetTwoFactorToken("")
fmt.Println("Two-factor authentication reset successfully")
}
}
if listenIP != "" {
err := settingService.SetListen(listenIP)
if err != nil {
fmt.Println("Failed to set listen IP:", err)
} else {
fmt.Printf("listen %v set successfully", listenIP)
}
}
}
// updateCert updates the SSL certificate files for the panel.
func updateCert(publicKey string, privateKey string) {
err := database.InitDB(config.GetDBPath())
if err != nil {
fmt.Println(err)
return
}
if (privateKey != "" && publicKey != "") || (privateKey == "" && publicKey == "") {
settingService := service.SettingService{}
err = settingService.SetCertFile(publicKey)
if err != nil {
fmt.Println("set certificate public key failed:", err)
} else {
fmt.Println("set certificate public key success")
}
err = settingService.SetKeyFile(privateKey)
if err != nil {
fmt.Println("set certificate private key failed:", err)
} else {
fmt.Println("set certificate private key success")
}
err = settingService.SetSubCertFile(publicKey)
if err != nil {
fmt.Println("set certificate for subscription public key failed:", err)
} else {
fmt.Println("set certificate for subscription public key success")
}
err = settingService.SetSubKeyFile(privateKey)
if err != nil {
fmt.Println("set certificate for subscription private key failed:", err)
} else {
fmt.Println("set certificate for subscription private key success")
}
} else {
fmt.Println("both public and private key should be entered.")
}
}
// GetCertificate displays the current SSL certificate settings if getCert is true.
func GetCertificate(getCert bool) {
if getCert {
settingService := service.SettingService{}
certFile, err := settingService.GetCertFile()
if err != nil {
fmt.Println("get cert file failed, error info:", err)
}
keyFile, err := settingService.GetKeyFile()
if err != nil {
fmt.Println("get key file failed, error info:", err)
}
fmt.Println("cert:", certFile)
fmt.Println("key:", keyFile)
}
}
// GetListenIP displays the current panel listen IP address if getListen is true.
func GetListenIP(getListen bool) {
if getListen {
settingService := service.SettingService{}
ListenIP, err := settingService.GetListen()
if err != nil {
log.Printf("Failed to retrieve listen IP: %v", err)
return
}
fmt.Println("listenIP:", ListenIP)
}
}
// migrateDb performs database migration operations for the 3x-ui panel.
func migrateDb() {
inboundService := service.InboundService{}
err := database.InitDB(config.GetDBPath())
if err != nil {
log.Fatal(err)
}
fmt.Println("Start migrating database...")
inboundService.MigrateDB()
fmt.Println("Migration done!")
}
// main is the entry point of the 3x-ui application.
// It parses command-line arguments to run the web server, migrate database, or update settings.
func main() {
if len(os.Args) < 2 {
runWebServer()
return
}
var showVersion bool
flag.BoolVar(&showVersion, "v", false, "show version")
runCmd := flag.NewFlagSet("run", flag.ExitOnError)
settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
var port int
var username string
var password string
var webBasePath string
var listenIP string
var getListen bool
var webCertFile string
var webKeyFile string
var tgbottoken string
var tgbotchatid string
var enabletgbot bool
var tgbotRuntime string
var reset bool
var show bool
var getCert bool
var resetTwoFactor bool
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
settingCmd.BoolVar(&show, "show", false, "Display current settings")
settingCmd.IntVar(&port, "port", 0, "Set panel port number")
settingCmd.StringVar(&username, "username", "", "Set login username")
settingCmd.StringVar(&password, "password", "", "Set login password")
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
settingCmd.StringVar(&listenIP, "listenIP", "", "set panel listenIP IP")
settingCmd.BoolVar(&resetTwoFactor, "resetTwoFactor", false, "Reset two-factor authentication settings")
settingCmd.BoolVar(&getListen, "getListen", false, "Display current panel listenIP IP")
settingCmd.BoolVar(&getCert, "getCert", false, "Display current certificate settings")
settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "Set cron time for Telegram bot notifications")
settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "Set chat ID for Telegram bot notifications")
settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "Enable notifications via Telegram bot")
oldUsage := flag.Usage
flag.Usage = func() {
oldUsage()
fmt.Println()
fmt.Println("Commands:")
fmt.Println(" run run web panel")
fmt.Println(" migrate migrate form other/old x-ui")
fmt.Println(" setting set settings")
}
flag.Parse()
if showVersion {
fmt.Println(config.GetVersion())
return
}
switch os.Args[1] {
case "run":
err := runCmd.Parse(os.Args[2:])
if err != nil {
fmt.Println(err)
return
}
runWebServer()
case "migrate":
migrateDb()
case "setting":
err := settingCmd.Parse(os.Args[2:])
if err != nil {
fmt.Println(err)
return
}
if reset {
resetSetting()
} else {
updateSetting(port, username, password, webBasePath, listenIP, resetTwoFactor)
}
if show {
showSetting(show)
}
if getListen {
GetListenIP(getListen)
}
if getCert {
GetCertificate(getCert)
}
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
}
if enabletgbot {
updateTgbotEnableSts(enabletgbot)
}
case "cert":
err := settingCmd.Parse(os.Args[2:])
if err != nil {
fmt.Println(err)
return
}
if reset {
updateCert("", "")
} else {
updateCert(webCertFile, webKeyFile)
}
default:
fmt.Println("Invalid subcommands")
fmt.Println()
runCmd.Usage()
fmt.Println()
settingCmd.Usage()
}
}
================================================
FILE: sub/default.json
================================================
{
"remarks": "",
"dns": {
"tag": "dns_out",
"queryStrategy": "UseIP",
"servers": [
{
"address": "8.8.8.8",
"skipFallback": false
}
]
},
"inbounds": [
{
"port": 10808,
"protocol": "mixed",
"settings": {
"auth": "noauth",
"udp": true,
"userLevel": 8
},
"sniffing": {
"destOverride": [
"http",
"tls",
"quic",
"fakedns"
],
"enabled": true
},
"tag": "mixed"
},
{
"port": 10809,
"protocol": "http",
"settings": {
"userLevel": 8
},
"tag": "http"
}
],
"log": {
"loglevel": "warning"
},
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "AsIs",
"redirect": "",
"noises": []
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "http"
}
}
}
],
"policy": {
"levels": {
"8": {
"connIdle": 300,
"downlinkOnly": 1,
"handshake": 4,
"uplinkOnly": 1
}
},
"system": {
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
},
"stats": {}
}
================================================
FILE: sub/sub.go
================================================
// Package sub provides subscription server functionality for the 3x-ui panel,
// including HTTP/HTTPS servers for serving subscription links and JSON configurations.
package sub
import (
"context"
"crypto/tls"
"html/template"
"io"
"io/fs"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/mhsanaei/3x-ui/v2/logger"
"github.com/mhsanaei/3x-ui/v2/util/common"
webpkg "github.com/mhsanaei/3x-ui/v2/web"
"github.com/mhsanaei/3x-ui/v2/web/locale"
"github.com/mhsanaei/3x-ui/v2/web/middleware"
"github.com/mhsanaei/3x-ui/v2/web/network"
"github.com/mhsanaei/3x-ui/v2/web/service"
"github.com/gin-gonic/gin"
)
// setEmbeddedTemplates parses and sets embedded templates on the engine
func setEmbeddedTemplates(engine *gin.Engine) error {
t, err := template.New("").Funcs(engine.FuncMap).ParseFS(
webpkg.EmbeddedHTML(),
"html/common/page.html",
"html/component/aThemeSwitch.html",
"html/settings/panel/subscription/subpage.html",
)
if err != nil {
return err
}
engine.SetHTMLTemplate(t)
return nil
}
// Server represents the subscription server that serves subscription links and JSON configurations.
type Server struct {
httpServer *http.Server
listener net.Listener
sub *SUBController
settingService service.SettingService
ctx context.Context
cancel context.CancelFunc
}
// NewServer creates a new subscription server instance with a cancellable context.
func NewServer() *Server {
ctx, cancel := context.WithCancel(context.Background())
return &Server{
ctx: ctx,
cancel: cancel,
}
}
// initRouter configures the subscription server's Gin engine, middleware,
// templates and static assets and returns the ready-to-use engine.
func (s *Server) initRouter() (*gin.Engine, error) {
// Always run in release mode for the subscription server
gin.DefaultWriter = io.Discard
gin.DefaultErrorWriter = io.Discard
gin.SetMode(gin.ReleaseMode)
engine := gin.Default()
subDomain, err := s.settingService.GetSubDomain()
if err != nil {
return nil, err
}
if subDomain != "" {
engine.Use(middleware.DomainValidatorMiddleware(subDomain))
}
LinksPath, err := s.settingService.GetSubPath()
if err != nil {
return nil, err
}
JsonPath, err := s.settingService.GetSubJsonPath()
if err != nil {
return nil, err
}
// Determine if JSON subscription endpoint is enabled
subJsonEnable, err := s.settingService.GetSubJsonEnable()
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
if basePath != "/" && !strings.HasSuffix(basePath, "/") {
basePath += "/"
}
// logger.Debug("sub: Setting base_path to:", basePath)
engine.Use(func(c *gin.Context) {
c.Set("base_path", basePath)
})
Encrypt, err := s.settingService.GetSubEncrypt()
if err != nil {
return nil, err
}
ShowInfo, err := s.settingService.GetSubShowInfo()
if err != nil {
return nil, err
}
RemarkModel, err := s.settingService.GetRemarkModel()
if err != nil {
RemarkModel = "-ieo"
}
SubUpdates, err := s.settingService.GetSubUpdates()
if err != nil {
SubUpdates = "10"
}
SubJsonFragment, err := s.settingService.GetSubJsonFragment()
if err != nil {
SubJsonFragment = ""
}
SubJsonNoises, err := s.settingService.GetSubJsonNoises()
if err != nil {
SubJsonNoises = ""
}
SubJsonMux, err := s.settingService.GetSubJsonMux()
if err != nil {
SubJsonMux = ""
}
SubJsonRules, err := s.settingService.GetSubJsonRules()
if err != nil {
SubJsonRules = ""
}
SubTitle, err := s.settingService.GetSubTitle()
if err != nil {
SubTitle = ""
}
SubSupportUrl, err := s.settingService.GetSubSupportUrl()
if err != nil {
SubSupportUrl = ""
}
SubProfileUrl, err := s.settingService.GetSubProfileUrl()
if err != nil {
SubProfileUrl = ""
}
SubAnnounce, err := s.settingService.GetSubAnnounce()
if err != nil {
SubAnnounce = ""
}
SubEnableRouting, err := s.settingService.GetSubEnableRouting()
if err != nil {
return nil, err
}
SubRoutingRules, err := s.settingService.GetSubRoutingRules()
if err != nil {
SubRoutingRules = ""
}
// set per-request localizer from headers/cookies
engine.Use(locale.LocalizerMiddleware())
// register i18n function similar to web server
i18nWebFunc := func(key string, params ...string) string {
return locale.I18n(locale.Web, key, params...)
}
engine.SetFuncMap(map[string]any{"i18n": i18nWebFunc})
// Templates: prefer embedded; fallback to disk if necessary
if err := setEmbeddedTemplates(engine); err != nil {
logger.Warning("sub: failed to parse embedded templates:", err)
if files, derr := s.getHtmlFiles(); derr == nil {
engine.LoadHTMLFiles(files...)
} else {
logger.Error("sub: no templates available (embedded parse and disk load failed)", err, derr)
}
}
// Assets: use disk if present, fallback to embedded
// Serve under both root (/assets) and under the subscription path prefix (LinksPath + "assets")
// so reverse proxies with a URI prefix can load assets correctly.
// Determine LinksPath earlier to comp
gitextract_ww701i7l/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yaml
│ │ ├── feature_request.yaml
│ │ └── question.yaml
│ ├── copilot-instructions.md
│ ├── dependabot.yml
│ ├── pull_request_template.yml
│ └── workflows/
│ ├── cleanup_caches.yml
│ ├── docker.yml
│ └── release.yml
├── .gitignore
├── CONTRIBUTING.md
├── DockerEntrypoint.sh
├── DockerInit.sh
├── Dockerfile
├── LICENSE
├── README.ar_EG.md
├── README.es_ES.md
├── README.fa_IR.md
├── README.md
├── README.ru_RU.md
├── README.zh_CN.md
├── config/
│ ├── config.go
│ ├── name
│ └── version
├── database/
│ ├── db.go
│ └── model/
│ └── model.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── install.sh
├── logger/
│ └── logger.go
├── main.go
├── sub/
│ ├── default.json
│ ├── sub.go
│ ├── subController.go
│ ├── subJsonService.go
│ └── subService.go
├── update.sh
├── util/
│ ├── common/
│ │ ├── err.go
│ │ ├── format.go
│ │ └── multi_error.go
│ ├── crypto/
│ │ └── crypto.go
│ ├── json_util/
│ │ └── json.go
│ ├── ldap/
│ │ └── ldap.go
│ ├── random/
│ │ └── random.go
│ ├── reflect_util/
│ │ └── reflect.go
│ └── sys/
│ ├── psutil.go
│ ├── sys_darwin.go
│ ├── sys_linux.go
│ └── sys_windows.go
├── web/
│ ├── assets/
│ │ ├── codemirror/
│ │ │ ├── fold/
│ │ │ │ ├── brace-fold.js
│ │ │ │ ├── foldcode.js
│ │ │ │ ├── foldgutter.css
│ │ │ │ └── foldgutter.js
│ │ │ ├── hint/
│ │ │ │ └── javascript-hint.js
│ │ │ ├── javascript.js
│ │ │ ├── jshint.js
│ │ │ ├── jsonlint.js
│ │ │ └── lint/
│ │ │ ├── javascript-lint.js
│ │ │ ├── lint.css
│ │ │ └── lint.js
│ │ └── js/
│ │ ├── axios-init.js
│ │ ├── model/
│ │ │ ├── dbinbound.js
│ │ │ ├── inbound.js
│ │ │ ├── outbound.js
│ │ │ ├── reality_targets.js
│ │ │ └── setting.js
│ │ ├── subscription.js
│ │ ├── util/
│ │ │ └── index.js
│ │ └── websocket.js
│ ├── controller/
│ │ ├── api.go
│ │ ├── base.go
│ │ ├── inbound.go
│ │ ├── index.go
│ │ ├── server.go
│ │ ├── setting.go
│ │ ├── util.go
│ │ ├── websocket.go
│ │ ├── xray_setting.go
│ │ └── xui.go
│ ├── entity/
│ │ └── entity.go
│ ├── global/
│ │ ├── global.go
│ │ └── hashStorage.go
│ ├── html/
│ │ ├── common/
│ │ │ └── page.html
│ │ ├── component/
│ │ │ ├── aClientTable.html
│ │ │ ├── aCustomStatistic.html
│ │ │ ├── aPersianDatepicker.html
│ │ │ ├── aSettingListItem.html
│ │ │ ├── aSidebar.html
│ │ │ ├── aTableSortable.html
│ │ │ └── aThemeSwitch.html
│ │ ├── form/
│ │ │ ├── client.html
│ │ │ ├── inbound.html
│ │ │ ├── outbound.html
│ │ │ ├── protocol/
│ │ │ │ ├── dokodemo.html
│ │ │ │ ├── http.html
│ │ │ │ ├── shadowsocks.html
│ │ │ │ ├── socks.html
│ │ │ │ ├── trojan.html
│ │ │ │ ├── tun.html
│ │ │ │ ├── vless.html
│ │ │ │ ├── vmess.html
│ │ │ │ └── wireguard.html
│ │ │ ├── reality_settings.html
│ │ │ ├── sniffing.html
│ │ │ ├── stream/
│ │ │ │ ├── external_proxy.html
│ │ │ │ ├── stream_finalmask.html
│ │ │ │ ├── stream_grpc.html
│ │ │ │ ├── stream_httpupgrade.html
│ │ │ │ ├── stream_kcp.html
│ │ │ │ ├── stream_settings.html
│ │ │ │ ├── stream_sockopt.html
│ │ │ │ ├── stream_tcp.html
│ │ │ │ ├── stream_ws.html
│ │ │ │ └── stream_xhttp.html
│ │ │ └── tls_settings.html
│ │ ├── inbounds.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── modals/
│ │ │ ├── client_bulk_modal.html
│ │ │ ├── client_modal.html
│ │ │ ├── dns_presets_modal.html
│ │ │ ├── inbound_info_modal.html
│ │ │ ├── inbound_modal.html
│ │ │ ├── prompt_modal.html
│ │ │ ├── qrcode_modal.html
│ │ │ ├── text_modal.html
│ │ │ ├── two_factor_modal.html
│ │ │ ├── warp_modal.html
│ │ │ ├── xray_balancer_modal.html
│ │ │ ├── xray_dns_modal.html
│ │ │ ├── xray_fakedns_modal.html
│ │ │ ├── xray_outbound_modal.html
│ │ │ ├── xray_reverse_modal.html
│ │ │ └── xray_rule_modal.html
│ │ ├── settings/
│ │ │ ├── panel/
│ │ │ │ ├── general.html
│ │ │ │ ├── security.html
│ │ │ │ ├── subscription/
│ │ │ │ │ ├── general.html
│ │ │ │ │ ├── json.html
│ │ │ │ │ └── subpage.html
│ │ │ │ └── telegram.html
│ │ │ └── xray/
│ │ │ ├── advanced.html
│ │ │ ├── balancers.html
│ │ │ ├── basics.html
│ │ │ ├── dns.html
│ │ │ ├── outbounds.html
│ │ │ ├── reverse.html
│ │ │ └── routing.html
│ │ ├── settings.html
│ │ └── xray.html
│ ├── job/
│ │ ├── check_client_ip_job.go
│ │ ├── check_cpu_usage.go
│ │ ├── check_hash_storage.go
│ │ ├── check_xray_running_job.go
│ │ ├── clear_logs_job.go
│ │ ├── ldap_sync_job.go
│ │ ├── periodic_traffic_reset_job.go
│ │ ├── stats_notify_job.go
│ │ └── xray_traffic_job.go
│ ├── locale/
│ │ └── locale.go
│ ├── middleware/
│ │ ├── domainValidator.go
│ │ └── redirect.go
│ ├── network/
│ │ ├── auto_https_conn.go
│ │ └── auto_https_listener.go
│ ├── service/
│ │ ├── config.json
│ │ ├── inbound.go
│ │ ├── outbound.go
│ │ ├── panel.go
│ │ ├── server.go
│ │ ├── setting.go
│ │ ├── tgbot.go
│ │ ├── user.go
│ │ ├── warp.go
│ │ ├── xray.go
│ │ └── xray_setting.go
│ ├── session/
│ │ └── session.go
│ ├── translation/
│ │ ├── translate.ar_EG.toml
│ │ ├── translate.en_US.toml
│ │ ├── translate.es_ES.toml
│ │ ├── translate.fa_IR.toml
│ │ ├── translate.id_ID.toml
│ │ ├── translate.ja_JP.toml
│ │ ├── translate.pt_BR.toml
│ │ ├── translate.ru_RU.toml
│ │ ├── translate.tr_TR.toml
│ │ ├── translate.uk_UA.toml
│ │ ├── translate.vi_VN.toml
│ │ ├── translate.zh_CN.toml
│ │ └── translate.zh_TW.toml
│ ├── web.go
│ └── websocket/
│ ├── hub.go
│ └── notifier.go
├── windows_files/
│ └── readme.txt
├── x-ui.rc
├── x-ui.service.arch
├── x-ui.service.debian
├── x-ui.service.rhel
├── x-ui.sh
└── xray/
├── api.go
├── client_traffic.go
├── config.go
├── inbound.go
├── log_writer.go
├── process.go
└── traffic.go
SYMBOL INDEX (2091 symbols across 86 files)
FILE: config/config.go
type LogLevel (line 22) | type LogLevel
constant Debug (line 26) | Debug LogLevel = "debug"
constant Info (line 27) | Info LogLevel = "info"
constant Notice (line 28) | Notice LogLevel = "notice"
constant Warning (line 29) | Warning LogLevel = "warning"
constant Error (line 30) | Error LogLevel = "error"
function GetVersion (line 34) | func GetVersion() string {
function GetName (line 39) | func GetName() string {
function GetLogLevel (line 44) | func GetLogLevel() LogLevel {
function IsDebug (line 56) | func IsDebug() bool {
function GetBinFolderPath (line 61) | func GetBinFolderPath() string {
function getBaseDir (line 69) | func getBaseDir() string {
function GetDBFolderPath (line 87) | func GetDBFolderPath() string {
function GetDBPath (line 99) | func GetDBPath() string {
function GetLogFolder (line 104) | func GetLogFolder() string {
function copyFile (line 115) | func copyFile(src, dst string) error {
function init (line 136) | func init() {
FILE: database/db.go
constant defaultUsername (line 28) | defaultUsername = "admin"
constant defaultPassword (line 29) | defaultPassword = "admin"
function initModels (line 32) | func initModels() error {
function initUser (line 52) | func initUser() error {
function runSeeders (line 76) | func runSeeders(isUsersEmpty bool) error {
function isTableEmpty (line 116) | func isTableEmpty(tableName string) (bool, error) {
function InitDB (line 123) | func InitDB(dbPath string) error {
function CloseDB (line 162) | func CloseDB() error {
function GetDB (line 174) | func GetDB() *gorm.DB {
function IsNotFound (line 179) | func IsNotFound(err error) bool {
function IsSQLiteDB (line 184) | func IsSQLiteDB(file io.ReaderAt) (bool, error) {
function Checkpoint (line 195) | func Checkpoint() error {
function ValidateSQLiteDB (line 207) | func ValidateSQLiteDB(dbPath string) error {
FILE: database/model/model.go
type Protocol (line 12) | type Protocol
constant VMESS (line 16) | VMESS Protocol = "vmess"
constant VLESS (line 17) | VLESS Protocol = "vless"
constant Tunnel (line 18) | Tunnel Protocol = "tunnel"
constant HTTP (line 19) | HTTP Protocol = "http"
constant Trojan (line 20) | Trojan Protocol = "trojan"
constant Shadowsocks (line 21) | Shadowsocks Protocol = "shadowsocks"
constant Mixed (line 22) | Mixed Protocol = "mixed"
constant WireGuard (line 23) | WireGuard Protocol = "wireguard"
type User (line 27) | type User struct
type Inbound (line 34) | type Inbound struct
method GenXrayInboundConfig (line 81) | func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
type OutboundTraffics (line 59) | type OutboundTraffics struct
type InboundClientIps (line 68) | type InboundClientIps struct
type HistoryOfSeeders (line 75) | type HistoryOfSeeders struct
type Setting (line 101) | type Setting struct
type Client (line 108) | type Client struct
FILE: logger/logger.go
constant maxLogBufferSize (line 17) | maxLogBufferSize = 10240
constant logFileName (line 18) | logFileName = "3xui.log"
constant timeFormat (line 19) | timeFormat = "2006/01/02 15:04:05"
function InitLogger (line 36) | func InitLogger(level logging.Level) {
function initDefaultBackend (line 62) | func initDefaultBackend() logging.Backend {
function initFileBackend (line 86) | func initFileBackend() logging.Backend {
function newFormatter (line 111) | func newFormatter(withTime bool) logging.Formatter {
function CloseLogger (line 121) | func CloseLogger() {
function Debug (line 129) | func Debug(args ...any) {
function Debugf (line 135) | func Debugf(format string, args ...any) {
function Info (line 141) | func Info(args ...any) {
function Infof (line 147) | func Infof(format string, args ...any) {
function Notice (line 153) | func Notice(args ...any) {
function Noticef (line 159) | func Noticef(format string, args ...any) {
function Warning (line 165) | func Warning(args ...any) {
function Warningf (line 171) | func Warningf(format string, args ...any) {
function Error (line 177) | func Error(args ...any) {
function Errorf (line 183) | func Errorf(format string, args ...any) {
function addToBuffer (line 189) | func addToBuffer(level string, newLog string) {
function GetLogs (line 208) | func GetLogs(c int, level string) []string {
FILE: main.go
function runWebServer (line 29) | func runWebServer() {
function resetSetting (line 133) | func resetSetting() {
function showSetting (line 150) | func showSetting(show bool) {
function updateTgbotEnableSts (line 200) | func updateTgbotEnableSts(status bool) {
function updateTgbotSetting (line 220) | func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRunt...
function updateSetting (line 258) | func updateSetting(port int, username string, password string, webBasePa...
function updateCert (line 317) | func updateCert(publicKey string, privateKey string) {
function GetCertificate (line 359) | func GetCertificate(getCert bool) {
function GetListenIP (line 377) | func GetListenIP(getListen bool) {
function migrateDb (line 392) | func migrateDb() {
function main (line 406) | func main() {
FILE: sub/sub.go
function setEmbeddedTemplates (line 30) | func setEmbeddedTemplates(engine *gin.Engine) error {
type Server (line 45) | type Server struct
method initRouter (line 67) | func (s *Server) initRouter() (*gin.Engine, error) {
method getHtmlFiles (line 266) | func (s *Server) getHtmlFiles() ([]string, error) {
method Start (line 290) | func (s *Server) Start() (err error) {
method Stop (line 364) | func (s *Server) Stop() error {
method GetCtx (line 379) | func (s *Server) GetCtx() context.Context {
function NewServer (line 57) | func NewServer() *Server {
FILE: sub/subController.go
type SUBController (line 15) | type SUBController struct
method initRouter (line 76) | func (a *SUBController) initRouter(g *gin.RouterGroup) {
method subs (line 86) | func (a *SUBController) subs(c *gin.Context) {
method subJsons (line 161) | func (a *SUBController) subJsons(c *gin.Context) {
method ApplyCommonHeaders (line 180) | func (a *SUBController) ApplyCommonHeaders(
function NewSUBController (line 33) | func NewSUBController(
FILE: sub/subJsonService.go
type SubJsonService (line 22) | type SubJsonService struct
method GetJson (line 74) | func (s *SubJsonService) GetJson(subId string, host string) (string, s...
method getConfig (line 151) | func (s *SubJsonService) getConfig(inbound *model.Inbound, client mode...
method streamData (line 213) | func (s *SubJsonService) streamData(stream string) map[string]any {
method removeAcceptProxy (line 242) | func (s *SubJsonService) removeAcceptProxy(setting any) map[string]any {
method tlsData (line 250) | func (s *SubJsonService) tlsData(tData map[string]any) map[string]any {
method realityData (line 262) | func (s *SubJsonService) realityData(rData map[string]any) map[string]...
method genVnext (line 289) | func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettin...
method genVless (line 317) | func (s *SubJsonService) genVless(inbound *model.Inbound, streamSettin...
method genServer (line 345) | func (s *SubJsonService) genServer(inbound *model.Inbound, streamSetti...
function NewSubJsonService (line 34) | func NewSubJsonService(fragment string, noises string, mux string, rules...
type Outbound (line 384) | type Outbound struct
type VnextSetting (line 392) | type VnextSetting struct
type UserVnext (line 398) | type UserVnext struct
type ServerSetting (line 404) | type ServerSetting struct
FILE: sub/subService.go
type SubService (line 24) | type SubService struct
method GetSubs (line 42) | func (s *SubService) GetSubs(subId string, host string) ([]string, int...
method getInboundsBySubId (line 115) | func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inboun...
method getClientTraffics (line 132) | func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, ...
method getFallbackMaster (line 141) | func (s *SubService) getFallbackMaster(dest string, streamSettings str...
method getLink (line 164) | func (s *SubService) getLink(inbound *model.Inbound, email string) str...
method genVmessLink (line 178) | func (s *SubService) genVmessLink(inbound *model.Inbound, email string...
method genVlessLink (line 322) | func (s *SubService) genVlessLink(inbound *model.Inbound, email string...
method genTrojanLink (line 526) | func (s *SubService) genTrojanLink(inbound *model.Inbound, email strin...
method genShadowsocksLink (line 722) | func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email ...
method genRemark (line 888) | func (s *SubService) genRemark(inbound *model.Inbound, email string, e...
method ResolveRequest (line 1039) | func (s *SubService) ResolveRequest(c *gin.Context) (scheme string, ho...
method BuildURLs (line 1083) | func (s *SubService) BuildURLs(scheme, hostWithPort, subPath, subJsonP...
method getBaseSchemeAndHost (line 1109) | func (s *SubService) getBaseSchemeAndHost(requestScheme, requestHostWi...
method buildSingleURL (line 1133) | func (s *SubService) buildSingleURL(configuredURI, baseScheme, baseHos...
method joinPathWithID (line 1143) | func (s *SubService) joinPathWithID(basePath, subId string) string {
method BuildPageData (line 1152) | func (s *SubService) BuildPageData(subId string, hostHeader string, tr...
function NewSubService (line 34) | func NewSubService(showInfo bool, remarkModel string) *SubService {
function searchKey (line 973) | func searchKey(data any, key string) (any, bool) {
function searchHost (line 994) | func searchHost(headers any) string {
type PageData (line 1017) | type PageData struct
function getHostFromXFH (line 1190) | func getHostFromXFH(s string) (string, error) {
FILE: util/common/err.go
function NewErrorf (line 12) | func NewErrorf(format string, a ...any) error {
function NewError (line 18) | func NewError(a ...any) error {
function Recover (line 24) | func Recover(msg string) any {
FILE: util/common/format.go
function FormatTraffic (line 8) | func FormatTraffic(trafficBytes int64) string {
FILE: util/common/multi_error.go
type multiError (line 8) | type multiError
method Error (line 11) | func (e multiError) Error() string {
function Combine (line 22) | func Combine(maybeError ...error) error {
FILE: util/crypto/crypto.go
function HashPasswordAsBcrypt (line 9) | func HashPasswordAsBcrypt(password string) (string, error) {
function CheckPasswordHash (line 15) | func CheckPasswordHash(hash, password string) bool {
FILE: util/json_util/json.go
type RawMessage (line 9) | type RawMessage
method MarshalJSON (line 13) | func (m RawMessage) MarshalJSON() ([]byte, error) {
method UnmarshalJSON (line 21) | func (m *RawMessage) UnmarshalJSON(data []byte) error {
FILE: util/ldap/ldap.go
type Config (line 10) | type Config struct
function FetchVlessFlags (line 25) | func FetchVlessFlags(cfg Config) (map[string]bool, error) {
function AuthenticateUser (line 101) | func AuthenticateUser(cfg Config, username, password string) (bool, erro...
FILE: util/random/random.go
function init (line 20) | func init() {
function Seq (line 41) | func Seq(n int) string {
function Num (line 54) | func Num(n int) int {
FILE: util/reflect_util/reflect.go
function GetFields (line 7) | func GetFields(t reflect.Type) []reflect.StructField {
function GetFieldValues (line 17) | func GetFieldValues(v reflect.Value) []reflect.Value {
FILE: util/sys/psutil.go
function HostProc (line 10) | func HostProc(combineWith ...string) string
FILE: util/sys/sys_darwin.go
function GetTCPCount (line 18) | func GetTCPCount() (int, error) {
function GetUDPCount (line 26) | func GetUDPCount() (int, error) {
function CPUPercentRaw (line 44) | func CPUPercentRaw() (float64, error) {
FILE: util/sys/sys_linux.go
function getLinesNum (line 20) | func getLinesNum(filename string) (int, error) {
function GetTCPCount (line 53) | func GetTCPCount() (int, error) {
function GetUDPCount (line 68) | func GetUDPCount() (int, error) {
function safeGetLinesNum (line 85) | func safeGetLinesNum(path string) (int, error) {
function CPUPercentRaw (line 105) | func CPUPercentRaw() (float64, error) {
FILE: util/sys/sys_windows.go
function GetConnectionCount (line 18) | func GetConnectionCount(proto string) (int, error) {
function GetTCPCount (line 31) | func GetTCPCount() (int, error) {
function GetUDPCount (line 36) | func GetUDPCount() (int, error) {
type filetime (line 53) | type filetime struct
function ftToUint64 (line 60) | func ftToUint64(ft filetime) uint64 {
function CPUPercentRaw (line 67) | func CPUPercentRaw() (float64, error) {
FILE: web/assets/codemirror/fold/brace-fold.js
function bracketFolding (line 14) | function bracketFolding(pairs) {
function hasImport (line 77) | function hasImport(line) {
function hasInclude (line 101) | function hasInclude(line) {
FILE: web/assets/codemirror/fold/foldcode.js
function doFold (line 14) | function doFold(cm, pos, options, force) {
function makeWidget (line 63) | function makeWidget(cm, options, range) {
function getOption (line 147) | function getOption(cm, options, name) {
FILE: web/assets/codemirror/fold/foldgutter.js
function State (line 41) | function State(options) {
function parseOptions (line 46) | function parseOptions(opts) {
function isFolded (line 54) | function isFolded(cm, line) {
function marker (line 65) | function marker(spec) {
function updateFoldInfo (line 75) | function updateFoldInfo(cm, from, to) {
function classTest (line 104) | function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)...
function updateInViewport (line 106) | function updateInViewport(cm) {
function onGutterClick (line 115) | function onGutterClick(cm, line, gutter) {
function optionChange (line 125) | function optionChange(cm, option) {
function onChange (line 129) | function onChange(cm) {
function onViewportChange (line 138) | function onViewportChange(cm) {
function onFold (line 162) | function onFold(cm, from) {
FILE: web/assets/codemirror/hint/javascript-hint.js
function forEach (line 14) | function forEach(arr, f) {
function arrayContains (line 18) | function arrayContains(arr, item) {
function scriptHint (line 31) | function scriptHint(editor, keywords, getToken, options) {
function javascriptHint (line 62) | function javascriptHint(editor, options) {
function getCoffeeScriptToken (line 69) | function getCoffeeScriptToken(editor, cur) {
function coffeescriptHint (line 87) | function coffeescriptHint(editor, options) {
function forAllProps (line 102) | function forAllProps(obj, callback) {
function getCompletions (line 111) | function getCompletions(token, context, keywords, options) {
FILE: web/assets/codemirror/javascript.js
function kw (line 26) | function kw(type) {return {type: type, style: "keyword"};}
function readRegexp (line 47) | function readRegexp(stream) {
function ret (line 62) | function ret(tp, style, cont) {
function tokenBase (line 66) | function tokenBase(stream, state) {
function tokenString (line 137) | function tokenString(quote) {
function tokenComment (line 153) | function tokenComment(stream, state) {
function tokenQuasi (line 165) | function tokenQuasi(stream, state) {
function findFatArrow (line 185) | function findFatArrow(stream, state) {
function JSLexical (line 225) | function JSLexical(indented, column, type, align, prev, info) {
function inScope (line 234) | function inScope(state, varname) {
function parseJS (line 244) | function parseJS(state, style, type, content, stream) {
function pass (line 268) | function pass() {
function cont (line 271) | function cont() {
function inList (line 275) | function inList(name, list) {
function register (line 279) | function register(varname) {
function registerVarScoped (line 300) | function registerVarScoped(varname, context) {
function isModifier (line 315) | function isModifier(name) {
function Context (line 321) | function Context(prev, vars, block) { this.prev = prev; this.vars = vars...
function Var (line 322) | function Var(name, next) { this.name = name; this.next = next }
function pushcontext (line 325) | function pushcontext() {
function pushblockcontext (line 329) | function pushblockcontext() {
function popcontext (line 334) | function popcontext() {
function pushlex (line 339) | function pushlex(type, info) {
function poplex (line 350) | function poplex() {
function expect (line 360) | function expect(wanted) {
function statement (line 369) | function statement(type, value) {
function maybeCatchBinding (line 418) | function maybeCatchBinding(type) {
function expression (line 421) | function expression(type, value) {
function expressionNoComma (line 424) | function expressionNoComma(type, value) {
function parenExpr (line 427) | function parenExpr(type) {
function expressionInner (line 431) | function expressionInner(type, value, noComma) {
function maybeexpression (line 451) | function maybeexpression(type) {
function maybeoperatorComma (line 456) | function maybeoperatorComma(type, value) {
function maybeoperatorNoComma (line 460) | function maybeoperatorNoComma(type, value, noComma) {
function quasi (line 483) | function quasi(type, value) {
function continueQuasi (line 488) | function continueQuasi(type) {
function arrowBody (line 495) | function arrowBody(type) {
function arrowBodyNoComma (line 499) | function arrowBodyNoComma(type) {
function maybeTarget (line 503) | function maybeTarget(noComma) {
function target (line 510) | function target(_, value) {
function targetNoComma (line 513) | function targetNoComma(_, value) {
function maybelabel (line 516) | function maybelabel(type) {
function property (line 520) | function property(type) {
function objprop (line 523) | function objprop(type, value) {
function getterSetter (line 553) | function getterSetter(type) {
function afterprop (line 558) | function afterprop(type) {
function commasep (line 562) | function commasep(what, end, sep) {
function contCommasep (line 581) | function contCommasep(what, end, info) {
function block (line 586) | function block(type) {
function maybetype (line 590) | function maybetype(type, value) {
function maybetypeOrIn (line 596) | function maybetypeOrIn(type, value) {
function mayberettype (line 599) | function mayberettype(type) {
function isKW (line 605) | function isKW(_, value) {
function typeexpr (line 611) | function typeexpr(type, value) {
function maybeReturnType (line 628) | function maybeReturnType(type) {
function typeprops (line 631) | function typeprops(type) {
function typeprop (line 636) | function typeprop(type, value) {
function quasiType (line 652) | function quasiType(type, value) {
function continueQuasiType (line 657) | function continueQuasiType(type) {
function typearg (line 664) | function typearg(type, value) {
function afterType (line 670) | function afterType(type, value) {
function maybeTypeArgs (line 677) | function maybeTypeArgs(_, value) {
function typeparam (line 680) | function typeparam() {
function maybeTypeDefault (line 683) | function maybeTypeDefault(_, value) {
function vardef (line 686) | function vardef(_, value) {
function pattern (line 690) | function pattern(type, value) {
function proppattern (line 697) | function proppattern(type, value) {
function eltpattern (line 708) | function eltpattern() {
function maybeAssign (line 711) | function maybeAssign(_type, value) {
function vardefCont (line 714) | function vardefCont(type) {
function maybeelse (line 717) | function maybeelse(type, value) {
function forspec (line 720) | function forspec(type, value) {
function forspec1 (line 724) | function forspec1(type) {
function forspec2 (line 729) | function forspec2(type, value) {
function functiondef (line 735) | function functiondef(type, value) {
function functiondecl (line 741) | function functiondecl(type, value) {
function typename (line 747) | function typename(type, value) {
function funarg (line 755) | function funarg(type, value) {
function classExpression (line 762) | function classExpression(type, value) {
function className (line 767) | function className(type, value) {
function classNameAfter (line 770) | function classNameAfter(type, value) {
function classBody (line 778) | function classBody(type, value) {
function classfield (line 802) | function classfield(type, value) {
function afterExport (line 810) | function afterExport(type, value) {
function exportField (line 816) | function exportField(type, value) {
function afterImport (line 820) | function afterImport(type) {
function importSpec (line 826) | function importSpec(type, value) {
function maybeMoreImports (line 832) | function maybeMoreImports(type) {
function maybeAs (line 835) | function maybeAs(_type, value) {
function maybeFrom (line 838) | function maybeFrom(_type, value) {
function arrayLiteral (line 841) | function arrayLiteral(type) {
function enumdef (line 845) | function enumdef() {
function enummember (line 848) | function enummember() {
function isContinuedStatement (line 852) | function isContinuedStatement(state, textAfter) {
function expressionAllowed (line 858) | function expressionAllowed(stream, state, backUp) {
FILE: web/assets/codemirror/jshint.js
function s (line 6) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function replacer (line 129) | function replacer(key, value) {
function truncate (line 142) | function truncate(s, n) {
function getMessage (line 150) | function getMessage(self) {
function fail (line 167) | function fail(actual, expected, message, operator, stackStartFunction) {
function ok (line 187) | function ok(value, message) {
function _deepEqual (line 218) | function _deepEqual(actual, expected) {
function isArguments (line 263) | function isArguments(object) {
function objEquiv (line 267) | function objEquiv(a, b) {
function expectedException (line 336) | function expectedException(actual, expected) {
function _throws (line 352) | function _throws(shouldThrow, block, expected, message) {
function deprecated (line 516) | function deprecated() {
function inspect (line 563) | function inspect(obj, opts) {
function stylizeWithColor (line 621) | function stylizeWithColor(str, styleType) {
function stylizeNoColor (line 633) | function stylizeNoColor(str, styleType) {
function arrayToHash (line 638) | function arrayToHash(array) {
function formatValue (line 649) | function formatValue(ctx, value, recurseTimes) {
function formatPrimitive (line 762) | function formatPrimitive(ctx, value) {
function formatError (line 781) | function formatError(value) {
function formatArray (line 786) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
function formatProperty (line 806) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
function reduceToSingleString (line 865) | function reduceToSingleString(output, base, braces) {
function isArray (line 888) | function isArray(ar) {
function isBoolean (line 893) | function isBoolean(arg) {
function isNull (line 898) | function isNull(arg) {
function isNullOrUndefined (line 903) | function isNullOrUndefined(arg) {
function isNumber (line 908) | function isNumber(arg) {
function isString (line 913) | function isString(arg) {
function isSymbol (line 918) | function isSymbol(arg) {
function isUndefined (line 923) | function isUndefined(arg) {
function isRegExp (line 928) | function isRegExp(re) {
function isObject (line 933) | function isObject(arg) {
function isDate (line 938) | function isDate(d) {
function isError (line 943) | function isError(e) {
function isFunction (line 949) | function isFunction(arg) {
function isPrimitive (line 954) | function isPrimitive(arg) {
function objectToString (line 966) | function objectToString(o) {
function pad (line 971) | function pad(n) {
function timestamp (line 980) | function timestamp() {
function hasOwnProperty (line 1022) | function hasOwnProperty(obj, prop) {
function log (line 1070) | function log() {}
function info (line 1072) | function info() {
function warn (line 1076) | function warn() {
function error (line 1080) | function error() {
function time (line 1084) | function time(label) {
function timeEnd (line 1088) | function timeEnd(label) {
function trace (line 1098) | function trace() {
function dir (line 1105) | function dir(object) {
function consoleAssert (line 1109) | function consoleAssert(expression) {
function now (line 1120) | function now() {
function EventEmitter (line 1146) | function EventEmitter() {
function g (line 1288) | function g() {
function isFunction (line 1411) | function isFunction(arg) {
function isNumber (line 1415) | function isNumber(arg) {
function isObject (line 1419) | function isObject(arg) {
function isUndefined (line 1423) | function isUndefined(arg) {
function apply (line 1899) | function apply(func, thisArg, args) {
function arrayAggregator (line 1919) | function arrayAggregator(array, setter, iteratee, accumulator) {
function arrayEach (line 1939) | function arrayEach(array, iteratee) {
function arrayEachRight (line 1960) | function arrayEachRight(array, iteratee) {
function arrayEvery (line 1981) | function arrayEvery(array, predicate) {
function arrayFilter (line 2002) | function arrayFilter(array, predicate) {
function arrayIncludes (line 2026) | function arrayIncludes(array, value) {
function arrayIncludesWith (line 2040) | function arrayIncludesWith(array, value, comparator) {
function arrayMap (line 2061) | function arrayMap(array, iteratee) {
function arrayPush (line 2080) | function arrayPush(array, values) {
function arrayReduce (line 2103) | function arrayReduce(array, iteratee, accumulator, initAccum) {
function arrayReduceRight (line 2128) | function arrayReduceRight(array, iteratee, accumulator, initAccum) {
function arraySome (line 2149) | function arraySome(array, predicate) {
function asciiToArray (line 2177) | function asciiToArray(string) {
function asciiWords (line 2188) | function asciiWords(string) {
function baseFindKey (line 2203) | function baseFindKey(collection, predicate, eachFunc) {
function baseFindIndex (line 2225) | function baseFindIndex(array, predicate, fromIndex, fromRight) {
function baseIndexOf (line 2246) | function baseIndexOf(array, value, fromIndex) {
function baseIndexOfWith (line 2262) | function baseIndexOfWith(array, value, fromIndex, comparator) {
function baseIsNaN (line 2281) | function baseIsNaN(value) {
function baseMean (line 2294) | function baseMean(array, iteratee) {
function baseProperty (line 2306) | function baseProperty(key) {
function basePropertyOf (line 2319) | function basePropertyOf(object) {
function baseReduce (line 2338) | function baseReduce(collection, iteratee, accumulator, initAccum, eachFu...
function baseSortBy (line 2357) | function baseSortBy(array, comparer) {
function baseSum (line 2376) | function baseSum(array, iteratee) {
function baseTimes (line 2399) | function baseTimes(n, iteratee) {
function baseToPairs (line 2418) | function baseToPairs(object, props) {
function baseUnary (line 2431) | function baseUnary(func) {
function baseValues (line 2447) | function baseValues(object, props) {
function cacheHas (line 2461) | function cacheHas(cache, key) {
function charsStartIndex (line 2474) | function charsStartIndex(strSymbols, chrSymbols) {
function charsEndIndex (line 2491) | function charsEndIndex(strSymbols, chrSymbols) {
function countHolders (line 2506) | function countHolders(array, placeholder) {
function escapeStringChar (line 2544) | function escapeStringChar(chr) {
function getValue (line 2556) | function getValue(object, key) {
function hasUnicode (line 2567) | function hasUnicode(string) {
function hasUnicodeWord (line 2578) | function hasUnicodeWord(string) {
function iteratorToArray (line 2589) | function iteratorToArray(iterator) {
function mapToArray (line 2606) | function mapToArray(map) {
function overArg (line 2624) | function overArg(func, transform) {
function replaceHolders (line 2639) | function replaceHolders(array, placeholder) {
function setToArray (line 2662) | function setToArray(set) {
function setToPairs (line 2679) | function setToPairs(set) {
function strictIndexOf (line 2699) | function strictIndexOf(array, value, fromIndex) {
function strictLastIndexOf (line 2721) | function strictLastIndexOf(array, value, fromIndex) {
function stringSize (line 2738) | function stringSize(string) {
function stringToArray (line 2751) | function stringToArray(string) {
function unicodeSize (line 2773) | function unicodeSize(string) {
function unicodeToArray (line 2788) | function unicodeToArray(string) {
function unicodeWords (line 2799) | function unicodeWords(string) {
function lodash (line 3076) | function lodash(value) {
function object (line 3097) | function object() {}
function baseLodash (line 3117) | function baseLodash() {
function LodashWrapper (line 3128) | function LodashWrapper(value, chainAll) {
function LazyWrapper (line 3213) | function LazyWrapper(value) {
function lazyClone (line 3231) | function lazyClone() {
function lazyReverse (line 3250) | function lazyReverse() {
function lazyValue (line 3270) | function lazyValue() {
function Hash (line 3332) | function Hash(entries) {
function hashClear (line 3350) | function hashClear() {
function hashDelete (line 3365) | function hashDelete(key) {
function hashGet (line 3380) | function hashGet(key) {
function hashHas (line 3398) | function hashHas(key) {
function hashSet (line 3413) | function hashSet(key, value) {
function ListCache (line 3436) | function ListCache(entries) {
function listCacheClear (line 3454) | function listCacheClear() {
function listCacheDelete (line 3468) | function listCacheDelete(key) {
function listCacheGet (line 3494) | function listCacheGet(key) {
function listCacheHas (line 3510) | function listCacheHas(key) {
function listCacheSet (line 3524) | function listCacheSet(key, value) {
function MapCache (line 3553) | function MapCache(entries) {
function mapCacheClear (line 3571) | function mapCacheClear() {
function mapCacheDelete (line 3589) | function mapCacheDelete(key) {
function mapCacheGet (line 3604) | function mapCacheGet(key) {
function mapCacheHas (line 3617) | function mapCacheHas(key) {
function mapCacheSet (line 3631) | function mapCacheSet(key, value) {
function SetCache (line 3657) | function SetCache(values) {
function setCacheAdd (line 3677) | function setCacheAdd(value) {
function setCacheHas (line 3691) | function setCacheHas(value) {
function Stack (line 3708) | function Stack(entries) {
function stackClear (line 3720) | function stackClear() {
function stackDelete (line 3734) | function stackDelete(key) {
function stackGet (line 3751) | function stackGet(key) {
function stackHas (line 3764) | function stackHas(key) {
function stackSet (line 3778) | function stackSet(key, value) {
function arrayLikeKeys (line 3811) | function arrayLikeKeys(value, inherited) {
function arraySample (line 3845) | function arraySample(array) {
function arraySampleSize (line 3858) | function arraySampleSize(array, n) {
function arrayShuffle (line 3869) | function arrayShuffle(array) {
function assignMergeValue (line 3882) | function assignMergeValue(object, key, value) {
function assignValue (line 3899) | function assignValue(object, key, value) {
function assocIndexOf (line 3915) | function assocIndexOf(array, key) {
function baseAggregator (line 3936) | function baseAggregator(collection, setter, iteratee, accumulator) {
function baseAssign (line 3952) | function baseAssign(object, source) {
function baseAssignIn (line 3965) | function baseAssignIn(object, source) {
function baseAssignValue (line 3978) | function baseAssignValue(object, key, value) {
function baseAt (line 3999) | function baseAt(object, paths) {
function baseClamp (line 4020) | function baseClamp(number, lower, upper) {
function baseClone (line 4048) | function baseClone(value, bitmask, customizer, key, object, stack) {
function baseConforms (line 4131) | function baseConforms(source) {
function baseConformsTo (line 4146) | function baseConformsTo(object, source, props) {
function baseDelay (line 4174) | function baseDelay(func, wait, args) {
function baseDifference (line 4192) | function baseDifference(array, values, iteratee, comparator) {
function baseEvery (line 4266) | function baseEvery(collection, predicate) {
function baseExtremum (line 4285) | function baseExtremum(array, iteratee, comparator) {
function baseFill (line 4314) | function baseFill(array, value, start, end) {
function baseFilter (line 4340) | function baseFilter(collection, predicate) {
function baseFlatten (line 4361) | function baseFlatten(array, depth, predicate, isStrict, result) {
function baseForOwn (line 4417) | function baseForOwn(object, iteratee) {
function baseForOwnRight (line 4429) | function baseForOwnRight(object, iteratee) {
function baseFunctions (line 4442) | function baseFunctions(object, props) {
function baseGet (line 4456) | function baseGet(object, path) {
function baseGetAllKeys (line 4479) | function baseGetAllKeys(object, keysFunc, symbolsFunc) {
function baseGetTag (line 4491) | function baseGetTag(value) {
function baseGt (line 4509) | function baseGt(value, other) {
function baseHas (line 4521) | function baseHas(object, key) {
function baseHasIn (line 4533) | function baseHasIn(object, key) {
function baseInRange (line 4546) | function baseInRange(number, start, end) {
function baseIntersection (line 4560) | function baseIntersection(arrays, iteratee, comparator) {
function baseInverter (line 4624) | function baseInverter(object, setter, iteratee, accumulator) {
function baseInvoke (line 4641) | function baseInvoke(object, path, args) {
function baseIsArguments (line 4655) | function baseIsArguments(value) {
function baseIsArrayBuffer (line 4666) | function baseIsArrayBuffer(value) {
function baseIsDate (line 4677) | function baseIsDate(value) {
function baseIsEqual (line 4695) | function baseIsEqual(value, other, bitmask, customizer, stack) {
function baseIsEqualDeep (line 4719) | function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, ...
function baseIsMap (line 4771) | function baseIsMap(value) {
function baseIsMatch (line 4785) | function baseIsMatch(object, source, matchData, customizer) {
function baseIsNative (line 4837) | function baseIsNative(value) {
function baseIsRegExp (line 4852) | function baseIsRegExp(value) {
function baseIsSet (line 4863) | function baseIsSet(value) {
function baseIsTypedArray (line 4874) | function baseIsTypedArray(value) {
function baseIteratee (line 4886) | function baseIteratee(value) {
function baseKeys (line 4910) | function baseKeys(object) {
function baseKeysIn (line 4930) | function baseKeysIn(object) {
function baseLt (line 4954) | function baseLt(value, other) {
function baseMap (line 4966) | function baseMap(collection, iteratee) {
function baseMatches (line 4983) | function baseMatches(source) {
function baseMatchesProperty (line 5001) | function baseMatchesProperty(path, srcValue) {
function baseMerge (line 5024) | function baseMerge(object, source, srcIndex, customizer, stack) {
function baseMergeDeep (line 5061) | function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customi...
function baseNth (line 5131) | function baseNth(array, n) {
function baseOrderBy (line 5149) | function baseOrderBy(collection, iteratees, orders) {
function basePick (line 5187) | function basePick(object, paths) {
function basePickBy (line 5202) | function basePickBy(object, paths, predicate) {
function basePropertyDeep (line 5225) | function basePropertyDeep(path) {
function basePullAll (line 5242) | function basePullAll(array, values, iteratee, comparator) {
function basePullAt (line 5278) | function basePullAt(array, indexes) {
function baseRandom (line 5305) | function baseRandom(lower, upper) {
function baseRange (line 5320) | function baseRange(start, end, step, fromRight) {
function baseRepeat (line 5340) | function baseRepeat(string, n) {
function baseRest (line 5368) | function baseRest(func, start) {
function baseSample (line 5379) | function baseSample(collection) {
function baseSampleSize (line 5391) | function baseSampleSize(collection, n) {
function baseSet (line 5406) | function baseSet(object, path, value, customizer) {
function baseShuffle (line 5477) | function baseShuffle(collection) {
function baseSlice (line 5490) | function baseSlice(array, start, end) {
function baseSome (line 5520) | function baseSome(collection, predicate) {
function baseSortedIndex (line 5542) | function baseSortedIndex(array, value, retHighest) {
function baseSortedIndexBy (line 5576) | function baseSortedIndexBy(array, value, iteratee, retHighest) {
function baseSortedUniq (line 5628) | function baseSortedUniq(array, iteratee) {
function baseToNumber (line 5654) | function baseToNumber(value) {
function baseToString (line 5672) | function baseToString(value) {
function baseUniq (line 5697) | function baseUniq(array, iteratee, comparator) {
function baseUnset (line 5757) | function baseUnset(object, path) {
function baseUpdate (line 5773) | function baseUpdate(object, path, updater, customizer) {
function baseWhile (line 5788) | function baseWhile(array, predicate, isDrop, fromRight) {
function baseWrapperValue (line 5810) | function baseWrapperValue(value, actions) {
function baseXor (line 5830) | function baseXor(arrays, iteratee, comparator) {
function baseZipObject (line 5860) | function baseZipObject(props, values, assignFunc) {
function castArrayLikeObject (line 5880) | function castArrayLikeObject(value) {
function castFunction (line 5891) | function castFunction(value) {
function castPath (line 5903) | function castPath(value, object) {
function castSlice (line 5930) | function castSlice(array, start, end) {
function cloneBuffer (line 5954) | function cloneBuffer(buffer, isDeep) {
function cloneArrayBuffer (line 5972) | function cloneArrayBuffer(arrayBuffer) {
function cloneDataView (line 5986) | function cloneDataView(dataView, isDeep) {
function cloneRegExp (line 5998) | function cloneRegExp(regexp) {
function cloneSymbol (line 6011) | function cloneSymbol(symbol) {
function cloneTypedArray (line 6023) | function cloneTypedArray(typedArray, isDeep) {
function compareAscending (line 6036) | function compareAscending(value, other) {
function compareMultiple (line 6080) | function compareMultiple(object, other, orders) {
function composeArgs (line 6118) | function composeArgs(args, partials, holders, isCurried) {
function composeArgsRight (line 6153) | function composeArgsRight(args, partials, holders, isCurried) {
function copyArray (line 6187) | function copyArray(source, array) {
function copyObject (line 6208) | function copyObject(source, props, object, customizer) {
function copySymbols (line 6242) | function copySymbols(source, object) {
function copySymbolsIn (line 6254) | function copySymbolsIn(source, object) {
function createAggregator (line 6266) | function createAggregator(setter, initializer) {
function createAssigner (line 6282) | function createAssigner(assigner) {
function createBaseEach (line 6316) | function createBaseEach(eachFunc, fromRight) {
function createBaseFor (line 6344) | function createBaseFor(fromRight) {
function createBind (line 6371) | function createBind(func, bitmask, thisArg) {
function createCaseFirst (line 6389) | function createCaseFirst(methodName) {
function createCompounder (line 6416) | function createCompounder(callback) {
function createCtor (line 6430) | function createCtor(Ctor) {
function createCurry (line 6464) | function createCurry(func, bitmask, arity) {
function createFind (line 6499) | function createFind(findIndexFunc) {
function createFlow (line 6519) | function createFlow(fromRight) {
function createHybrid (line 6592) | function createHybrid(func, bitmask, thisArg, partials, holders, partial...
function createInverter (line 6654) | function createInverter(setter, toIteratee) {
function createMathOperation (line 6668) | function createMathOperation(operator, defaultValue) {
function createOver (line 6701) | function createOver(arrayFunc) {
function createPadding (line 6722) | function createPadding(length, chars) {
function createPartial (line 6747) | function createPartial(func, bitmask, thisArg, partials) {
function createRange (line 6777) | function createRange(fromRight) {
function createRelationalOperation (line 6802) | function createRelationalOperation(operator) {
function createRecurry (line 6829) | function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, pa...
function createRound (line 6862) | function createRound(methodName) {
function createToPairs (line 6898) | function createToPairs(keysFunc) {
function createWrap (line 6936) | function createWrap(func, bitmask, thisArg, partials, holders, argPos, a...
function customDefaultsAssignIn (line 7003) | function customDefaultsAssignIn(objValue, srcValue, key, object) {
function customDefaultsMerge (line 7025) | function customDefaultsMerge(objValue, srcValue, key, object, source, st...
function customOmitClone (line 7044) | function customOmitClone(value) {
function equalArrays (line 7061) | function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
function equalByTag (line 7140) | function equalByTag(object, other, tag, bitmask, customizer, equalFunc, ...
function equalObjects (line 7218) | function equalObjects(object, other, bitmask, customizer, equalFunc, sta...
function flatRest (line 7290) | function flatRest(func) {
function getAllKeys (line 7301) | function getAllKeys(object) {
function getAllKeysIn (line 7313) | function getAllKeysIn(object) {
function getFuncName (line 7335) | function getFuncName(func) {
function getHolder (line 7357) | function getHolder(func) {
function getIteratee (line 7373) | function getIteratee() {
function getMapData (line 7387) | function getMapData(map, key) {
function getMatchData (line 7401) | function getMatchData(object) {
function getNative (line 7422) | function getNative(object, key) {
function getRawTag (line 7434) | function getRawTag(value) {
function getView (line 7530) | function getView(start, end, transforms) {
function getWrapDetails (line 7555) | function getWrapDetails(source) {
function hasPath (line 7569) | function hasPath(object, path, hasFunc) {
function initCloneArray (line 7598) | function initCloneArray(array) {
function initCloneObject (line 7617) | function initCloneObject(object) {
function initCloneByTag (line 7635) | function initCloneByTag(object, tag, isDeep) {
function insertWrapDetails (line 7679) | function insertWrapDetails(source, details) {
function isFlattenable (line 7697) | function isFlattenable(value) {
function isIndex (line 7710) | function isIndex(value, length) {
function isIterateeCall (line 7730) | function isIterateeCall(value, index, object) {
function isKey (line 7752) | function isKey(value, object) {
function isKeyable (line 7772) | function isKeyable(value) {
function isLaziable (line 7787) | function isLaziable(func) {
function isMasked (line 7808) | function isMasked(func) {
function isPrototype (line 7828) | function isPrototype(value) {
function isStrictComparable (line 7843) | function isStrictComparable(value) {
function matchesStrictComparable (line 7856) | function matchesStrictComparable(key, srcValue) {
function memoizeCapped (line 7874) | function memoizeCapped(func) {
function mergeData (line 7902) | function mergeData(data, source) {
function nativeKeysIn (line 7966) | function nativeKeysIn(object) {
function objectToString (line 7983) | function objectToString(value) {
function overRest (line 7996) | function overRest(func, start, transform) {
function parent (line 8025) | function parent(object, path) {
function reorder (line 8039) | function reorder(array, indexes) {
function safeGet (line 8059) | function safeGet(object, key) {
function setWrapToString (line 8119) | function setWrapToString(wrapper, reference, bitmask) {
function shortOut (line 8133) | function shortOut(func) {
function shuffleSelf (line 8161) | function shuffleSelf(array, size) {
function toKey (line 8203) | function toKey(value) {
function toSource (line 8218) | function toSource(func) {
function updateWrapDetails (line 8238) | function updateWrapDetails(details, bitmask) {
function wrapperClone (line 8255) | function wrapperClone(wrapper) {
function chunk (line 8289) | function chunk(array, size, guard) {
function compact (line 8324) | function compact(array) {
function concat (line 8361) | function concat() {
function drop (line 8497) | function drop(array, n, guard) {
function dropRight (line 8531) | function dropRight(array, n, guard) {
function dropRightWhile (line 8576) | function dropRightWhile(array, predicate) {
function dropWhile (line 8617) | function dropWhile(array, predicate) {
function fill (line 8652) | function fill(array, value, start, end) {
function findIndex (line 8699) | function findIndex(array, predicate, fromIndex) {
function findLastIndex (line 8746) | function findLastIndex(array, predicate, fromIndex) {
function flatten (line 8775) | function flatten(array) {
function flattenDeep (line 8794) | function flattenDeep(array) {
function flattenDepth (line 8819) | function flattenDepth(array, depth) {
function fromPairs (line 8843) | function fromPairs(pairs) {
function head (line 8873) | function head(array) {
function indexOf (line 8900) | function indexOf(array, value, fromIndex) {
function initial (line 8926) | function initial(array) {
function join (line 9041) | function join(array, separator) {
function last (line 9059) | function last(array) {
function lastIndexOf (line 9085) | function lastIndexOf(array, value, fromIndex) {
function nth (line 9121) | function nth(array, n) {
function pullAll (line 9170) | function pullAll(array, values) {
function pullAllBy (line 9199) | function pullAllBy(array, values, iteratee) {
function pullAllWith (line 9228) | function pullAllWith(array, values, comparator) {
function remove (line 9297) | function remove(array, predicate) {
function reverse (line 9341) | function reverse(array) {
function slice (line 9361) | function slice(array, start, end) {
function sortedIndex (line 9394) | function sortedIndex(array, value) {
function sortedIndexBy (line 9423) | function sortedIndexBy(array, value, iteratee) {
function sortedIndexOf (line 9443) | function sortedIndexOf(array, value) {
function sortedLastIndex (line 9472) | function sortedLastIndex(array, value) {
function sortedLastIndexBy (line 9501) | function sortedLastIndexBy(array, value, iteratee) {
function sortedLastIndexOf (line 9521) | function sortedLastIndexOf(array, value) {
function sortedUniq (line 9547) | function sortedUniq(array) {
function sortedUniqBy (line 9569) | function sortedUniqBy(array, iteratee) {
function tail (line 9589) | function tail(array) {
function take (line 9619) | function take(array, n, guard) {
function takeRight (line 9652) | function takeRight(array, n, guard) {
function takeRightWhile (line 9697) | function takeRightWhile(array, predicate) {
function takeWhile (line 9738) | function takeWhile(array, predicate) {
function uniq (line 9840) | function uniq(array) {
function uniqBy (line 9867) | function uniqBy(array, iteratee) {
function uniqWith (line 9891) | function uniqWith(array, comparator) {
function unzip (line 9915) | function unzip(array) {
function unzipWith (line 9952) | function unzipWith(array, iteratee) {
function zipObject (line 10105) | function zipObject(props, values) {
function zipObjectDeep (line 10124) | function zipObjectDeep(props, values) {
function chain (line 10187) | function chain(value) {
function tap (line 10216) | function tap(value, interceptor) {
function thru (line 10244) | function thru(value, interceptor) {
function wrapperChain (line 10315) | function wrapperChain() {
function wrapperCommit (line 10345) | function wrapperCommit() {
function wrapperNext (line 10371) | function wrapperNext() {
function wrapperToIterator (line 10399) | function wrapperToIterator() {
function wrapperPlant (line 10427) | function wrapperPlant(value) {
function wrapperReverse (line 10467) | function wrapperReverse() {
function wrapperValue (line 10499) | function wrapperValue() {
function every (line 10576) | function every(collection, predicate, guard) {
function filter (line 10625) | function filter(collection, predicate) {
function flatMap (line 10710) | function flatMap(collection, iteratee) {
function flatMapDeep (line 10734) | function flatMapDeep(collection, iteratee) {
function flatMapDepth (line 10759) | function flatMapDepth(collection, iteratee, depth) {
function forEach (line 10794) | function forEach(collection, iteratee) {
function forEachRight (line 10819) | function forEachRight(collection, iteratee) {
function includes (line 10885) | function includes(collection, value, fromIndex, guard) {
function map (line 11006) | function map(collection, iteratee) {
function orderBy (line 11040) | function orderBy(collection, iteratees, orders, guard) {
function reduce (line 11131) | function reduce(collection, iteratee, accumulator) {
function reduceRight (line 11160) | function reduceRight(collection, iteratee, accumulator) {
function reject (line 11201) | function reject(collection, predicate) {
function sample (line 11220) | function sample(collection) {
function sampleSize (line 11245) | function sampleSize(collection, n, guard) {
function shuffle (line 11270) | function shuffle(collection) {
function size (line 11296) | function size(collection) {
function some (line 11346) | function some(collection, predicate, guard) {
function after (line 11444) | function after(n, func) {
function ary (line 11473) | function ary(func, n, guard) {
function before (line 11496) | function before(n, func) {
function curry (line 11652) | function curry(func, arity, guard) {
function curryRight (line 11697) | function curryRight(func, arity, guard) {
function debounce (line 11758) | function debounce(func, wait, options) {
function flip (line 11946) | function flip(func) {
function memoize (line 11994) | function memoize(func, resolver) {
function negate (line 12037) | function negate(predicate) {
function once (line 12071) | function once(func) {
function rest (line 12249) | function rest(func, start) {
function spread (line 12291) | function spread(func, start) {
function throttle (line 12351) | function throttle(func, wait, options) {
function unary (line 12384) | function unary(func) {
function wrap (line 12410) | function wrap(value, wrapper) {
function castArray (line 12449) | function castArray() {
function clone (line 12483) | function clone(value) {
function cloneWith (line 12518) | function cloneWith(value, customizer) {
function cloneDeep (line 12541) | function cloneDeep(value) {
function cloneDeepWith (line 12573) | function cloneDeepWith(value, customizer) {
function conformsTo (line 12602) | function conformsTo(object, source) {
function eq (line 12638) | function eq(value, other) {
function isArrayLike (line 12786) | function isArrayLike(value) {
function isArrayLikeObject (line 12815) | function isArrayLikeObject(value) {
function isBoolean (line 12836) | function isBoolean(value) {
function isElement (line 12896) | function isElement(value) {
function isEmpty (line 12933) | function isEmpty(value) {
function isEqual (line 12985) | function isEqual(value, other) {
function isEqualWith (line 13021) | function isEqualWith(value, other, customizer) {
function isError (line 13045) | function isError(value) {
function isFinite (line 13080) | function isFinite(value) {
function isFunction (line 13101) | function isFunction(value) {
function isInteger (line 13137) | function isInteger(value) {
function isLength (line 13167) | function isLength(value) {
function isObject (line 13197) | function isObject(value) {
function isObjectLike (line 13226) | function isObjectLike(value) {
function isMatch (line 13277) | function isMatch(object, source) {
function isMatchWith (line 13313) | function isMatchWith(object, source, customizer) {
function isNaN (line 13346) | function isNaN(value) {
function isNative (line 13379) | function isNative(value) {
function isNull (line 13403) | function isNull(value) {
function isNil (line 13427) | function isNil(value) {
function isNumber (line 13457) | function isNumber(value) {
function isPlainObject (line 13490) | function isPlainObject(value) {
function isSafeInteger (line 13549) | function isSafeInteger(value) {
function isString (line 13589) | function isString(value) {
function isSymbol (line 13611) | function isSymbol(value) {
function isUndefined (line 13652) | function isUndefined(value) {
function isWeakMap (line 13673) | function isWeakMap(value) {
function isWeakSet (line 13694) | function isWeakSet(value) {
function toArray (line 13773) | function toArray(value) {
function toFinite (line 13812) | function toFinite(value) {
function toInteger (line 13850) | function toInteger(value) {
function toLength (line 13884) | function toLength(value) {
function toNumber (line 13911) | function toNumber(value) {
function toPlainObject (line 13956) | function toPlainObject(value) {
function toSafeInteger (line 13984) | function toSafeInteger(value) {
function toString (line 14011) | function toString(value) {
function create (line 14214) | function create(prototype, properties) {
function findKey (line 14330) | function findKey(object, predicate) {
function findLastKey (line 14369) | function findLastKey(object, predicate) {
function forIn (line 14401) | function forIn(object, iteratee) {
function forInRight (line 14433) | function forInRight(object, iteratee) {
function forOwn (line 14467) | function forOwn(object, iteratee) {
function forOwnRight (line 14497) | function forOwnRight(object, iteratee) {
function functions (line 14524) | function functions(object) {
function functionsIn (line 14551) | function functionsIn(object) {
function get (line 14580) | function get(object, path, defaultValue) {
function has (line 14612) | function has(object, path) {
function hasIn (line 14642) | function hasIn(object, path) {
function keys (line 14760) | function keys(object) {
function keysIn (line 14787) | function keysIn(object) {
function mapKeys (line 14812) | function mapKeys(object, iteratee) {
function mapValues (line 14850) | function mapValues(object, iteratee) {
function omitBy (line 14992) | function omitBy(object, predicate) {
function pickBy (line 15035) | function pickBy(object, predicate) {
function result (line 15077) | function result(object, path, defaultValue) {
function set (line 15127) | function set(object, path, value) {
function setWith (line 15155) | function setWith(object, path, value, customizer) {
function transform (line 15242) | function transform(object, iteratee, accumulator) {
function unset (line 15292) | function unset(object, path) {
function update (line 15323) | function update(object, path, updater) {
function updateWith (line 15351) | function updateWith(object, path, updater, customizer) {
function values (line 15382) | function values(object) {
function valuesIn (line 15410) | function valuesIn(object) {
function clamp (line 15435) | function clamp(number, lower, upper) {
function inRange (line 15489) | function inRange(number, start, end) {
function random (line 15532) | function random(lower, upper, floating) {
function capitalize (line 15613) | function capitalize(string) {
function deburr (line 15635) | function deburr(string) {
function endsWith (line 15663) | function endsWith(string, target, position) {
function escape (line 15705) | function escape(string) {
function escapeRegExp (line 15727) | function escapeRegExp(string) {
function pad (line 15825) | function pad(string, length, chars) {
function padEnd (line 15864) | function padEnd(string, length, chars) {
function padStart (line 15897) | function padStart(string, length, chars) {
function parseInt (line 15931) | function parseInt(string, radix, guard) {
function repeat (line 15962) | function repeat(string, n, guard) {
function replace (line 15990) | function replace() {
function split (line 16041) | function split(string, separator, limit) {
function startsWith (line 16110) | function startsWith(string, target, position) {
function template (line 16224) | function template(string, options, guard) {
function toLower (line 16356) | function toLower(value) {
function toUpper (line 16381) | function toUpper(value) {
function trim (line 16407) | function trim(string, chars, guard) {
function trimEnd (line 16442) | function trimEnd(string, chars, guard) {
function trimStart (line 16475) | function trimStart(string, chars, guard) {
function truncate (line 16526) | function truncate(string, options) {
function unescape (line 16601) | function unescape(string) {
function words (line 16670) | function words(string, pattern, guard) {
function cond (line 16775) | function cond(pairs) {
function conforms (line 16821) | function conforms(source) {
function constant (line 16844) | function constant(value) {
function defaultTo (line 16870) | function defaultTo(value, defaultValue) {
function identity (line 16937) | function identity(value) {
function iteratee (line 16983) | function iteratee(func) {
function matches (line 17022) | function matches(source) {
function matchesProperty (line 17059) | function matchesProperty(path, srcValue) {
function mixin (line 17158) | function mixin(object, source, options) {
function noConflict (line 17207) | function noConflict() {
function noop (line 17226) | function noop() {
function nthArg (line 17250) | function nthArg(n) {
function property (line 17362) | function property(path) {
function propertyOf (line 17387) | function propertyOf(object) {
function stubArray (line 17492) | function stubArray() {
function stubFalse (line 17509) | function stubFalse() {
function stubObject (line 17531) | function stubObject() {
function stubString (line 17548) | function stubString() {
function stubTrue (line 17565) | function stubTrue() {
function times (line 17588) | function times(n, iteratee) {
function toPath (line 17623) | function toPath(value) {
function uniqueId (line 17647) | function uniqueId(prefix) {
function max (line 17756) | function max(array) {
function maxBy (line 17785) | function maxBy(array, iteratee) {
function mean (line 17805) | function mean(array) {
function meanBy (line 17832) | function meanBy(array, iteratee) {
function min (line 17854) | function min(array) {
function minBy (line 17883) | function minBy(array, iteratee) {
function sum (line 17964) | function sum(array) {
function sumBy (line 17993) | function sumBy(array, iteratee) {
function drainQueue (line 18599) | function drainQueue() {
function noop (line 18631) | function noop() {}
function isHex (line 18701) | function isHex(str) {
function isHexDigit (line 18705) | function isHexDigit(str) {
function asyncTrigger (line 18712) | function asyncTrigger() {
function Lexer (line 18756) | function Lexer(source) {
function commentToken (line 19076) | function commentToken(label, body, opt) {
function isNonAsciiIdentifierStart (line 19275) | function isNonAsciiIdentifierStart(code) {
function isNonAsciiIdentifierPart (line 19279) | function isNonAsciiIdentifierPart(code) {
function removeEscapeSequences (line 19361) | function removeEscapeSequences(id) {
function isDecimalDigit (line 19430) | function isDecimalDigit(str) {
function isOctalDigit (line 19434) | function isOctalDigit(str) {
function isNonOctalDigit (line 19438) | function isNonOctalDigit(str) {
function isBinaryDigit (line 19442) | function isBinaryDigit(str) {
function isIdentifierStart (line 19446) | function isIdentifierStart(ch) {
function NameStack (line 21260) | function NameStack() {
function _newScope (line 22609) | function _newScope(type) {
function warning (line 22632) | function warning(code, token) {
function error (line 22640) | function error(code, token) {
function _setupUsages (line 22648) | function _setupUsages(bindingName) {
function _checkForUnused (line 22702) | function _checkForUnused() {
function _getBinding (line 22754) | function _getBinding(bindingName) {
function usedSoFarInCurrentFunction (line 22771) | function usedSoFarInCurrentFunction(bindingName) {
function _checkOuterShadow (line 22784) | function _checkOuterShadow(bindingName, token) {
function _latedefWarning (line 22810) | function _latedefWarning(type, bindingName, token) {
function checkOption (line 25409) | function checkOption(name, isStable, t) {
function isString (line 25436) | function isString(obj) {
function isIdentifier (line 25440) | function isIdentifier(tkn, value) {
function isReserved (line 25467) | function isReserved(context, token) {
function supplant (line 25506) | function supplant(str, data) {
function combine (line 25513) | function combine(dest, src) {
function processenforceall (line 25520) | function processenforceall() {
function applyOptions (line 25539) | function applyOptions() {
function quit (line 25675) | function quit(code, token, a, b) {
function removeIgnoredMessages (line 25696) | function removeIgnoredMessages() {
function warning (line 25703) | function warning(code, t, a, b, c, d) {
function warningAt (line 25751) | function warningAt(m, l, ch, a, b, c, d) {
function error (line 25758) | function error(m, t, a, b, c, d) {
function errorAt (line 25762) | function errorAt(m, l, ch, a, b, c, d) {
function addEvalCode (line 25770) | function addEvalCode(elem, token) {
function lintingDirective (line 25785) | function lintingDirective(directiveToken, previous) {
function peek (line 26104) | function peek(p) {
function peekIgnoreEOL (line 26135) | function peekIgnoreEOL() {
function advance (line 26157) | function advance(expected, relatedToken) {
function isOperator (line 26207) | function isOperator(token) {
function isEndOfExpr (line 26211) | function isEndOfExpr(context, curr, next) {
function expression (line 26263) | function expression(context, rbp) {
function sameLine (line 26341) | function sameLine(first, second) {
function nobreaknonadjacent (line 26345) | function nobreaknonadjacent(left, right) {
function nolinebreak (line 26351) | function nolinebreak(t) {
function checkComma (line 26372) | function checkComma(opts) {
function symbol (line 26452) | function symbol(s, p) {
function delim (line 26479) | function delim(s) {
function stmt (line 26495) | function stmt(s, f) {
function blockstmt (line 26515) | function blockstmt(s, f) {
function reserveName (line 26527) | function reserveName(x) {
function prefix (line 26546) | function prefix(s, f) {
function type (line 26581) | function type(s, f) {
function reserve (line 26601) | function reserve(name, func) {
function FutureReservedWord (line 26624) | function FutureReservedWord(name, meta) {
function infix (line 26652) | function infix(s, f, p, w) {
function application (line 26683) | function application(s) {
function relation (line 26707) | function relation(s, f) {
function beginsUnaryExpression (line 26746) | function beginsUnaryExpression(token) {
function isTypoTypeof (line 26782) | function isTypoTypeof(left, right, state) {
function isGlobalEval (line 26816) | function isGlobalEval(left, state) {
function findNativePrototype (line 26844) | function findNativePrototype(left) {
function checkLeftSideAssign (line 26889) | function checkLeftSideAssign(context, left, assignToken, options) {
function assignop (line 26964) | function assignop(s, f) {
function bitwise (line 26993) | function bitwise(s, f, p) {
function bitwiseassignop (line 27017) | function bitwiseassignop(s) {
function suffix (line 27041) | function suffix(s) {
function optionalidentifier (line 27071) | function optionalidentifier(context, isName, preserve) {
function spreadrest (line 27099) | function spreadrest(operation) {
function identifier (line 27129) | function identifier(context, isName) {
function reachable (line 27153) | function reachable(controlToken) {
function parseFinalSemicolon (line 27187) | function parseFinalSemicolon(stmt) {
function statement (line 27220) | function statement(context) {
function statements (line 27320) | function statements(context) {
function directives (line 27343) | function directives() {
function block (line 27397) | function block(context, ordinary, stmt, isfunc, isfatarrow, iscase) {
function countMember (line 27552) | function countMember(m) {
function classBody (line 28187) | function classBody(classToken, context) {
function doMethod (line 28316) | function doMethod(classToken, context, name, generator) {
function isTypicalCallExpression (line 28419) | function isTypicalCallExpression(token) {
function peekThroughParens (line 28525) | function peekThroughParens(parens) {
function comprehensiveArrayExpression (line 28720) | function comprehensiveArrayExpression(context) {
function isMethod (line 28844) | function isMethod() {
function propertyName (line 28857) | function propertyName(context) {
function functionparams (line 28888) | function functionparams(context, options) {
function functor (line 29007) | function functor(name, token, overwrites) {
function hasParsedCode (line 29059) | function hasParsedCode(funct) {
function doTemplateLiteral (line 29067) | function doTemplateLiteral(context, leftOrRbp) {
function doFunction (line 29126) | function doFunction(context, options) {
function createMetrics (line 29253) | function createMetrics(functionStartToken) {
function increaseComplexityCount (line 29292) | function increaseComplexityCount() {
function checkCondAssignment (line 29299) | function checkCondAssignment(token) {
function checkProperties (line 29332) | function checkProperties(props) {
function metaProperty (line 29344) | function metaProperty(context, name, c) {
function destructuringPattern (line 29533) | function destructuringPattern(context, options) {
function destructuringPatternRecursive (line 29546) | function destructuringPatternRecursive(context, options) {
function destructuringPatternMatch (line 29711) | function destructuringPatternMatch(tokens, value) {
function blockVariableStatement (line 29729) | function blockVariableStatement(type, statement, context) {
function isMozillaLet (line 29873) | function isMozillaLet() {
function catchParameter (line 30167) | function catchParameter() {
function supportsSuper (line 31229) | function supportsSuper(type, funct) {
function saveProperty (line 31354) | function saveProperty(props, name, tkn, isClass, isStatic, isComputed) {
function saveAccessor (line 31388) | function saveAccessor(accessorType, props, name, tkn, isClass, isStatic) {
function computedPropertyName (line 31433) | function computedPropertyName(context) {
function checkPunctuators (line 31460) | function checkPunctuators(token, values) {
function checkPunctuator (line 31478) | function checkPunctuator(token, value) {
function destructuringAssignOrJsonValue (line 31483) | function destructuringAssignOrJsonValue(context) {
function declare (line 31521) | function declare(v) {
function use (line 31531) | function use(v) {
function jsonValue (line 31615) | function jsonValue() {
function lintEvalCode (line 31707) | function lintEvalCode(internals, options, globals) {
function each (line 31753) | function each(obj, cb) {
method isJSON (line 31836) | get isJSON() {
FILE: web/assets/codemirror/jsonlint.js
function o (line 1) | function o(a){d.length=d.length-2*a,e.length=e.length-a,f.length=f.lengt...
function p (line 1) | function p(){var a;return a=c.lexer.lex()||1,typeof a!="number"&&(a=c.sy...
FILE: web/assets/codemirror/lint/javascript-lint.js
function validator (line 17) | function validator(text, options) {
function parseErrors (line 34) | function parseErrors(errors, output) {
FILE: web/assets/codemirror/lint/lint.js
function showTooltip (line 16) | function showTooltip(cm, e, content) {
function rm (line 37) | function rm(elt) {
function hideTooltip (line 40) | function hideTooltip(tt) {
function showTooltipFor (line 47) | function showTooltipFor(cm, e, content, node) {
function LintState (line 64) | function LintState(cm, conf, hasGutter) {
function clearMarks (line 96) | function clearMarks(cm) {
function clearErrorLines (line 105) | function clearErrorLines(cm) {
function makeMarker (line 112) | function makeMarker(cm, labels, severity, multiple, tooltips) {
function getMaxSeverity (line 127) | function getMaxSeverity(a, b) {
function groupByLine (line 132) | function groupByLine(annotations) {
function annotationTooltip (line 141) | function annotationTooltip(ann) {
function lintAsync (line 154) | function lintAsync(cm, getAnnotations) {
function startLinting (line 170) | function startLinting(cm) {
function updateLinting (line 192) | function updateLinting(cm, annotationsNotSorted) {
function onChange (line 231) | function onChange(cm) {
function popupTooltips (line 238) | function popupTooltips(cm, annotations, e) {
function onMouseOver (line 248) | function onMouseOver(cm, e) {
FILE: web/assets/js/model/dbinbound.js
class DBInbound (line 1) | class DBInbound {
method constructor (line 3) | constructor(data) {
method totalGB (line 30) | get totalGB() {
method totalGB (line 34) | set totalGB(gb) {
method isVMess (line 38) | get isVMess() {
method isVLess (line 42) | get isVLess() {
method isTrojan (line 46) | get isTrojan() {
method isSS (line 50) | get isSS() {
method isMixed (line 54) | get isMixed() {
method isHTTP (line 58) | get isHTTP() {
method isWireguard (line 62) | get isWireguard() {
method address (line 66) | get address() {
method _expiryTime (line 74) | get _expiryTime() {
method _expiryTime (line 81) | set _expiryTime(t) {
method isExpiry (line 89) | get isExpiry() {
method toInbound (line 93) | toInbound() {
method isMultiUser (line 122) | isMultiUser() {
method hasLink (line 135) | hasLink() {
method genInboundLinks (line 147) | genInboundLinks(remarkModel) {
FILE: web/assets/js/model/inbound.js
constant TLS_FLOW_CONTROL (line 23) | const TLS_FLOW_CONTROL = {
constant TLS_VERSION_OPTION (line 28) | const TLS_VERSION_OPTION = {
constant TLS_CIPHER_OPTION (line 35) | const TLS_CIPHER_OPTION = {
constant UTLS_FINGERPRINT (line 51) | const UTLS_FINGERPRINT = {
constant ALPN_OPTION (line 66) | const ALPN_OPTION = {
constant SNIFFING_OPTION (line 72) | const SNIFFING_OPTION = {
constant USAGE_OPTION (line 79) | const USAGE_OPTION = {
constant DOMAIN_STRATEGY_OPTION (line 85) | const DOMAIN_STRATEGY_OPTION = {
constant TCP_CONGESTION_OPTION (line 99) | const TCP_CONGESTION_OPTION = {
constant USERS_SECURITY (line 105) | const USERS_SECURITY = {
constant MODE_OPTION (line 113) | const MODE_OPTION = {
class XrayCommonClass (line 134) | class XrayCommonClass {
method toJsonArray (line 136) | static toJsonArray(arr) {
method fromJson (line 140) | static fromJson() {
method toJson (line 144) | toJson() {
method toString (line 148) | toString(format = true) {
method toHeaders (line 152) | static toHeaders(v2Headers) {
method toV2Headers (line 169) | static toV2Headers(headers, arr = true) {
class TcpStreamSettings (line 191) | class TcpStreamSettings extends XrayCommonClass {
method constructor (line 192) | constructor(
method fromJson (line 205) | static fromJson(json = {}) {
method toJson (line 217) | toJson() {
method constructor (line 230) | constructor(
method addPath (line 243) | addPath(path) {
method removePath (line 247) | removePath(index) {
method addHeader (line 251) | addHeader(name, value) {
method removeHeader (line 255) | removeHeader(index) {
method fromJson (line 259) | static fromJson(json = {}) {
method toJson (line 268) | toJson() {
method constructor (line 279) | constructor(
method addHeader (line 292) | addHeader(name, value) {
method removeHeader (line 296) | removeHeader(index) {
method fromJson (line 300) | static fromJson(json = {}) {
method toJson (line 309) | toJson() {
class KcpStreamSettings (line 319) | class KcpStreamSettings extends XrayCommonClass {
method constructor (line 320) | constructor(
method fromJson (line 339) | static fromJson(json = {}) {
method toJson (line 351) | toJson() {
class WsStreamSettings (line 364) | class WsStreamSettings extends XrayCommonClass {
method constructor (line 365) | constructor(
method addHeader (line 380) | addHeader(name, value) {
method removeHeader (line 384) | removeHeader(index) {
method fromJson (line 388) | static fromJson(json = {}) {
method toJson (line 398) | toJson() {
class GrpcStreamSettings (line 409) | class GrpcStreamSettings extends XrayCommonClass {
method constructor (line 410) | constructor(
method fromJson (line 421) | static fromJson(json = {}) {
method toJson (line 429) | toJson() {
class HTTPUpgradeStreamSettings (line 438) | class HTTPUpgradeStreamSettings extends XrayCommonClass {
method constructor (line 439) | constructor(
method addHeader (line 452) | addHeader(name, value) {
method removeHeader (line 456) | removeHeader(index) {
method fromJson (line 460) | static fromJson(json = {}) {
method toJson (line 469) | toJson() {
class xHTTPStreamSettings (line 479) | class xHTTPStreamSettings extends XrayCommonClass {
method constructor (line 480) | constructor(
method addHeader (line 529) | addHeader(name, value) {
method removeHeader (line 533) | removeHeader(index) {
method fromJson (line 537) | static fromJson(json = {}) {
method toJson (line 564) | toJson() {
class TlsStreamSettings (line 592) | class TlsStreamSettings extends XrayCommonClass {
method constructor (line 593) | constructor(
method addCert (line 622) | addCert() {
method removeCert (line 626) | removeCert(index) {
method fromJson (line 630) | static fromJson(json = {}) {
method toJson (line 656) | toJson() {
method constructor (line 675) | constructor(
method fromJson (line 696) | static fromJson(json = {}) {
method toJson (line 718) | toJson() {
method constructor (line 740) | constructor(
method fromJson (line 748) | static fromJson(json = {}) {
method toJson (line 754) | toJson() {
class RealityStreamSettings (line 763) | class RealityStreamSettings extends XrayCommonClass {
method constructor (line 764) | constructor(
method fromJson (line 799) | static fromJson(json = {}) {
method toJson (line 825) | toJson() {
method constructor (line 843) | constructor(
method fromJson (line 857) | static fromJson(json = {}) {
method toJson (line 866) | toJson() {
class SockoptStreamSettings (line 877) | class SockoptStreamSettings extends XrayCommonClass {
method constructor (line 878) | constructor(
method fromJson (line 917) | static fromJson(json = {}) {
method toJson (line 940) | toJson() {
class UdpMask (line 966) | class UdpMask extends XrayCommonClass {
method constructor (line 967) | constructor(type = 'salamander', settings = {}) {
method _getDefaultSettings (line 973) | _getDefaultSettings(type, settings = {}) {
method fromJson (line 995) | static fromJson(json = {}) {
method toJson (line 1002) | toJson() {
class FinalMaskStreamSettings (line 1010) | class FinalMaskStreamSettings extends XrayCommonClass {
method constructor (line 1011) | constructor(udp = []) {
method fromJson (line 1016) | static fromJson(json = {}) {
method toJson (line 1020) | toJson() {
class StreamSettings (line 1028) | class StreamSettings extends XrayCommonClass {
method constructor (line 1029) | constructor(network = 'tcp',
method addUdpMask (line 1059) | addUdpMask(type = 'salamander') {
method delUdpMask (line 1063) | delUdpMask(index) {
method hasFinalMask (line 1069) | get hasFinalMask() {
method isTls (line 1073) | get isTls() {
method isTls (line 1077) | set isTls(isTls) {
method isReality (line 1086) | get isReality() {
method isReality (line 1090) | set isReality(isReality) {
method sockoptSwitch (line 1098) | get sockoptSwitch() {
method sockoptSwitch (line 1102) | set sockoptSwitch(value) {
method fromJson (line 1106) | static fromJson(json = {}) {
method toJson (line 1124) | toJson() {
class Sniffing (line 1144) | class Sniffing extends XrayCommonClass {
method constructor (line 1145) | constructor(
method fromJson (line 1157) | static fromJson(json = {}) {
class Inbound (line 1173) | class Inbound extends XrayCommonClass {
method constructor (line 1174) | constructor(
method getClientStats (line 1194) | getClientStats() {
method clients (line 1198) | get clients() {
method protocol (line 1208) | get protocol() {
method protocol (line 1212) | set protocol(protocol) {
method network (line 1220) | get network() {
method network (line 1224) | set network(network) {
method isTcp (line 1228) | get isTcp() {
method isWs (line 1232) | get isWs() {
method isKcp (line 1236) | get isKcp() {
method isGrpc (line 1240) | get isGrpc() {
method isHttpupgrade (line 1244) | get isHttpupgrade() {
method isXHTTP (line 1248) | get isXHTTP() {
method method (line 1253) | get method() {
method isSSMultiUser (line 1261) | get isSSMultiUser() {
method isSS2022 (line 1264) | get isSS2022() {
method serverName (line 1268) | get serverName() {
method getHeader (line 1274) | getHeader(obj, name) {
method host (line 1283) | get host() {
method path (line 1296) | get path() {
method serviceName (line 1309) | get serviceName() {
method isExpiry (line 1313) | isExpiry(index) {
method canEnableTls (line 1318) | canEnableTls() {
method canEnableTlsFlow (line 1324) | canEnableTlsFlow() {
method canEnableVisionSeed (line 1332) | canEnableVisionSeed() {
method canEnableReality (line 1339) | canEnableReality() {
method canEnableStream (line 1344) | canEnableStream() {
method reset (line 1348) | reset() {
method genVmessLink (line 1358) | genVmessLink(address = '', port = this.port, forceTls, remark = '', cl...
method genVLESSLink (line 1421) | genVLESSLink(address = '', port = this.port, forceTls, remark = '', cl...
method genSSLink (line 1522) | genSSLink(address = '', port = this.port, forceTls, remark = '', clien...
method genTrojanLink (line 1599) | genTrojanLink(address = '', port = this.port, forceTls, remark = '', c...
method getWireguardLink (line 1692) | getWireguardLink(address, port, remark, peerId) {
method genLink (line 1714) | genLink(address = '', port = this.port, forceTls = 'same', remark = ''...
method genAllLinks (line 1728) | genAllLinks(remark = '', remarkModel = '-ieo', client) {
method genInboundLinks (line 1759) | genInboundLinks(remark = '', remarkModel = '-ieo') {
method fromJson (line 1782) | static fromJson(json = {}) {
method toJson (line 1795) | toJson() {
method constructor (line 1814) | constructor(protocol) {
method getSettings (line 1819) | static getSettings(protocol) {
method fromJson (line 1834) | static fromJson(protocol, json) {
method toJson (line 1849) | toJson() {
method constructor (line 1855) | constructor(protocol,
method indexOfVmessById (line 1861) | indexOfVmessById(id) {
method addVmess (line 1865) | addVmess(VMESS) {
method delVmess (line 1872) | delVmess(VMESS) {
method fromJson (line 1879) | static fromJson(json = {}) {
method toJson (line 1886) | toJson() {
method constructor (line 1894) | constructor(
method fromJson (line 1925) | static fromJson(json = {}) {
method _expiryTime (line 1942) | get _expiryTime() {
method _expiryTime (line 1952) | set _expiryTime(t) {
method _totalGB (line 1959) | get _totalGB() {
method _totalGB (line 1963) | set _totalGB(gb) {
method constructor (line 1970) | constructor(
method addFallback (line 1988) | addFallback() {
method delFallback (line 1992) | delFallback(index) {
method fromJson (line 1996) | static fromJson(json = {}) {
method toJson (line 2016) | toJson() {
method constructor (line 2049) | constructor(
method fromJson (line 2080) | static fromJson(json = {}) {
method _expiryTime (line 2098) | get _expiryTime() {
method _expiryTime (line 2108) | set _expiryTime(t) {
method _totalGB (line 2115) | get _totalGB() {
method _totalGB (line 2119) | set _totalGB(gb) {
method constructor (line 2124) | constructor(name = "", alpn = '', path = '', dest = '', xver = 0) {
method toJson (line 2133) | toJson() {
method fromJson (line 2147) | static fromJson(json = []) {
method constructor (line 2163) | constructor(protocol,
method addFallback (line 2171) | addFallback() {
method delFallback (line 2175) | delFallback(index) {
method fromJson (line 2179) | static fromJson(json = {}) {
method toJson (line 2186) | toJson() {
method constructor (line 2195) | constructor(
method toJson (line 2224) | toJson() {
method fromJson (line 2241) | static fromJson(json = {}) {
method _expiryTime (line 2258) | get _expiryTime() {
method _expiryTime (line 2268) | set _expiryTime(t) {
method _totalGB (line 2275) | get _totalGB() {
method _totalGB (line 2279) | set _totalGB(gb) {
method constructor (line 2286) | constructor(name = "", alpn = '', path = '', dest = '', xver = 0) {
method toJson (line 2295) | toJson() {
method fromJson (line 2309) | static fromJson(json = []) {
method constructor (line 2325) | constructor(protocol,
method fromJson (line 2340) | static fromJson(json = {}) {
method toJson (line 2351) | toJson() {
method constructor (line 2363) | constructor(
method toJson (line 2394) | toJson() {
method fromJson (line 2412) | static fromJson(json = {}) {
method _expiryTime (line 2430) | get _expiryTime() {
method _expiryTime (line 2440) | set _expiryTime(t) {
method _totalGB (line 2447) | get _totalGB() {
method _totalGB (line 2451) | set _totalGB(gb) {
method constructor (line 2458) | constructor(
method fromJson (line 2474) | static fromJson(json = {}) {
method toJson (line 2485) | toJson() {
method constructor (line 2497) | constructor(protocol, auth = 'password', accounts = [new Inbound.MixedSe...
method addAccount (line 2505) | addAccount(account) {
method delAccount (line 2509) | delAccount(index) {
method fromJson (line 2513) | static fromJson(json = {}) {
method toJson (line 2529) | toJson() {
method constructor (line 2539) | constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq...
method fromJson (line 2545) | static fromJson(json = {}) {
method constructor (line 2551) | constructor(
method addAccount (line 2561) | addAccount(account) {
method delAccount (line 2565) | delAccount(index) {
method fromJson (line 2569) | static fromJson(json = {}) {
method toJson (line 2577) | toJson() {
method constructor (line 2586) | constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq...
method fromJson (line 2592) | static fromJson(json = {}) {
method constructor (line 2598) | constructor(
method addPeer (line 2613) | addPeer() {
method delPeer (line 2617) | delPeer(index) {
method fromJson (line 2621) | static fromJson(json = {}) {
method toJson (line 2631) | toJson() {
method constructor (line 2642) | constructor(privateKey, publicKey, psk = '', allowedIPs = ['10.0.0.2/32'...
method fromJson (line 2657) | static fromJson(json = {}) {
method toJson (line 2667) | toJson() {
method constructor (line 2682) | constructor(
method fromJson (line 2694) | static fromJson(json = {}) {
method toJson (line 2703) | toJson() {
FILE: web/assets/js/model/outbound.js
constant TLS_FLOW_CONTROL (line 27) | const TLS_FLOW_CONTROL = {
constant UTLS_FINGERPRINT (line 32) | const UTLS_FINGERPRINT = {
constant ALPN_OPTION (line 47) | const ALPN_OPTION = {
constant USERS_SECURITY (line 75) | const USERS_SECURITY = {
constant MODE_OPTION (line 83) | const MODE_OPTION = {
class CommonClass (line 111) | class CommonClass {
method toJsonArray (line 113) | static toJsonArray(arr) {
method fromJson (line 117) | static fromJson() {
method toJson (line 121) | toJson() {
method toString (line 125) | toString(format = true) {
class TcpStreamSettings (line 130) | class TcpStreamSettings extends CommonClass {
method constructor (line 131) | constructor(type = 'none', host, path) {
method fromJson (line 138) | static fromJson(json = {}) {
method toJson (line 151) | toJson() {
class KcpStreamSettings (line 166) | class KcpStreamSettings extends CommonClass {
method constructor (line 167) | constructor(
method fromJson (line 186) | static fromJson(json = {}) {
method toJson (line 198) | toJson() {
class WsStreamSettings (line 211) | class WsStreamSettings extends CommonClass {
method constructor (line 212) | constructor(
method fromJson (line 224) | static fromJson(json = {}) {
method toJson (line 232) | toJson() {
class GrpcStreamSettings (line 241) | class GrpcStreamSettings extends CommonClass {
method constructor (line 242) | constructor(
method fromJson (line 253) | static fromJson(json = {}) {
method toJson (line 257) | toJson() {
class HttpUpgradeStreamSettings (line 266) | class HttpUpgradeStreamSettings extends CommonClass {
method constructor (line 267) | constructor(path = '/', host = '') {
method fromJson (line 273) | static fromJson(json = {}) {
method toJson (line 280) | toJson() {
class xHTTPStreamSettings (line 288) | class xHTTPStreamSettings extends CommonClass {
method constructor (line 289) | constructor(
method fromJson (line 313) | static fromJson(json = {}) {
method toJson (line 324) | toJson() {
class TlsStreamSettings (line 343) | class TlsStreamSettings extends CommonClass {
method constructor (line 344) | constructor(
method fromJson (line 361) | static fromJson(json = {}) {
method toJson (line 372) | toJson() {
class RealityStreamSettings (line 384) | class RealityStreamSettings extends CommonClass {
method constructor (line 385) | constructor(
method fromJson (line 401) | static fromJson(json = {}) {
method toJson (line 411) | toJson() {
class HysteriaStreamSettings (line 423) | class HysteriaStreamSettings extends CommonClass {
method constructor (line 424) | constructor(
method fromJson (line 459) | static fromJson(json = {}) {
method toJson (line 493) | toJson() {
class SockoptStreamSettings (line 518) | class SockoptStreamSettings extends CommonClass {
method constructor (line 519) | constructor(
method fromJson (line 538) | static fromJson(json = {}) {
method toJson (line 551) | toJson() {
class UdpMask (line 567) | class UdpMask extends CommonClass {
method constructor (line 568) | constructor(type = 'salamander', settings = {}) {
method _getDefaultSettings (line 574) | _getDefaultSettings(type, settings = {}) {
method fromJson (line 594) | static fromJson(json = {}) {
method toJson (line 601) | toJson() {
class FinalMaskStreamSettings (line 609) | class FinalMaskStreamSettings extends CommonClass {
method constructor (line 610) | constructor(udp = []) {
method fromJson (line 615) | static fromJson(json = {}) {
method toJson (line 619) | toJson() {
class StreamSettings (line 627) | class StreamSettings extends CommonClass {
method constructor (line 628) | constructor(
method addUdpMask (line 659) | addUdpMask(type = 'salamander') {
method delUdpMask (line 663) | delUdpMask(index) {
method hasFinalMask (line 669) | get hasFinalMask() {
method isTls (line 673) | get isTls() {
method isReality (line 677) | get isReality() {
method sockoptSwitch (line 681) | get sockoptSwitch() {
method sockoptSwitch (line 685) | set sockoptSwitch(value) {
method fromJson (line 689) | static fromJson(json = {}) {
method toJson (line 707) | toJson() {
class Mux (line 727) | class Mux extends CommonClass {
method constructor (line 728) | constructor(enabled = false, concurrency = 8, xudpConcurrency = 16, xu...
method fromJson (line 736) | static fromJson(json = {}) {
method toJson (line 746) | toJson() {
class Outbound (line 756) | class Outbound extends CommonClass {
method constructor (line 757) | constructor(
method protocol (line 774) | get protocol() {
method protocol (line 778) | set protocol(protocol) {
method canEnableTls (line 784) | canEnableTls() {
method canEnableTlsFlow (line 791) | canEnableTlsFlow() {
method canEnableVisionSeed (line 799) | canEnableVisionSeed() {
method canEnableReality (line 805) | canEnableReality() {
method canEnableStream (line 810) | canEnableStream() {
method canEnableMux (line 814) | canEnableMux() {
method hasServers (line 838) | hasServers() {
method hasAddressPort (line 842) | hasAddressPort() {
method hasUsername (line 855) | hasUsername() {
method fromJson (line 859) | static fromJson(json = {}) {
method toJson (line 870) | toJson() {
method fromLink (line 890) | static fromLink(link) {
method fromVmessLink (line 908) | static fromVmessLink(json = {}) {
method fromParamLink (line 943) | static fromParamLink(link) {
method fromHysteriaLink (line 1022) | static fromHysteriaLink(link) {
method constructor (line 1088) | constructor(protocol) {
method getSettings (line 1093) | static getSettings(protocol) {
method fromJson (line 1110) | static fromJson(protocol, json) {
method toJson (line 1127) | toJson() {
method constructor (line 1132) | constructor(
method addNoise (line 1145) | addNoise() {
method delNoise (line 1149) | delNoise(index) {
method fromJson (line 1153) | static fromJson(json = {}) {
method toJson (line 1162) | toJson() {
method constructor (line 1173) | constructor(
method fromJson (line 1186) | static fromJson(json = {}) {
method constructor (line 1197) | constructor(
method fromJson (line 1210) | static fromJson(json = {}) {
method toJson (line 1219) | toJson() {
method constructor (line 1230) | constructor(type) {
method fromJson (line 1235) | static fromJson(json = {}) {
method toJson (line 1241) | toJson() {
method constructor (line 1248) | constructor(
method fromJson (line 1263) | static fromJson(json = {}) {
method constructor (line 1274) | constructor(address, port, id, security) {
method fromJson (line 1282) | static fromJson(json = {}) {
method toJson (line 1295) | toJson() {
method constructor (line 1309) | constructor(address, port, id, flow, encryption, testpre = 0, testseed =...
method fromJson (line 1320) | static fromJson(json = {}) {
method toJson (line 1333) | toJson() {
method constructor (line 1354) | constructor(address, port, password) {
method fromJson (line 1361) | static fromJson(json = {}) {
method toJson (line 1370) | toJson() {
method constructor (line 1381) | constructor(address, port, password, method, uot, UoTVersion) {
method fromJson (line 1391) | static fromJson(json = {}) {
method toJson (line 1404) | toJson() {
method constructor (line 1419) | constructor(address, port, user, pass) {
method fromJson (line 1427) | static fromJson(json = {}) {
method toJson (line 1438) | toJson() {
method constructor (line 1449) | constructor(address, port, user, pass) {
method fromJson (line 1457) | static fromJson(json = {}) {
method toJson (line 1468) | toJson() {
method constructor (line 1480) | constructor(
method addPeer (line 1502) | addPeer() {
method delPeer (line 1506) | delPeer(index) {
method fromJson (line 1510) | static fromJson(json = {}) {
method toJson (line 1523) | toJson() {
method constructor (line 1538) | constructor(
method fromJson (line 1553) | static fromJson(json = {}) {
method toJson (line 1563) | toJson() {
method constructor (line 1575) | constructor(address = '', port = 443, version = 2) {
method fromJson (line 1582) | static fromJson(json = {}) {
method toJson (line 1591) | toJson() {
FILE: web/assets/js/model/reality_targets.js
constant REALITY_TARGETS (line 2) | const REALITY_TARGETS = [
function getRandomRealityTarget (line 19) | function getRandomRealityTarget() {
FILE: web/assets/js/model/setting.js
class AllSetting (line 1) | class AllSetting {
method constructor (line 3) | constructor(data) {
method equals (line 86) | equals(other) {
FILE: web/assets/js/subscription.js
function renderLink (line 30) | function renderLink(item) {
function copy (line 41) | function copy(text) {
function open (line 48) | function open(url) {
function drawQR (line 52) | function drawQR(value) {
function linkName (line 61) | function linkName(link, idx) {
method mounted (line 97) | async mounted() {
method beforeDestroy (line 112) | beforeDestroy() {
method isMobile (line 116) | isMobile() {
method isUnlimited (line 119) | isUnlimited() {
method isActive (line 122) | isActive() {
method shadowrocketUrl (line 128) | shadowrocketUrl() {
method v2boxUrl (line 134) | v2boxUrl() {
method streisandUrl (line 137) | streisandUrl() {
method v2raytunUrl (line 140) | v2raytunUrl() {
method npvtunUrl (line 143) | npvtunUrl() {
method happUrl (line 146) | happUrl() {
method i18nLabel (line 155) | i18nLabel(key) {
FILE: web/assets/js/util/index.js
class Msg (line 1) | class Msg {
method constructor (line 2) | constructor(success = false, msg = "", obj = null) {
class HttpUtil (line 9) | class HttpUtil {
method _handleMsg (line 10) | static _handleMsg(msg) {
method _respToMsg (line 18) | static _respToMsg(resp) {
method get (line 32) | static async get(url, params, options = {}) {
method post (line 46) | static async post(url, data, options = {}) {
method postWithModal (line 60) | static async postWithModal(url, data, modal) {
class PromiseUtil (line 75) | class PromiseUtil {
method sleep (line 76) | static async sleep(timeout) {
class RandomUtil (line 83) | class RandomUtil {
method getSeq (line 84) | static getSeq({ type = "default", hasNumbers = true, hasLowercase = tr...
method randomInteger (line 101) | static randomInteger(min, max) {
method randomSeq (line 108) | static randomSeq(count, options = {}) {
method randomShortIds (line 116) | static randomShortIds() {
method randomLowerAndNum (line 122) | static randomLowerAndNum(len) {
method randomUUID (line 126) | static randomUUID() {
method randomShadowsocksPassword (line 141) | static randomShadowsocksPassword(method = SSMethods.BLAKE3_AES_256_GCM) {
method randomBase32String (line 155) | static randomBase32String(length = 16) {
class ObjectUtil (line 183) | class ObjectUtil {
method getPropIgnoreCase (line 184) | static getPropIgnoreCase(obj, prop) {
method deepSearch (line 196) | static deepSearch(obj, key) {
method isEmpty (line 218) | static isEmpty(obj) {
method isArrEmpty (line 222) | static isArrEmpty(arr) {
method copyArr (line 226) | static copyArr(dest, src) {
method clone (line 233) | static clone(obj) {
method deepClone (line 249) | static deepClone(obj) {
method cloneProps (line 267) | static cloneProps(dest, src, ...ignoreProps) {
method delProps (line 297) | static delProps(obj, ...props) {
method execute (line 305) | static execute(func, ...args) {
method orDefault (line 311) | static orDefault(obj, defaultValue) {
method equals (line 318) | static equals(a, b) {
class Wireguard (line 331) | class Wireguard {
method gf (line 332) | static gf(init) {
method pack (line 341) | static pack(o, n) {
method carry (line 365) | static carry(o) {
method cswap (line 373) | static cswap(p, q, b) {
method add (line 382) | static add(o, a, b) {
method subtract (line 387) | static subtract(o, a, b) {
method multmod (line 392) | static multmod(o, a, b) {
method invert (line 406) | static invert(o, i) {
method clamp (line 419) | static clamp(z) {
method generatePublicKey (line 424) | static generatePublicKey(privateKey) {
method generatePresharedKey (line 468) | static generatePresharedKey() {
method generatePrivateKey (line 474) | static generatePrivateKey() {
method encodeBase64 (line 480) | static encodeBase64(dest, src) {
method keyToBase64 (line 490) | static keyToBase64(key) {
method keyFromBase64 (line 499) | static keyFromBase64(encoded) {
method generateKeypair (line 508) | static generateKeypair(secretKey = '') {
class ClipboardManager (line 518) | class ClipboardManager {
method copyText (line 519) | static copyText(content = "") {
class Base64 (line 550) | class Base64 {
method encode (line 551) | static encode(content = "", safe = false) {
method alternativeEncode (line 564) | static alternativeEncode(content) {
method decode (line 570) | static decode(content = "") {
class SizeFormatter (line 578) | class SizeFormatter {
method sizeFormat (line 585) | static sizeFormat(size) {
class CPUFormatter (line 596) | class CPUFormatter {
method cpuSpeedFormat (line 597) | static cpuSpeedFormat(speed) {
method cpuCoreFormat (line 601) | static cpuCoreFormat(cores) {
class TimeFormatter (line 606) | class TimeFormatter {
method formatSecond (line 607) | static formatSecond(second) {
class NumberFormatter (line 617) | class NumberFormatter {
method addZero (line 618) | static addZero(num) {
method toFixed (line 622) | static toFixed(num, n) {
class Utils (line 628) | class Utils {
method debounce (line 629) | static debounce(fn, delay) {
class CookieManager (line 640) | class CookieManager {
method getCookie (line 641) | static getCookie(cname) {
method setCookie (line 653) | static setCookie(cname, cvalue, exdays) {
class ColorUtils (line 661) | class ColorUtils {
method usageColor (line 662) | static usageColor(data, threshold, total) {
method clientUsageColor (line 673) | static clientUsageColor(clientStats, trafficDiff) {
method userExpiryColor (line 682) | static userExpiryColor(threshold, client, isDark = false) {
class ArrayUtils (line 696) | class ArrayUtils {
method doAllItemsExist (line 697) | static doAllItemsExist(array1, array2) {
class URLBuilder (line 702) | class URLBuilder {
method buildURL (line 703) | static buildURL({ host, port, isTLS, base, path }) {
class LanguageManager (line 720) | class LanguageManager {
method getLanguage (line 789) | static getLanguage() {
method setLanguage (line 830) | static setLanguage(language) {
method isSupportLanguage (line 839) | static isSupportLanguage(language) {
method data (line 849) | data() {
method updateDeviceType (line 855) | updateDeviceType() {
method mounted (line 859) | mounted() {
method beforeDestroy (line 862) | beforeDestroy() {
class FileManager (line 867) | class FileManager {
method downloadTextFile (line 868) | static downloadTextFile(content, filename = 'file.txt', options = { ty...
class IntlUtil (line 887) | class IntlUtil {
method formatDate (line 888) | static formatDate(date) {
method formatRelativeTime (line 907) | static formatRelativeTime(date) {
FILE: web/assets/js/websocket.js
class WebSocketClient (line 4) | class WebSocketClient {
method constructor (line 5) | constructor(basePath = '') {
method connect (line 16) | connect() {
method handleMessage (line 88) | handleMessage(message) {
method on (line 98) | on(event, callback) {
method off (line 108) | off(event, callback) {
method emit (line 119) | emit(event, ...args) {
method disconnect (line 131) | disconnect() {
method send (line 139) | send(data) {
FILE: web/controller/api.go
type APIController (line 13) | type APIController struct
method checkAPIAuth (line 29) | func (a *APIController) checkAPIAuth(c *gin.Context) {
method initRouter (line 38) | func (a *APIController) initRouter(g *gin.RouterGroup) {
method BackuptoTgbot (line 56) | func (a *APIController) BackuptoTgbot(c *gin.Context) {
function NewAPIController (line 21) | func NewAPIController(g *gin.RouterGroup) *APIController {
FILE: web/controller/base.go
type BaseController (line 16) | type BaseController struct
method checkLogin (line 19) | func (a *BaseController) checkLogin(c *gin.Context) {
function I18nWeb (line 33) | func I18nWeb(c *gin.Context, name string, params ...string) string {
FILE: web/controller/inbound.go
type InboundController (line 18) | type InboundController struct
method initRouter (line 31) | func (a *InboundController) initRouter(g *gin.RouterGroup) {
method getInbounds (line 58) | func (a *InboundController) getInbounds(c *gin.Context) {
method getInbound (line 69) | func (a *InboundController) getInbound(c *gin.Context) {
method getClientTraffics (line 84) | func (a *InboundController) getClientTraffics(c *gin.Context) {
method getClientTrafficsById (line 95) | func (a *InboundController) getClientTrafficsById(c *gin.Context) {
method addInbound (line 106) | func (a *InboundController) addInbound(c *gin.Context) {
method delInbound (line 136) | func (a *InboundController) delInbound(c *gin.Context) {
method updateInbound (line 158) | func (a *InboundController) updateInbound(c *gin.Context) {
method getClientIps (line 188) | func (a *InboundController) getClientIps(c *gin.Context) {
method clearClientIps (line 232) | func (a *InboundController) clearClientIps(c *gin.Context) {
method addInboundClient (line 244) | func (a *InboundController) addInboundClient(c *gin.Context) {
method delInboundClient (line 264) | func (a *InboundController) delInboundClient(c *gin.Context) {
method updateInboundClient (line 284) | func (a *InboundController) updateInboundClient(c *gin.Context) {
method resetClientTraffic (line 306) | func (a *InboundController) resetClientTraffic(c *gin.Context) {
method resetAllTraffics (line 326) | func (a *InboundController) resetAllTraffics(c *gin.Context) {
method resetAllClientTraffics (line 338) | func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
method importInbound (line 356) | func (a *InboundController) importInbound(c *gin.Context) {
method delDepletedClients (line 386) | func (a *InboundController) delDepletedClients(c *gin.Context) {
method onlines (line 401) | func (a *InboundController) onlines(c *gin.Context) {
method lastOnline (line 406) | func (a *InboundController) lastOnline(c *gin.Context) {
method updateClientTraffic (line 412) | func (a *InboundController) updateClientTraffic(c *gin.Context) {
method delInboundClientByEmail (line 438) | func (a *InboundController) delInboundClientByEmail(c *gin.Context) {
function NewInboundController (line 24) | func NewInboundController(g *gin.RouterGroup) *InboundController {
FILE: web/controller/index.go
type LoginForm (line 18) | type LoginForm struct
type IndexController (line 25) | type IndexController struct
method initRouter (line 41) | func (a *IndexController) initRouter(g *gin.RouterGroup) {
method index (line 50) | func (a *IndexController) index(c *gin.Context) {
method login (line 59) | func (a *IndexController) login(c *gin.Context) {
method logout (line 115) | func (a *IndexController) logout(c *gin.Context) {
method getTwoFactorEnable (line 128) | func (a *IndexController) getTwoFactorEnable(c *gin.Context) {
function NewIndexController (line 34) | func NewIndexController(g *gin.RouterGroup) *IndexController {
FILE: web/controller/server.go
type ServerController (line 20) | type ServerController struct
method initRouter (line 41) | func (a *ServerController) initRouter(g *gin.RouterGroup) {
method refreshStatus (line 66) | func (a *ServerController) refreshStatus() {
method startTask (line 77) | func (a *ServerController) startTask() {
method status (line 88) | func (a *ServerController) status(c *gin.Context) { jsonObj(c, a.lastS...
method getCpuHistoryBucket (line 91) | func (a *ServerController) getCpuHistoryBucket(c *gin.Context) {
method getXrayVersion (line 115) | func (a *ServerController) getXrayVersion(c *gin.Context) {
method installXray (line 135) | func (a *ServerController) installXray(c *gin.Context) {
method updateGeofile (line 142) | func (a *ServerController) updateGeofile(c *gin.Context) {
method stopXrayService (line 157) | func (a *ServerController) stopXrayService(c *gin.Context) {
method restartXrayService (line 174) | func (a *ServerController) restartXrayService(c *gin.Context) {
method getLogs (line 191) | func (a *ServerController) getLogs(c *gin.Context) {
method getXrayLogs (line 200) | func (a *ServerController) getXrayLogs(c *gin.Context) {
method getConfigJson (line 245) | func (a *ServerController) getConfigJson(c *gin.Context) {
method getDb (line 255) | func (a *ServerController) getDb(c *gin.Context) {
method importDB (line 283) | func (a *ServerController) importDB(c *gin.Context) {
method getNewX25519Cert (line 304) | func (a *ServerController) getNewX25519Cert(c *gin.Context) {
method getNewmldsa65 (line 314) | func (a *ServerController) getNewmldsa65(c *gin.Context) {
method getNewEchCert (line 324) | func (a *ServerController) getNewEchCert(c *gin.Context) {
method getNewVlessEnc (line 335) | func (a *ServerController) getNewVlessEnc(c *gin.Context) {
method getNewUUID (line 345) | func (a *ServerController) getNewUUID(c *gin.Context) {
method getNewmlkem768 (line 356) | func (a *ServerController) getNewmlkem768(c *gin.Context) {
function NewServerController (line 33) | func NewServerController(g *gin.RouterGroup) *ServerController {
function isValidFilename (line 277) | func isValidFilename(filename string) bool {
FILE: web/controller/setting.go
type updateUserForm (line 16) | type updateUserForm struct
type SettingController (line 24) | type SettingController struct
method initRouter (line 38) | func (a *SettingController) initRouter(g *gin.RouterGroup) {
method getAllSetting (line 50) | func (a *SettingController) getAllSetting(c *gin.Context) {
method getDefaultSettings (line 60) | func (a *SettingController) getDefaultSettings(c *gin.Context) {
method updateSetting (line 70) | func (a *SettingController) updateSetting(c *gin.Context) {
method updateUser (line 82) | func (a *SettingController) updateUser(c *gin.Context) {
method restartPanel (line 108) | func (a *SettingController) restartPanel(c *gin.Context) {
method getDefaultXrayConfig (line 114) | func (a *SettingController) getDefaultXrayConfig(c *gin.Context) {
function NewSettingController (line 31) | func NewSettingController(g *gin.RouterGroup) *SettingController {
FILE: web/controller/util.go
function getRemoteIp (line 16) | func getRemoteIp(c *gin.Context) string {
function jsonMsg (line 32) | func jsonMsg(c *gin.Context, msg string, err error) {
function jsonObj (line 37) | func jsonObj(c *gin.Context, obj any, err error) {
function jsonMsgObj (line 42) | func jsonMsgObj(c *gin.Context, msg string, obj any, err error) {
function pureJsonMsg (line 60) | func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg strin...
function html (line 68) | func html(c *gin.Context, name string, title string, data gin.H) {
function getContext (line 91) | func getContext(h gin.H) gin.H {
function isAjax (line 102) | func isAjax(c *gin.Context) bool {
FILE: web/controller/websocket.go
constant writeWait (line 20) | writeWait = 10 * time.Second
constant pongWait (line 23) | pongWait = 60 * time.Second
constant pingPeriod (line 26) | pingPeriod = (pongWait * 9) / 10
constant maxMessageSize (line 29) | maxMessageSize = 512
type WebSocketController (line 69) | type WebSocketController struct
method HandleWebSocket (line 82) | func (w *WebSocketController) HandleWebSocket(c *gin.Context) {
method readPump (line 116) | func (w *WebSocketController) readPump(client *websocket.Client, conn ...
method writePump (line 154) | func (w *WebSocketController) writePump(client *websocket.Client, conn...
function NewWebSocketController (line 75) | func NewWebSocketController(hub *websocket.Hub) *WebSocketController {
FILE: web/controller/xray_setting.go
type XraySettingController (line 13) | type XraySettingController struct
method initRouter (line 30) | func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
method getXraySetting (line 44) | func (a *XraySettingController) getXraySetting(c *gin.Context) {
method updateSetting (line 73) | func (a *XraySettingController) updateSetting(c *gin.Context) {
method getDefaultXrayConfig (line 88) | func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
method getXrayResult (line 98) | func (a *XraySettingController) getXrayResult(c *gin.Context) {
method warp (line 103) | func (a *XraySettingController) warp(c *gin.Context) {
method getOutboundsTraffic (line 127) | func (a *XraySettingController) getOutboundsTraffic(c *gin.Context) {
method resetOutboundsTraffic (line 137) | func (a *XraySettingController) resetOutboundsTraffic(c *gin.Context) {
method testOutbound (line 149) | func (a *XraySettingController) testOutbound(c *gin.Context) {
function NewXraySettingController (line 23) | func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
FILE: web/controller/xui.go
type XUIController (line 8) | type XUIController struct
method initRouter (line 23) | func (a *XUIController) initRouter(g *gin.RouterGroup) {
method index (line 37) | func (a *XUIController) index(c *gin.Context) {
method inbounds (line 42) | func (a *XUIController) inbounds(c *gin.Context) {
method settings (line 47) | func (a *XUIController) settings(c *gin.Context) {
method xraySettings (line 52) | func (a *XUIController) xraySettings(c *gin.Context) {
function NewXUIController (line 16) | func NewXUIController(g *gin.RouterGroup) *XUIController {
FILE: web/entity/entity.go
type Msg (line 15) | type Msg struct
type AllSetting (line 22) | type AllSetting struct
method CheckValid (line 110) | func (s *AllSetting) CheckValid() error {
FILE: web/global/global.go
type WebServer (line 17) | type WebServer interface
type SubServer (line 24) | type SubServer interface
function SetWebServer (line 29) | func SetWebServer(s WebServer) {
function GetWebServer (line 34) | func GetWebServer() WebServer {
function SetSubServer (line 39) | func SetSubServer(s SubServer) {
function GetSubServer (line 44) | func GetSubServer() SubServer {
FILE: web/global/hashStorage.go
type HashEntry (line 12) | type HashEntry struct
type HashStorage (line 19) | type HashStorage struct
method SaveHash (line 34) | func (h *HashStorage) SaveHash(query string) string {
method GetValue (line 53) | func (h *HashStorage) GetValue(hash string) (string, bool) {
method IsMD5 (line 63) | func (h *HashStorage) IsMD5(hash string) bool {
method RemoveExpiredHashes (line 69) | func (h *HashStorage) RemoveExpiredHashes() {
method Reset (line 83) | func (h *HashStorage) Reset() {
function NewHashStorage (line 26) | func NewHashStorage(expiration time.Duration) *HashStorage {
FILE: web/job/check_client_ip_job.go
type IPWithTimestamp (line 22) | type IPWithTimestamp struct
type CheckClientIpJob (line 28) | type CheckClientIpJob struct
method Run (line 41) | func (j *CheckClientIpJob) Run() {
method clearAccessLog (line 74) | func (j *CheckClientIpJob) clearAccessLog() {
method hasLimitIp (line 95) | func (j *CheckClientIpJob) hasLimitIp() bool {
method processLogFile (line 124) | func (j *CheckClientIpJob) processLogFile() bool {
method checkFail2BanInstalled (line 202) | func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
method checkAccessLogAvailable (line 209) | func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool)...
method checkError (line 225) | func (j *CheckClientIpJob) checkError(e error) {
method getInboundClientIps (line 231) | func (j *CheckClientIpJob) getInboundClientIps(clientEmail string) (*m...
method addInboundClientIps (line 241) | func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips...
method updateInboundClientIps (line 267) | func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *mo...
method getInboundByEmail (line 381) | func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*mod...
function NewCheckClientIpJob (line 36) | func NewCheckClientIpJob() *CheckClientIpJob {
FILE: web/job/check_cpu_usage.go
type CheckCpuJob (line 13) | type CheckCpuJob struct
method Run (line 24) | func (j *CheckCpuJob) Run() {
function NewCheckCpuJob (line 19) | func NewCheckCpuJob() *CheckCpuJob {
FILE: web/job/check_hash_storage.go
type CheckHashStorageJob (line 8) | type CheckHashStorageJob struct
method Run (line 18) | func (j *CheckHashStorageJob) Run() {
function NewCheckHashStorageJob (line 13) | func NewCheckHashStorageJob() *CheckHashStorageJob {
FILE: web/job/check_xray_running_job.go
type CheckXrayRunningJob (line 11) | type CheckXrayRunningJob struct
method Run (line 22) | func (j *CheckXrayRunningJob) Run() {
function NewCheckXrayRunningJob (line 17) | func NewCheckXrayRunningJob() *CheckXrayRunningJob {
FILE: web/job/clear_logs_job.go
type ClearLogsJob (line 13) | type ClearLogsJob struct
method Run (line 36) | func (j *ClearLogsJob) Run() {
function NewClearLogsJob (line 16) | func NewClearLogsJob() *ClearLogsJob {
function ensureFileExists (line 21) | func ensureFileExists(path string) error {
FILE: web/job/ldap_sync_job.go
type LdapSyncJob (line 20) | type LdapSyncJob struct
method Run (line 63) | func (j *LdapSyncJob) Run() {
method buildClient (line 190) | func (j *LdapSyncJob) buildClient(ib *model.Inbound, email string, def...
method batchSetEnable (line 210) | func (j *LdapSyncJob) batchSetEnable(ib *model.Inbound, emails []strin...
method deleteClientsNotInLDAP (line 240) | func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEm...
method clientsToJSON (line 309) | func (j *LdapSyncJob) clientsToJSON(clients []model.Client) string {
method clientToJSON (line 323) | func (j *LdapSyncJob) clientToJSON(c model.Client) string {
function mustGetString (line 27) | func mustGetString(fn func() (string, error)) string {
function mustGetInt (line 35) | func mustGetInt(fn func() (int, error)) int {
function mustGetBool (line 43) | func mustGetBool(fn func() (bool, error)) bool {
function mustGetStringOr (line 51) | func mustGetStringOr(fn func() (string, error), fallback string) string {
function NewLdapSyncJob (line 59) | func NewLdapSyncJob() *LdapSyncJob {
function splitCsv (line 174) | func splitCsv(s string) []string {
FILE: web/job/periodic_traffic_reset_job.go
type Period (line 9) | type Period
type PeriodicTrafficResetJob (line 12) | type PeriodicTrafficResetJob struct
method Run (line 25) | func (j *PeriodicTrafficResetJob) Run() {
function NewPeriodicTrafficResetJob (line 18) | func NewPeriodicTrafficResetJob(period Period) *PeriodicTrafficResetJob {
FILE: web/job/stats_notify_job.go
type LoginStatus (line 8) | type LoginStatus
constant LoginSuccess (line 11) | LoginSuccess LoginStatus = 1
constant LoginFail (line 12) | LoginFail LoginStatus = 0
type StatsNotifyJob (line 16) | type StatsNotifyJob struct
method Run (line 27) | func (j *StatsNotifyJob) Run() {
function NewStatsNotifyJob (line 22) | func NewStatsNotifyJob() *StatsNotifyJob {
FILE: web/job/xray_traffic_job.go
type XrayTrafficJob (line 15) | type XrayTrafficJob struct
method Run (line 28) | func (j *XrayTrafficJob) Run() {
method informTrafficToExternalAPI (line 93) | func (j *XrayTrafficJob) informTrafficToExternalAPI(inboundTraffics []...
function NewXrayTrafficJob (line 23) | func NewXrayTrafficJob() *XrayTrafficJob {
FILE: web/locale/locale.go
type I18nType (line 26) | type I18nType
constant Bot (line 29) | Bot I18nType = "bot"
constant Web (line 30) | Web I18nType = "web"
type SettingService (line 34) | type SettingService interface
function InitLocalizer (line 39) | func InitLocalizer(i18nFS embed.FS, settingService SettingService) error {
function createTemplateData (line 58) | func createTemplateData(params []string, separator ...string) map[string...
function I18n (line 76) | func I18n(i18nType I18nType, key string, params ...string) string {
function initTGBotLocalizer (line 109) | func initTGBotLocalizer(settingService SettingService) error {
function LocalizerMiddleware (line 123) | func LocalizerMiddleware() gin.HandlerFunc {
function loadTranslationsFromDisk (line 151) | func loadTranslationsFromDisk(bundle *i18n.Bundle) error {
function parseTranslationFiles (line 170) | func parseTranslationFiles(i18nFS embed.FS, i18nBundle *i18n.Bundle) err...
FILE: web/middleware/domainValidator.go
function DomainValidatorMiddleware (line 17) | func DomainValidatorMiddleware(domain string) gin.HandlerFunc {
FILE: web/middleware/redirect.go
function RedirectMiddleware (line 13) | func RedirectMiddleware(basePath string) gin.HandlerFunc {
FILE: web/network/auto_https_conn.go
type AutoHttpsConn (line 17) | type AutoHttpsConn struct
method readRequest (line 34) | func (c *AutoHttpsConn) readRequest() bool {
method Read (line 62) | func (c *AutoHttpsConn) Read(buf []byte) (int, error) {
function NewAutoHttpsConn (line 28) | func NewAutoHttpsConn(conn net.Conn) net.Conn {
FILE: web/network/auto_https_listener.go
type AutoHttpsListener (line 7) | type AutoHttpsListener struct
method Accept (line 21) | func (l *AutoHttpsListener) Accept() (net.Conn, error) {
function NewAutoHttpsListener (line 13) | func NewAutoHttpsListener(listener net.Listener) net.Listener {
FILE: web/service/inbound.go
type InboundService (line 25) | type InboundService struct
method GetInbounds (line 31) | func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, er...
method GetAllInbounds (line 62) | func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
method GetInboundsByTrafficReset (line 90) | func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*...
method checkPortExist (line 100) | func (s *InboundService) checkPortExist(listen string, port int, ignor...
method GetClients (line 130) | func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.C...
method getAllEmails (line 144) | func (s *InboundService) getAllEmails() ([]string, error) {
method contains (line 158) | func (s *InboundService) contains(slice []string, str string) bool {
method checkEmailsExistForClients (line 168) | func (s *InboundService) checkEmailsExistForClients(clients []model.Cl...
method checkEmailExistForInbound (line 188) | func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbo...
method AddInbound (line 216) | func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.In...
method DelInbound (line 325) | func (s *InboundService) DelInbound(id int) (bool, error) {
method GetInbound (line 368) | func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
method UpdateInbound (line 381) | func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model...
method updateClientTraffics (line 516) | func (s *InboundService) updateClientTraffics(tx *gorm.DB, oldInbound ...
method AddInboundClient (line 561) | func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, ...
method DelInboundClient (line 679) | func (s *InboundService) DelInboundClient(inboundId int, clientId stri...
method UpdateInboundClient (line 767) | func (s *InboundService) UpdateInboundClient(data *model.Inbound, clie...
method AddTraffic (line 942) | func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, c...
method addInboundTraffic (line 986) | func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xr...
method addClientTraffic (line 1009) | func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xra...
method adjustTraffics (line 1068) | func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics ...
method autoRenewClients (line 1126) | func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, e...
method disableInvalidInbounds (line 1222) | func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, in...
method disableInvalidClients (line 1256) | func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int...
method GetInboundTags (line 1302) | func (s *InboundService) GetInboundTags() (string, error) {
method MigrationRemoveOrphanedTraffics (line 1313) | func (s *InboundService) MigrationRemoveOrphanedTraffics() {
method AddClientStat (line 1325) | func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, cli...
method UpdateClientStat (line 1340) | func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, c...
method UpdateClientIPs (line 1354) | func (s *InboundService) UpdateClientIPs(tx *gorm.DB, oldEmail string,...
method DelClientStat (line 1358) | func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
method DelClientIPs (line 1362) | func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
method GetClientInboundByTrafficID (line 1366) | func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (t...
method GetClientInboundByEmail (line 1381) | func (s *InboundService) GetClientInboundByEmail(email string) (traffi...
method GetClientByEmail (line 1396) | func (s *InboundService) GetClientByEmail(clientEmail string) (*xray.C...
method SetClientTelegramUserID (line 1419) | func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId i...
method checkIsEnabledByEmail (line 1480) | func (s *InboundService) checkIsEnabledByEmail(clientEmail string) (bo...
method ToggleClientEnableByEmail (line 1506) | func (s *InboundService) ToggleClientEnableByEmail(clientEmail string)...
method SetClientEnableByEmail (line 1573) | func (s *InboundService) SetClientEnableByEmail(clientEmail string, en...
method ResetClientIpLimitByEmail (line 1588) | func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string,...
method ResetClientExpiryTimeByEmail (line 1647) | func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail stri...
method ResetClientTrafficLimitByEmail (line 1706) | func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail st...
method ResetClientTrafficByEmail (line 1768) | func (s *InboundService) ResetClientTrafficByEmail(clientEmail string)...
method ResetClientTraffic (line 1784) | func (s *InboundService) ResetClientTraffic(id int, clientEmail string...
method ResetAllClientTraffics (line 1846) | func (s *InboundService) ResetAllClientTraffics(id int) error {
method ResetAllTraffics (line 1883) | func (s *InboundService) ResetAllTraffics() error {
method DelDepletedClients (line 1894) | func (s *InboundService) DelDepletedClients(id int) (err error) {
method GetClientTrafficTgBot (line 1979) | func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.Cl...
method GetClientTrafficByEmail (line 2027) | func (s *InboundService) GetClientTrafficByEmail(email string) (traffi...
method UpdateClientTrafficByEmail (line 2042) | func (s *InboundService) UpdateClientTrafficByEmail(email string, uplo...
method GetClientTrafficByID (line 2057) | func (s *InboundService) GetClientTrafficByID(id string) ([]xray.Clien...
method SearchClientTraffic (line 2084) | func (s *InboundService) SearchClientTraffic(query string) (traffic *x...
method GetInboundClientIps (line 2136) | func (s *InboundService) GetInboundClientIps(clientEmail string) (stri...
method ClearClientIps (line 2183) | func (s *InboundService) ClearClientIps(clientEmail string) error {
method SearchInbounds (line 2196) | func (s *InboundService) SearchInbounds(query string) ([]*model.Inboun...
method MigrationRequirements (line 2206) | func (s *InboundService) MigrationRequirements() {
method MigrateDB (line 2362) | func (s *InboundService) MigrateDB() {
method GetOnlineClients (line 2367) | func (s *InboundService) GetOnlineClients() []string {
method GetClientsLastOnline (line 2371) | func (s *InboundService) GetClientsLastOnline() (map[string]int64, err...
method FilterAndSortClientEmails (line 2385) | func (s *InboundService) FilterAndSortClientEmails(emails []string) ([...
method DelInboundClientByEmail (line 2418) | func (s *InboundService) DelInboundClientByEmail(inboundId int, email ...
FILE: web/service/outbound.go
type OutboundService (line 27) | type OutboundService struct
method AddTraffic (line 32) | func (s *OutboundService) AddTraffic(traffics []*xray.Traffic, clientT...
method addOutboundTraffic (line 53) | func (s *OutboundService) addOutboundTraffic(tx *gorm.DB, traffics []*...
method GetOutboundsTraffic (line 85) | func (s *OutboundService) GetOutboundsTraffic() ([]*model.OutboundTraf...
method ResetOutboundTraffic (line 98) | func (s *OutboundService) ResetOutboundTraffic(tag string) error {
method TestOutbound (line 131) | func (s *OutboundService) TestOutbound(outboundJSON string, testURL st...
method createTestConfig (line 261) | func (s *OutboundService) createTestConfig(outboundTag string, allOutb...
method testConnection (line 335) | func (s *OutboundService) testConnection(proxyPort int, testURL string...
type TestOutboundResult (line 121) | type TestOutboundResult struct
function waitForPort (line 384) | func waitForPort(port int, timeout time.Duration) error {
function findAvailablePort (line 398) | func findAvailablePort() (int, error) {
function createTestConfigPath (line 411) | func createTestConfigPath() (string, error) {
FILE: web/service/panel.go
type PanelService (line 13) | type PanelService struct
method RestartPanel (line 15) | func (s *PanelService) RestartPanel(delay time.Duration) error {
FILE: web/service/server.go
type ProcessState (line 40) | type ProcessState
constant Running (line 44) | Running ProcessState = "running"
constant Stop (line 45) | Stop ProcessState = "stop"
constant Error (line 46) | Error ProcessState = "error"
type Status (line 51) | type Status struct
type Release (line 98) | type Release struct
type ServerService (line 104) | type ServerService struct
method AggregateCpuHistory (line 121) | func (s *ServerService) AggregateCpuHistory(bucketSeconds int, maxPoin...
method GetStatus (line 228) | func (s *ServerService) GetStatus(lastStatus *Status) *Status {
method AppendCpuSample (line 420) | func (s *ServerService) AppendCpuSample(t time.Time, v float64) {
method sampleCPUUtilization (line 435) | func (s *ServerService) sampleCPUUtilization() (float64, error) {
method GetXrayVersions (line 520) | func (s *ServerService) GetXrayVersions() ([]string, error) {
method StopXrayService (line 577) | func (s *ServerService) StopXrayService() error {
method RestartXrayService (line 586) | func (s *ServerService) RestartXrayService() error {
method downloadXRay (line 595) | func (s *ServerService) downloadXRay(version string) (string, error) {
method UpdateXray (line 646) | func (s *ServerService) UpdateXray(version string) error {
method GetLogs (line 712) | func (s *ServerService) GetLogs(count string, level string, syslog str...
method GetXrayLogs (line 759) | func (s *ServerService) GetXrayLogs(
method GetConfigJson (line 865) | func (s *ServerService) GetConfigJson() (any, error) {
method GetDb (line 884) | func (s *ServerService) GetDb() ([]byte, error) {
method ImportDB (line 906) | func (s *ServerService) ImportDB(file multipart.File) error {
method IsValidGeofileName (line 1031) | func (s *ServerService) IsValidGeofileName(filename string) bool {
method UpdateGeofile (line 1058) | func (s *ServerService) UpdateGeofile(fileName string) error {
method GetNewX25519Cert (line 1177) | func (s *ServerService) GetNewX25519Cert() (any, error) {
method GetNewmldsa65 (line 1203) | func (s *ServerService) GetNewmldsa65() (any, error) {
method GetNewEchCert (line 1229) | func (s *ServerService) GetNewEchCert(sni string) (any, error) {
method GetNewVlessEnc (line 1253) | func (s *ServerService) GetNewVlessEnc() (any, error) {
method GetNewUUID (line 1293) | func (s *ServerService) GetNewUUID() (map[string]string, error) {
method GetNewmlkem768 (line 1304) | func (s *ServerService) GetNewmlkem768() (any, error) {
type CPUSample (line 181) | type CPUSample struct
type LogEntry (line 186) | type LogEntry struct
function getPublicIP (line 196) | func getPublicIP(url string) string {
function logEntryContains (line 856) | func logEntryContains(line string, suffixes []string) bool {
FILE: web/service/setting.go
type SettingService (line 109) | type SettingService struct
method GetDefaultJSONConfig (line 111) | func (s *SettingService) GetDefaultJSONConfig() (any, error) {
method GetAllSetting (line 120) | func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) {
method ResetSettings (line 195) | func (s *SettingService) ResetSettings() error {
method getSetting (line 205) | func (s *SettingService) getSetting(key string) (*model.Setting, error) {
method saveSetting (line 215) | func (s *SettingService) saveSetting(key string, value string) error {
method getString (line 231) | func (s *SettingService) getString(key string) (string, error) {
method setString (line 245) | func (s *SettingService) setString(key string, value string) error {
method getBool (line 249) | func (s *SettingService) getBool(key string) (bool, error) {
method setBool (line 257) | func (s *SettingService) setBool(key string, value bool) error {
method getInt (line 261) | func (s *SettingService) getInt(key string) (int, error) {
method setInt (line 269) | func (s *SettingService) setInt(key string, value int) error {
method GetXrayConfigTemplate (line 273) | func (s *SettingService) GetXrayConfigTemplate() (string, error) {
method GetXrayOutboundTestUrl (line 277) | func (s *SettingService) GetXrayOutboundTestUrl() (string, error) {
method SetXrayOutboundTestUrl (line 281) | func (s *SettingService) SetXrayOutboundTestUrl(url string) error {
method GetListen (line 285) | func (s *SettingService) GetListen() (string, error) {
method SetListen (line 289) | func (s *SettingService) SetListen(ip string) error {
method GetWebDomain (line 293) | func (s *SettingService) GetWebDomain() (string, error) {
method GetTgBotToken (line 297) | func (s *SettingService) GetTgBotToken() (string, error) {
method SetTgBotToken (line 301) | func (s *SettingService) SetTgBotToken(token string) error {
method GetTgBotProxy (line 305) | func (s *SettingService) GetTgBotProxy() (string, error) {
method SetTgBotProxy (line 309) | func (s *SettingService) SetTgBotProxy(token string) error {
method GetTgBotAPIServer (line 313) | func (s *SettingService) GetTgBotAPIServer() (string, error) {
method SetTgBotAPIServer (line 317) | func (s *SettingService) SetTgBotAPIServer(token string) error {
method GetTgBotChatId (line 321) | func (s *SettingService) GetTgBotChatId() (string, error) {
method SetTgBotChatId (line 325) | func (s *SettingService) SetTgBotChatId(chatIds string) error {
method GetTgbotEnabled (line 329) | func (s *SettingService) GetTgbotEnabled() (bool, error) {
method SetTgbotEnabled (line 333) | func (s *SettingService) SetTgbotEnabled(value bool) error {
method GetTgbotRuntime (line 337) | func (s *SettingService) GetTgbotRuntime() (string, error) {
method SetTgbotRuntime (line 341) | func (s *SettingService) SetTgbotRuntime(time string) error {
method GetTgBotBackup (line 345) | func (s *SettingService) GetTgBotBackup() (bool, error) {
method GetTgBotLoginNotify (line 349) | func (s *SettingService) GetTgBotLoginNotify() (bool, error) {
method GetTgCpu (line 353) | func (s *SettingService) GetTgCpu() (int, error) {
method GetTgLang (line 357) | func (s *SettingService) GetTgLang() (string, error) {
method GetTwoFactorEnable (line 361) | func (s *SettingService) GetTwoFactorEnable() (bool, error) {
method SetTwoFactorEnable (line 365) | func (s *SettingService) SetTwoFactorEnable(value bool) error {
method GetTwoFactorToken (line 369) | func (s *SettingService) GetTwoFactorToken() (string, error) {
method SetTwoFactorToken (line 373) | func (s *SettingService) SetTwoFactorToken(value string) error {
method GetPort (line 377) | func (s *SettingService) GetPort() (int, error) {
method SetPort (line 381) | func (s *SettingService) SetPort(port int) error {
method SetCertFile (line 385) | func (s *SettingService) SetCertFile(webCertFile string) error {
method GetCertFile (line 389) | func (s *SettingService) GetCertFile() (string, error) {
method SetKeyFile (line 393) | func (s *SettingService) SetKeyFile(webKeyFile string) error {
method GetKeyFile (line 397) | func (s *SettingService) GetKeyFile() (string, error) {
method GetExpireDiff (line 401) | func (s *SettingService) GetExpireDiff() (int, error) {
method GetTrafficDiff (line 405) | func (s *SettingService) GetTrafficDiff() (int, error) {
method GetSessionMaxAge (line 409) | func (s *SettingService) GetSessionMaxAge() (int, error) {
method GetRemarkModel (line 413) | func (s *SettingService) GetRemarkModel() (string, error) {
method GetSecret (line 417) | func (s *SettingService) GetSecret() ([]byte, error) {
method SetBasePath (line 428) | func (s *SettingService) SetBasePath(basePath string) error {
method GetBasePath (line 438) | func (s *SettingService) GetBasePath() (string, error) {
method GetTimeLocation (line 452) | func (s *SettingService) GetTimeLocation() (*time.Location, error) {
method GetSubEnable (line 466) | func (s *SettingService) GetSubEnable() (bool, error) {
method GetSubJsonEnable (line 470) | func (s *SettingService) GetSubJsonEnable() (bool, error) {
method GetSubTitle (line 474) | func (s *SettingService) GetSubTitle() (string, error) {
method GetSubSupportUrl (line 478) | func (s *SettingService) GetSubSupportUrl() (string, error) {
method GetSubProfileUrl (line 482) | func (s *SettingService) GetSubProfileUrl() (string, error) {
method GetSubAnnounce (line 486) | func (s *SettingService) GetSubAnnounce() (string, error) {
method GetSubEnableRouting (line 490) | func (s *SettingService) GetSubEnableRouting() (bool, error) {
method GetSubRoutingRules (line 494) | func (s *SettingService) GetSubRoutingRules() (string, error) {
method GetSubListen (line 498) | func (s *SettingService) GetSubListen() (string, error) {
method GetSubPort (line 502) | func (s *SettingService) GetSubPort() (int, error) {
method GetSubPath (line 506) | func (s *SettingService) GetSubPath() (string, error) {
method GetSubJsonPath (line 510) | func (s *SettingService) GetSubJsonPath() (string, error) {
method GetSubDomain (line 514) | func (s *SettingService) GetSubDomain() (string, error) {
method SetSubCertFile (line 518) | func (s *SettingService) SetSubCertFile(subCertFile string) error {
method GetSubCertFile (line 522) | func (s *SettingService) GetSubCertFile() (string, error) {
method SetSubKeyFile (line 526) | func (s *SettingService) SetSubKeyFile(subKeyFile string) error {
method GetSubKeyFile (line 530) | func (s *SettingService) GetSubKeyFile() (string, error) {
method GetSubUpdates (line 534) | func (s *SettingService) GetSubUpdates() (string, error) {
method GetSubEncrypt (line 538) | func (s *SettingService) GetSubEncrypt() (bool, error) {
method GetSubShowInfo (line 542) | func (s *SettingService) GetSubShowInfo() (bool, error) {
method GetPageSize (line 546) | func (s *SettingService) GetPageSize() (int, error) {
method GetSubURI (line 550) | func (s *SettingService) GetSubURI() (string, error) {
method GetSubJsonURI (line 554) | func (s *SettingService) GetSubJsonURI() (string, error) {
method GetSubJsonFragment (line 558) | func (s *SettingService) GetSubJsonFragment() (string, error) {
method GetSubJsonNoises (line 562) | func (s *SettingService) GetSubJsonNoises() (string, error) {
method GetSubJsonMux (line 566) | func (s *SettingService) GetSubJsonMux() (string, error) {
method GetSubJsonRules (line 570) | func (s *SettingService) GetSubJsonRules() (string, error) {
method GetDatepicker (line 574) | func (s *SettingService) GetDatepicker() (string, error) {
method GetWarp (line 578) | func (s *SettingService) GetWarp() (string, error) {
method SetWarp (line 582) | func (s *SettingService) SetWarp(data string) error {
method GetExternalTrafficInformEnable (line 586) | func (s *SettingService) GetExternalTrafficInformEnable() (bool, error) {
method SetExternalTrafficInformEnable (line 590) | func (s *SettingService) SetExternalTrafficInformEnable(value bool) er...
method GetExternalTrafficInformURI (line 594) | func (s *SettingService) GetExternalTrafficInformURI() (string, error) {
method SetExternalTrafficInformURI (line 598) | func (s *SettingService) SetExternalTrafficInformURI(InformURI string)...
method GetIpLimitEnable (line 602) | func (s *SettingService) GetIpLimitEnable() (bool, error) {
method GetLdapEnable (line 611) | func (s *SettingService) GetLdapEnable() (bool, error) {
method GetLdapHost (line 615) | func (s *SettingService) GetLdapHost() (string, error) {
method GetLdapPort (line 619) | func (s *SettingService) GetLdapPort() (int, error) {
method GetLdapUseTLS (line 623) | func (s *SettingService) GetLdapUseTLS() (bool, error) {
method GetLdapBindDN (line 627) | func (s *SettingService) GetLdapBindDN() (string, error) {
method GetLdapPassword (line 631) | func (s *SettingService) GetLdapPassword() (string, error) {
method GetLdapBaseDN (line 635) | func (s *SettingService) GetLdapBaseDN() (string, error) {
method GetLdapUserFilter (line 639) | func (s *SettingService) GetLdapUserFilter() (string, error) {
method GetLdapUserAttr (line 643) | func (s *SettingService) GetLdapUserAttr() (string, error) {
method GetLdapVlessField (line 647) | func (s *SettingService) GetLdapVlessField() (string, error) {
method GetLdapSyncCron (line 651) | func (s *SettingService) GetLdapSyncCron() (string, error) {
method GetLdapFlagField (line 655) | func (s *SettingService) GetLdapFlagField() (string, error) {
method GetLdapTruthyValues (line 659) | func (s *SettingService) GetLdapTruthyValues() (string, error) {
method GetLdapInvertFlag (line 663) | func (s *SettingService) GetLdapInvertFlag() (bool, error) {
method GetLdapInboundTags (line 667) | func (s *SettingService) GetLdapInboundTags() (string, error) {
method GetLdapAutoCreate (line 671) | func (s *SettingService) GetLdapAutoCreate() (bool, error) {
method GetLdapAutoDelete (line 675) | func (s *SettingService) GetLdapAutoDelete() (bool, error) {
method GetLdapDefaultTotalGB (line 679) | func (s *SettingService) GetLdapDefaultTotalGB() (int, error) {
method GetLdapDefaultExpiryDays (line 683) | func (s *SettingService) GetLdapDefaultExpiryDays() (int, error) {
method GetLdapDefaultLimitIP (line 687) | func (s *SettingService) GetLdapDefaultLimitIP() (int, error) {
method UpdateAllSetting (line 691) | func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSettin...
method GetDefaultXrayConfig (line 712) | func (s *SettingService) GetDefaultXrayConfig() (any, error) {
method GetDefaultSettings (line 743) | func (s *SettingService) GetDefaultSettings(host string) (any, error) {
function extractHostname (line 721) | func extractHostname(host string) string {
FILE: web/service/tgbot.go
type LoginStatus (line 98) | type LoginStatus
constant LoginSuccess (line 102) | LoginSuccess LoginStatus = 1
constant LoginFail (line 103) | LoginFail LoginStatus = 0
constant EmptyTelegramUserID (line 104) | EmptyTelegramUserID = int64(0)
type Tgbot (line 109) | type Tgbot struct
method NewTgbot (line 118) | func (t *Tgbot) NewTgbot() *Tgbot {
method I18nBot (line 123) | func (t *Tgbot) I18nBot(name string, params ...string) string {
method GetHashStorage (line 128) | func (t *Tgbot) GetHashStorage() *global.HashStorage {
method getCachedStatus (line 133) | func (t *Tgbot) getCachedStatus() (*Status, bool) {
method setCachedStatus (line 144) | func (t *Tgbot) setCachedStatus(status *Status) {
method getCachedServerStats (line 153) | func (t *Tgbot) getCachedServerStats() (string, bool) {
method setCachedServerStats (line 164) | func (t *Tgbot) setCachedServerStats(stats string) {
method Start (line 173) | func (t *Tgbot) Start(i18nFS embed.FS) error {
method createRobustFastHTTPClient (line 278) | func (t *Tgbot) createRobustFastHTTPClient(proxyUrl string) *fasthttp....
method NewBot (line 308) | func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl str...
method IsRunning (line 352) | func (t *Tgbot) IsRunning() bool {
method SetHostname (line 359) | func (t *Tgbot) SetHostname() {
method Stop (line 371) | func (t *Tgbot) Stop() {
method encodeQuery (line 406) | func (t *Tgbot) encodeQuery(query string) string {
method decodeQuery (line 416) | func (t *Tgbot) decodeQuery(query string) (string, error) {
method OnReceive (line 430) | func (t *Tgbot) OnReceive() {
method answerCommand (line 640) | func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, i...
method sendResponse (line 716) | func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, is...
method randomLowerAndNum (line 725) | func (t *Tgbot) randomLowerAndNum(length int) string {
method randomShadowSocksPassword (line 736) | func (t *Tgbot) randomShadowSocksPassword() string {
method answerCallback (line 746) | func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, is...
method BuildInboundClientDataMessage (line 2033) | func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, p...
method BuildJSONForProtocol (line 2083) | func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string,...
method SubmitAddClient (line 2162) | func (t *Tgbot) SubmitAddClient() (bool, error) {
method SendAnswer (line 2195) | func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
method SendMsgToTgbot (line 2251) | func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ....
method buildSubscriptionURLs (line 2331) | func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, e...
method sendClientSubLinks (line 2419) | func (t *Tgbot) sendClientSubLinks(chatId int64, email string) {
method sendClientIndividualLinks (line 2441) | func (t *Tgbot) sendClientIndividualLinks(chatId int64, email string) {
method sendClientQRLinks (line 2523) | func (t *Tgbot) sendClientQRLinks(chatId int64, email string) {
method SendMsgToTgbotAdmins (line 2618) | func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego...
method SendReport (line 2631) | func (t *Tgbot) SendReport() {
method SendBackupToAdmins (line 2653) | func (t *Tgbot) SendBackupToAdmins() {
method sendExhaustedToAdmins (line 2667) | func (t *Tgbot) sendExhaustedToAdmins() {
method getServerUsage (line 2677) | func (t *Tgbot) getServerUsage(chatId int64, messageID ...int) string {
method sendServerUsage (line 2693) | func (t *Tgbot) sendServerUsage() string {
method prepareServerUsageInfo (line 2699) | func (t *Tgbot) prepareServerUsageInfo() string {
method UserLoginNotify (line 2763) | func (t *Tgbot) UserLoginNotify(username string, password string, ip s...
method getInboundUsages (line 2795) | func (t *Tgbot) getInboundUsages() string {
method getInbounds (line 2822) | func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) {
method getInboundsFor (line 2854) | func (t *Tgbot) getInboundsFor(nextAction string) (*telego.InlineKeybo...
method getInboundClientsFor (line 2886) | func (t *Tgbot) getInboundClientsFor(inboundID int, action string) (*t...
method getInboundsAddClient (line 2921) | func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, ...
method getInboundClients (line 2964) | func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarku...
method clientInfoMsg (line 2999) | func (t *Tgbot) clientInfoMsg(
method getClientUsage (line 3108) | func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ......
method searchClientIps (line 3151) | func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...
method clientTelegramUserInfo (line 3211) | func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, mes...
method searchClient (line 3264) | func (t *Tgbot) searchClient(chatId int64, email string, messageID ......
method getCommonClientButtons (line 3310) | func (t *Tgbot) getCommonClientButtons() [][]telego.InlineKeyboardButt...
method addClient (line 3331) | func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
method searchInbound (line 3377) | func (t *Tgbot) searchInbound(chatId int64, remark string) {
method getExhausted (line 3415) | func (t *Tgbot) getExhausted(chatId int64) {
method notifyExhausted (line 3512) | func (t *Tgbot) notifyExhausted() {
method onlineClients (line 3594) | func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
method sendBackup (line 3629) | func (t *Tgbot) sendBackup(chatId int64) {
method sendBanLogs (line 3680) | func (t *Tgbot) sendBanLogs(chatId int64, dt bool) {
method sendCallbackAnswerTgBot (line 3730) | func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) {
method editMessageCallbackTgBot (line 3741) | func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, ...
method editMessageTgBot (line 3753) | func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text str...
method SendMsgToTgbotDeleteAfter (line 3769) | func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, de...
method deleteMessageTgBot (line 3796) | func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) {
method isSingleWord (line 3809) | func (t *Tgbot) isSingleWord(text string) bool {
function StopBot (line 381) | func StopBot() {
function checkAdmin (line 2185) | func checkAdmin(tgId int64) bool {
function int64Contains (line 3584) | func int64Contains(slice []int64, item int64) bool {
FILE: web/service/user.go
type UserService (line 17) | type UserService struct
method GetFirstUser (line 23) | func (s *UserService) GetFirstUser() (*model.User, error) {
method CheckUser (line 36) | func (s *UserService) CheckUser(username string, password string, twoF...
method UpdateUser (line 105) | func (s *UserService) UpdateUser(id int, username string, password str...
method UpdateFirstUser (line 129) | func (s *UserService) UpdateFirstUser(username string, password string...
FILE: web/service/warp.go
type WarpService (line 17) | type WarpService struct
method GetWarpData (line 21) | func (s *WarpService) GetWarpData() (string, error) {
method DelWarpData (line 29) | func (s *WarpService) DelWarpData() error {
method GetWarpConfig (line 37) | func (s *WarpService) GetWarpConfig() (string, error) {
method RegWarp (line 71) | func (s *WarpService) RegWarp(secretKey string, publicKey string) (str...
method SetWarpLicense (line 122) | func (s *WarpService) SetWarpLicense(license string) (string, error) {
FILE: web/service/xray.go
type XrayService (line 25) | type XrayService struct
method IsXrayRunning (line 32) | func (s *XrayService) IsXrayRunning() bool {
method GetXrayErr (line 37) | func (s *XrayService) GetXrayErr() error {
method GetXrayResult (line 57) | func (s *XrayService) GetXrayResult() string {
method GetXrayVersion (line 80) | func (s *XrayService) GetXrayVersion() string {
method GetXrayConfig (line 94) | func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
method GetXrayTraffic (line 198) | func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.Clien...
method RestartXray (line 217) | func (s *XrayService) RestartXray(isForce bool) error {
method StopXray (line 247) | func (s *XrayService) StopXray() error {
method SetToNeedRestart (line 259) | func (s *XrayService) SetToNeedRestart() {
method IsNeedRestartAndSetFalse (line 264) | func (s *XrayService) IsNeedRestartAndSetFalse() bool {
method DidXrayCrash (line 269) | func (s *XrayService) DidXrayCrash() bool {
function RemoveIndex (line 89) | func RemoveIndex(s []any, index int) []any {
FILE: web/service/xray_setting.go
type XraySettingService (line 13) | type XraySettingService struct
method SaveXraySetting (line 17) | func (s *XraySettingService) SaveXraySetting(newXraySettings string) e...
method CheckXrayConfig (line 24) | func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string...
FILE: web/session/session.go
constant loginUserKey (line 16) | loginUserKey = "LOGIN_USER"
constant defaultPath (line 17) | defaultPath = "/"
function init (line 20) | func init() {
function SetLoginUser (line 26) | func SetLoginUser(c *gin.Context, user *model.User) {
function SetMaxAge (line 36) | func SetMaxAge(c *gin.Context, maxAge int) {
function GetLoginUser (line 48) | func GetLoginUser(c *gin.Context) *model.User {
function IsLogin (line 65) | func IsLogin(c *gin.Context) bool {
function ClearSession (line 71) | func ClearSession(c *gin.Context) {
FILE: web/web.go
type wrapAssetsFS (line 48) | type wrapAssetsFS struct
method Open (line 52) | func (f *wrapAssetsFS) Open(name string) (fs.File, error) {
type wrapAssetsFile (line 62) | type wrapAssetsFile struct
method Stat (line 66) | func (f *wrapAssetsFile) Stat() (fs.FileInfo, error) {
type wrapAssetsFileInfo (line 76) | type wrapAssetsFileInfo struct
method ModTime (line 80) | func (f *wrapAssetsFileInfo) ModTime() time.Time {
function EmbeddedHTML (line 85) | func EmbeddedHTML() embed.FS {
function EmbeddedAssets (line 90) | func EmbeddedAssets() embed.FS {
type Server (line 95) | type Server struct
method getHtmlFiles (line 127) | func (s *Server) getHtmlFiles() ([]string, error) {
method getHtmlTemplate (line 149) | func (s *Server) getHtmlTemplate(funcMap template.FuncMap) (*template....
method initRouter (line 174) | func (s *Server) initRouter() (*gin.Engine, error) {
method startTask (line 297) | func (s *Server) startTask() {
method Start (line 376) | func (s *Server) Start() (err error) {
method Stop (line 455) | func (s *Server) Stop() error {
method GetCtx (line 480) | func (s *Server) GetCtx() context.Context {
method GetCron (line 485) | func (s *Server) GetCron() *cron.Cron {
method GetWSHub (line 490) | func (s *Server) GetWSHub() any {
method RestartXray (line 494) | func (s *Server) RestartXray() error {
function NewServer (line 117) | func NewServer() *Server {
FILE: web/websocket/hub.go
type MessageType (line 15) | type MessageType
constant MessageTypeStatus (line 18) | MessageTypeStatus MessageType = "status"
constant MessageTypeTraffic (line 19) | MessageTypeTraffic MessageType = "traffic"
constant MessageTypeInbounds (line 20) | MessageTypeInbounds MessageType = "inbounds"
constant MessageTypeNotification (line 21) | MessageTypeNotification MessageType = "notification"
constant MessageTypeXrayState (line 22) | MessageTypeXrayState MessageType = "xray_state"
constant MessageTypeOutbounds (line 23) | MessageTypeOutbounds MessageType = "outbounds"
type Message (line 27) | type Message struct
type Client (line 34) | type Client struct
type Hub (line 42) | type Hub struct
method Run (line 92) | func (h *Hub) Run() {
method broadcastParallel (line 185) | func (h *Hub) broadcastParallel(clients []*Client, message []byte) {
method Broadcast (line 253) | func (h *Hub) Broadcast(messageType MessageType, payload any) {
method BroadcastToTopic (line 292) | func (h *Hub) BroadcastToTopic(messageType MessageType, payload any) {
method GetClientCount (line 337) | func (h *Hub) GetClientCount() int {
method Register (line 344) | func (h *Hub) Register(client *Client) {
method Unregister (line 356) | func (h *Hub) Unregister(client *Client) {
method Stop (line 368) | func (h *Hub) Stop() {
function NewHub (line 68) | func NewHub() *Hub {
function getCurrentTimestamp (line 378) | func getCurrentTimestamp() int64 {
FILE: web/websocket/notifier.go
function GetHub (line 10) | func GetHub() *Hub {
function BroadcastStatus (line 28) | func BroadcastStatus(status any) {
function BroadcastTraffic (line 36) | func BroadcastTraffic(traffic any) {
function BroadcastInbounds (line 44) | func BroadcastInbounds(inbounds any) {
function BroadcastOutbounds (line 52) | func BroadcastOutbounds(outbounds any) {
function BroadcastNotification (line 60) | func BroadcastNotification(title, message, level string) {
function BroadcastXrayState (line 73) | func BroadcastXrayState(state string, errorMsg string) {
FILE: xray/api.go
type XrayAPI (line 32) | type XrayAPI struct
method Init (line 40) | func (x *XrayAPI) Init(apiPort int) error {
method Close (line 64) | func (x *XrayAPI) Close() {
method AddInbound (line 74) | func (x *XrayAPI) AddInbound(inbound []byte) error {
method DelInbound (line 96) | func (x *XrayAPI) DelInbound(tag string) error {
method AddUser (line 105) | func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map...
method RemoveUser (line 189) | func (x *XrayAPI) RemoveUser(inboundTag, email string) error {
method GetTraffic (line 208) | func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic...
function processTraffic (line 243) | func processTraffic(matches []string, value int64, trafficMap map[string...
function processClientTraffic (line 270) | func processClientTraffic(matches []string, value int64, clientTrafficMa...
function mapToSlice (line 288) | func mapToSlice[T any](m map[string]*T) []*T {
FILE: xray/client_traffic.go
type ClientTraffic (line 5) | type ClientTraffic struct
FILE: xray/config.go
type Config (line 11) | type Config struct
method Equals (line 29) | func (c *Config) Equals(other *Config) bool {
FILE: xray/inbound.go
type InboundConfig (line 11) | type InboundConfig struct
method Equals (line 22) | func (c *InboundConfig) Equals(other *InboundConfig) bool {
FILE: xray/log_writer.go
function NewLogWriter (line 12) | func NewLogWriter() *LogWriter {
type LogWriter (line 17) | type LogWriter struct
method Write (line 22) | func (lw *LogWriter) Write(m []byte) (n int, err error) {
FILE: xray/process.go
function GetBinaryName (line 22) | func GetBinaryName() string {
function GetBinaryPath (line 27) | func GetBinaryPath() string {
function GetConfigPath (line 32) | func GetConfigPath() string {
function GetGeositePath (line 37) | func GetGeositePath() string {
function GetGeoipPath (line 42) | func GetGeoipPath() string {
function GetIPLimitLogPath (line 47) | func GetIPLimitLogPath() string {
function GetIPLimitBannedLogPath (line 52) | func GetIPLimitBannedLogPath() string {
function GetIPLimitBannedPrevLogPath (line 57) | func GetIPLimitBannedPrevLogPath() string {
function GetAccessPersistentLogPath (line 62) | func GetAccessPersistentLogPath() string {
function GetAccessPersistentPrevLogPath (line 67) | func GetAccessPersistentPrevLogPath() string {
function GetAccessLogPath (line 72) | func GetAccessLogPath() (string, error) {
function stopProcess (line 97) | func stopProcess(p *Process) {
type Process (line 102) | type Process struct
method GetAPIPort (line 184) | func (p *Process) GetAPIPort() int {
method GetConfig (line 189) | func (p *Process) GetConfig() *Config {
method GetOnlineClients (line 194) | func (p *Process) GetOnlineClients() []string {
method SetOnlineClients (line 199) | func (p *Process) SetOnlineClients(users []string) {
method GetUptime (line 204) | func (p *Process) GetUptime() uint64 {
function NewProcess (line 107) | func NewProcess(xrayConfig *Config) *Process {
function NewTestProcess (line 116) | func NewTestProcess(xrayConfig *Config, configPath string) *Process {
type process (line 122) | type process struct
method IsRunning (line 155) | func (p *process) IsRunning() bool {
method GetErr (line 166) | func (p *process) GetErr() error {
method GetResult (line 171) | func (p *process) GetResult() string {
method GetVersion (line 179) | func (p *process) GetVersion() string {
method refreshAPIPort (line 209) | func (p *process) refreshAPIPort() {
method refreshVersion (line 219) | func (p *process) refreshVersion() {
method Start (line 235) | func (p *process) Start() (err error) {
method Stop (line 296) | func (p *process) Stop() error {
function newProcess (line 138) | func newProcess(config *Config) *process {
function newTestProcess (line 148) | func newTestProcess(config *Config, configPath string) *process {
function writeCrashReport (line 319) | func writeCrashReport(m []byte) error {
FILE: xray/traffic.go
type Traffic (line 5) | type Traffic struct
Condensed preview — 206 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,659K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 724,
"preview": "# These are supported funding model platforms\n\ngithub: MHSanaei\npatreon: # Replace with a single Patreon username\nopen_c"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
"chars": 1921,
"preview": "name: Bug report\ndescription: Create a report to help us improve\ntitle: \"Bug report\"\nlabels: [\"bug\"]\n\nbody:\n - type: ma"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
"chars": 1159,
"preview": "name: Feature request\r\ndescription: Suggest an idea for this project\r\ntitle: \"Feature request\"\r\nlabels: [\"enhancement\"]\r"
},
{
"path": ".github/ISSUE_TEMPLATE/question.yaml",
"chars": 527,
"preview": "name: Question\ndescription: Describe this issue template's purpose here.\ntitle: \"Question\"\nlabels: [\"question\"]\n\nbody:\n "
},
{
"path": ".github/copilot-instructions.md",
"chars": 6527,
"preview": "# 3X-UI Development Guide\n\n## Project Overview\n3X-UI is a web-based control panel for managing Xray-core servers. It's a"
},
{
"path": ".github/dependabot.yml",
"chars": 536,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/pull_request_template.yml",
"chars": 414,
"preview": "## What is the pull request?\r\n\r\n<!-- Briefly describe the changes introduced by this pull request -->\r\n\r\n## Which part o"
},
{
"path": ".github/workflows/cleanup_caches.yml",
"chars": 1037,
"preview": "name: Cleanup Caches\non:\n schedule:\n - cron: '0 3 * * 0' # every Sunday\n workflow_dispatch:\n\njobs:\n cleanup:\n "
},
{
"path": ".github/workflows/docker.yml",
"chars": 1477,
"preview": "name: Release 3X-UI for Docker\n\npermissions:\n contents: read\n packages: write\n\non:\n workflow_dispatch:\n push:\n ta"
},
{
"path": ".github/workflows/release.yml",
"chars": 10571,
"preview": "name: Release 3X-UI\n\non:\n workflow_dispatch:\n push:\n branches:\n - '**'\n tags:\n - \"v*.*.*\"\n paths:\n "
},
{
"path": ".gitignore",
"chars": 501,
"preview": "# Ignore editor and IDE settings\n.idea/\n.vscode/\n.cache/\n.sync*\n\n# Ignore log files\n*.log\n\n# Ignore temporary files\ntmp/"
},
{
"path": "CONTRIBUTING.md",
"chars": 133,
"preview": "## Local Development Setup\n\n- Create a directory named `x-ui` in the project root \n- Rename `.env.example` to `.env `\n- "
},
{
"path": "DockerEntrypoint.sh",
"chars": 118,
"preview": "#!/bin/sh\n\n# Start fail2ban\n[ $XUI_ENABLE_FAIL2BAN == \"true\" ] && fail2ban-client -x start\n\n# Run x-ui\nexec /app/x-ui\n"
},
{
"path": "DockerInit.sh",
"chars": 1342,
"preview": "#!/bin/sh\ncase $1 in\n amd64)\n ARCH=\"64\"\n FNAME=\"amd64\"\n ;;\n i386)\n ARCH=\"32\"\n F"
},
{
"path": "Dockerfile",
"chars": 1424,
"preview": "# ========================================================\n# Stage: Builder\n# =========================================="
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.ar_EG.md",
"chars": 2948,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "README.es_ES.md",
"chars": 3200,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "README.fa_IR.md",
"chars": 3060,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "README.md",
"chars": 3098,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "README.ru_RU.md",
"chars": 3193,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "README.zh_CN.md",
"chars": 2496,
"preview": "[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/R"
},
{
"path": "config/config.go",
"chars": 3524,
"preview": "// Package config provides configuration management utilities for the 3x-ui panel,\n// including version information, log"
},
{
"path": "config/name",
"chars": 4,
"preview": "x-ui"
},
{
"path": "config/version",
"chars": 6,
"preview": "2.8.11"
},
{
"path": "database/db.go",
"chars": 5214,
"preview": "// Package database provides database initialization, migration, and management utilities\n// for the 3x-ui panel using G"
},
{
"path": "database/model/model.go",
"chars": 6802,
"preview": "// Package model defines the database models and data structures used by the 3x-ui panel.\npackage model\n\nimport (\n\t\"fmt\""
},
{
"path": "docker-compose.yml",
"chars": 373,
"preview": "services:\n 3xui:\n build:\n context: .\n dockerfile: ./Dockerfile\n container_name: 3xui_app\n # hostname"
},
{
"path": "go.mod",
"chars": 4588,
"preview": "module github.com/mhsanaei/3x-ui/v2\n\ngo 1.26.0\n\nrequire (\n\tgithub.com/gin-contrib/gzip v1.2.5\n\tgithub.com/gin-contrib/se"
},
{
"path": "go.sum",
"chars": 25208,
"preview": "github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=\ngithub.com/Azure/go-ntlmssp v0.1.0/go"
},
{
"path": "install.sh",
"chars": 40926,
"preview": "#!/bin/bash\n\nred='\\033[0;31m'\ngreen='\\033[0;32m'\nblue='\\033[0;34m'\nyellow='\\033[0;33m'\nplain='\\033[0m'\n\ncur_dir=$(pwd)\n\n"
},
{
"path": "logger/logger.go",
"chars": 6442,
"preview": "// Package logger provides logging functionality for the 3x-ui panel with\n// dual-backend logging (console/syslog and fi"
},
{
"path": "main.go",
"chars": 14321,
"preview": "// Package main is the entry point for the 3x-ui web panel application.\n// It initializes the database, web server, and "
},
{
"path": "sub/default.json",
"chars": 1512,
"preview": "{\n \"remarks\": \"\",\n \"dns\": {\n \"tag\": \"dns_out\",\n \"queryStrategy\": \"UseIP\",\n \"servers\": [\n {\n \"addr"
},
{
"path": "sub/sub.go",
"chars": 9779,
"preview": "// Package sub provides subscription server functionality for the 3x-ui panel,\n// including HTTP/HTTPS servers for servi"
},
{
"path": "sub/subController.go",
"chars": 6616,
"preview": "package sub\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/config\"\n\n\t\"github."
},
{
"path": "sub/subJsonService.go",
"chars": 12419,
"preview": "package sub\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"maps\"\n\t\"strings\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/database/mod"
},
{
"path": "sub/subService.go",
"chars": 34802,
"preview": "package sub\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"gith"
},
{
"path": "update.sh",
"chars": 40300,
"preview": "#!/bin/bash\n\nred='\\033[0;31m'\ngreen='\\033[0;32m'\nblue='\\033[0;34m'\nyellow='\\033[0;33m'\nplain='\\033[0m'\n\nxui_folder=\"${XU"
},
{
"path": "util/common/err.go",
"chars": 752,
"preview": "// Package common provides common utility functions for error handling, formatting, and multi-error management.\npackage "
},
{
"path": "util/common/format.go",
"chars": 411,
"preview": "package common\n\nimport (\n\t\"fmt\"\n)\n\n// FormatTraffic formats traffic bytes into human-readable units (B, KB, MB, GB, TB, "
},
{
"path": "util/common/multi_error.go",
"chars": 663,
"preview": "package common\n\nimport (\n\t\"strings\"\n)\n\n// multiError represents a collection of errors.\ntype multiError []error\n\n// Erro"
},
{
"path": "util/crypto/crypto.go",
"chars": 598,
"preview": "// Package crypto provides cryptographic utilities for password hashing and verification.\npackage crypto\n\nimport (\n\t\"gol"
},
{
"path": "util/json_util/json.go",
"chars": 734,
"preview": "// Package json_util provides JSON utilities including a custom RawMessage type.\npackage json_util\n\nimport (\n\t\"errors\"\n)"
},
{
"path": "util/ldap/ldap.go",
"chars": 3243,
"preview": "package ldaputil\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\n\t\"github.com/go-ldap/ldap/v3\"\n)\n\ntype Config struct {\n\tHost strin"
},
{
"path": "util/random/random.go",
"chars": 1507,
"preview": "// Package random provides utilities for generating random strings and numbers.\npackage random\n\nimport (\n\t\"crypto/rand\"\n"
},
{
"path": "util/reflect_util/reflect.go",
"chars": 675,
"preview": "// Package reflect_util provides reflection utilities for working with struct fields and values.\npackage reflect_util\n\ni"
},
{
"path": "util/sys/psutil.go",
"chars": 331,
"preview": "// Package sys provides system utilities for monitoring network connections and CPU usage.\n// Platform-specific implemen"
},
{
"path": "util/sys/sys_darwin.go",
"chars": 1826,
"preview": "//go:build darwin\n// +build darwin\n\npackage sys\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/shi"
},
{
"path": "util/sys/sys_linux.go",
"chars": 3595,
"preview": "//go:build linux\n// +build linux\n\npackage sys\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syn"
},
{
"path": "util/sys/sys_windows.go",
"chars": 2661,
"preview": "//go:build windows\n// +build windows\n\npackage sys\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/shirou/"
},
{
"path": "web/assets/codemirror/fold/brace-fold.js",
"chars": 4475,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/fold/foldcode.js",
"chars": 4985,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/fold/foldgutter.css",
"chars": 435,
"preview": ".CodeMirror-foldmarker {\n color: blue;\n text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1p"
},
{
"path": "web/assets/codemirror/fold/foldgutter.js",
"chars": 5539,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/hint/javascript-hint.js",
"chars": 6854,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/javascript.js",
"chars": 38893,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/jshint.js",
"chars": 1284243,
"preview": "/*! 2.13.2 */\nvar JSHINT;\nif (typeof window === 'undefined') window = {};\n(function () {\nvar require;\nrequire=(function "
},
{
"path": "web/assets/codemirror/jsonlint.js",
"chars": 8775,
"preview": "var jsonlint=function(){var a=!0,b=!1,c={},d=function(){var a={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,S"
},
{
"path": "web/assets/codemirror/lint/javascript-lint.js",
"chars": 2161,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/codemirror/lint/lint.css",
"chars": 3023,
"preview": "/* The lint marker gutter */\n.CodeMirror-lint-markers {\n width: 16px;\n}\n\n.CodeMirror-lint-tooltip {\n background-color:"
},
{
"path": "web/assets/codemirror/lint/lint.js",
"chars": 9841,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/"
},
{
"path": "web/assets/js/axios-init.js",
"chars": 915,
"preview": "axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';\naxios.defaults.headers"
},
{
"path": "web/assets/js/model/dbinbound.js",
"chars": 3635,
"preview": "class DBInbound {\n\n constructor(data) {\n this.id = 0;\n this.userId = 0;\n this.up = 0;\n th"
},
{
"path": "web/assets/js/model/inbound.js",
"chars": 83363,
"preview": "const Protocols = {\n VMESS: 'vmess',\n VLESS: 'vless',\n TROJAN: 'trojan',\n SHADOWSOCKS: 'shadowsocks',\n TU"
},
{
"path": "web/assets/js/model/outbound.js",
"chars": 49454,
"preview": "const Protocols = {\n Freedom: \"freedom\",\n Blackhole: \"blackhole\",\n DNS: \"dns\",\n VMess: \"vmess\",\n VLESS: \""
},
{
"path": "web/assets/js/model/reality_targets.js",
"chars": 1153,
"preview": "// List of popular services for VLESS Reality Target/SNI randomization\r\nconst REALITY_TARGETS = [\r\n { target: 'www.ap"
},
{
"path": "web/assets/js/model/setting.js",
"chars": 2689,
"preview": "class AllSetting {\n\n constructor(data) {\n this.webListen = \"\";\n this.webDomain = \"\";\n this.webPo"
},
{
"path": "web/assets/js/subscription.js",
"chars": 5778,
"preview": "(function () {\n // Vue app for Subscription page\n const el = document.getElementById('subscription-data');\n if (!el) "
},
{
"path": "web/assets/js/util/index.js",
"chars": 27008,
"preview": "class Msg {\n constructor(success = false, msg = \"\", obj = null) {\n this.success = success;\n this.msg = "
},
{
"path": "web/assets/js/websocket.js",
"chars": 4280,
"preview": "/**\n * WebSocket client for real-time updates\n */\nclass WebSocketClient {\n constructor(basePath = '') {\n this.basePa"
},
{
"path": "web/controller/api.go",
"chars": 1561,
"preview": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/web/service\"\n\t\"github.com/mhsanaei/3x-ui/v2/web"
},
{
"path": "web/controller/base.go",
"chars": 1407,
"preview": "// Package controller provides HTTP request handlers and controllers for the 3x-ui web management panel.\n// It handles r"
},
{
"path": "web/controller/inbound.go",
"chars": 14095,
"preview": "package controller\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/database/model\"\n"
},
{
"path": "web/controller/index.go",
"chars": 4154,
"preview": "package controller\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"text/template\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n\t\"githu"
},
{
"path": "web/controller/server.go",
"chars": 10812,
"preview": "package controller\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/web/global\""
},
{
"path": "web/controller/setting.go",
"chars": 4096,
"preview": "package controller\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/util/crypto\"\n\t\"github.com/mhsanaei/3x-ui/"
},
{
"path": "web/controller/util.go",
"chars": 2495,
"preview": "package controller\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/config\"\n\t\"github.com/mhsanaei"
},
{
"path": "web/controller/websocket.go",
"chars": 5541,
"preview": "package controller\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/mhsanaei/3x-ui/v2/lo"
},
{
"path": "web/controller/xray_setting.go",
"chars": 5548,
"preview": "package controller\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/util/common\"\n\t\"github.com/mhsanaei/3x-ui/v"
},
{
"path": "web/controller/xui.go",
"chars": 1486,
"preview": "package controller\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n)\n\n// XUIController is the main controller for the X-UI panel, "
},
{
"path": "web/entity/entity.go",
"chars": 10655,
"preview": "// Package entity defines data structures and entities used by the web layer of the 3x-ui panel.\npackage entity\n\nimport "
},
{
"path": "web/global/global.go",
"chars": 1151,
"preview": "// Package global provides global variables and interfaces for accessing web and subscription servers.\npackage global\n\ni"
},
{
"path": "web/global/hashStorage.go",
"chars": 2111,
"preview": "package global\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"regexp\"\n\t\"sync\"\n\t\"time\"\n)\n\n// HashEntry represents a stored has"
},
{
"path": "web/html/common/page.html",
"chars": 3034,
"preview": "{{ define \"page/head_start\" }}\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"renderer\" content=\"w"
},
{
"path": "web/html/component/aClientTable.html",
"chars": 15295,
"preview": "{{define \"component/aClientTable\"}}\n<template slot=\"actions\" slot-scope=\"text, client, index\">\n <a-tooltip>\n <templa"
},
{
"path": "web/html/component/aCustomStatistic.html",
"chars": 867,
"preview": "{{define \"component/customStatistic\"}}\n<template>\n <a-statistic :title=\"title\" :value=\"value\">\n <template #pre"
},
{
"path": "web/html/component/aPersianDatepicker.html",
"chars": 2449,
"preview": "{{define \"component/persianDatepickerTemplate\"}}\n<template>\n <div>\n <a-input :value=\"value\" type=\"text\" v-mode"
},
{
"path": "web/html/component/aSettingListItem.html",
"chars": 1428,
"preview": "{{define \"component/settingListItem\"}}\n<a-list-item :style=\"{ padding: padding }\">\n <a-row :gutter=\"[8,16]\">\n "
},
{
"path": "web/html/component/aSidebar.html",
"chars": 3860,
"preview": "{{define \"component/sidebar/content\"}}\n<template>\n <div class=\"ant-sidebar\">\n <a-layout-sider :theme=\"themeSwi"
},
{
"path": "web/html/component/aTableSortable.html",
"chars": 6737,
"preview": "{{define \"component/sortableTableTrigger\"}}\n<a-icon type=\"drag\" class=\"sortable-icon\" :style=\"{ cursor: 'move' }\" @mouse"
},
{
"path": "web/html/component/aThemeSwitch.html",
"chars": 4850,
"preview": "{{define \"component/themeSwitchTemplate\"}}\n<template>\n <a-menu :theme=\"themeSwitcher.currentTheme\" mode=\"inline\" select"
},
{
"path": "web/html/form/client.html",
"chars": 8352,
"preview": "{{define \"form/client\"}}\n<a-form layout=\"horizontal\" v-if=\"client\" :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper"
},
{
"path": "web/html/form/inbound.html",
"chars": 5923,
"preview": "{{define \"form/inbound\"}}\n<!-- base -->\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\"\n :wrapper-col=\"{ md: {sp"
},
{
"path": "web/html/form/outbound.html",
"chars": 37992,
"preview": "{{define \"form/outbound\"}}\n<!-- base -->\n<a-tabs :active-key=\"outModal.activeKey\"\n :style=\"{ padding: '0', backgroundCo"
},
{
"path": "web/html/form/protocol/dokodemo.html",
"chars": 1915,
"preview": "{{define \"form/tunnel\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <a-f"
},
{
"path": "web/html/form/protocol/http.html",
"chars": 1216,
"preview": "{{define \"form/http\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <table :"
},
{
"path": "web/html/form/protocol/shadowsocks.html",
"chars": 2506,
"preview": "{{define \"form/shadowsocks\"}}\n<template v-if=\"inbound.isSSMultiUser\">\n <a-collapse activeKey=\"0\" v-for=\"(client, inde"
},
{
"path": "web/html/form/protocol/socks.html",
"chars": 1690,
"preview": "{{define \"form/mixed\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <a-form"
},
{
"path": "web/html/form/protocol/trojan.html",
"chars": 2107,
"preview": "{{define \"form/trojan\"}}\n<a-collapse activeKey=\"0\" v-for=\"(client, index) in inbound.settings.trojans.slice(0,1)\" v-if=\""
},
{
"path": "web/html/form/protocol/tun.html",
"chars": 1591,
"preview": "{{define \"form/tun\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\"\n :wrapper-col=\"{ md: {span:14} }\">\n <a-"
},
{
"path": "web/html/form/protocol/vless.html",
"chars": 5724,
"preview": "{{define \"form/vless\"}}\n<a-collapse activeKey=\"0\" v-for=\"(client, index) in inbound.settings.vlesses.slice(0,1)\" v-if=\"!"
},
{
"path": "web/html/form/protocol/vmess.html",
"chars": 964,
"preview": "{{define \"form/vmess\"}}\n<a-collapse activeKey=\"0\" v-for=\"(client, index) in inbound.settings.vmesses.slice(0,1)\" v-if=\"!"
},
{
"path": "web/html/form/protocol/wireguard.html",
"chars": 3361,
"preview": "{{define \"form/wireguard\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <a-"
},
{
"path": "web/html/form/reality_settings.html",
"chars": 3727,
"preview": "{{define \"form/realitySettings\"}}\n<template>\n <a-form-item label='Show'>\n <a-switch v-model=\"inbound.stream.re"
},
{
"path": "web/html/form/sniffing.html",
"chars": 1078,
"preview": "{{define \"form/sniffing\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <a-f"
},
{
"path": "web/html/form/stream/external_proxy.html",
"chars": 1705,
"preview": "{{define \"form/externalProxy\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n "
},
{
"path": "web/html/form/stream/stream_finalmask.html",
"chars": 4562,
"preview": "{{define \"form/streamFinalMask\"}}\n<a-divider :style=\"{ margin: '5px 0 0' }\"></a-divider>\n<a-form :colon=\"false\" :label-c"
},
{
"path": "web/html/form/stream/stream_grpc.html",
"chars": 521,
"preview": "{{define \"form/streamGRPC\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n "
},
{
"path": "web/html/form/stream/stream_httpupgrade.html",
"chars": 1422,
"preview": "{{define \"form/streamHTTPUpgrade\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }"
},
{
"path": "web/html/form/stream/stream_kcp.html",
"chars": 1276,
"preview": "{{define \"form/streamKCP\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\"\n :wrapper-col=\"{ md: {span:14} }\">\n "
},
{
"path": "web/html/form/stream/stream_settings.html",
"chars": 1850,
"preview": "{{define \"form/streamSettings\"}}\n<!-- select stream network -->\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\"\n "
},
{
"path": "web/html/form/stream/stream_sockopt.html",
"chars": 4246,
"preview": "{{define \"form/streamSockopt\"}}\n<a-divider :style=\"{ margin: '5px 0 0' }\"></a-divider>\n<a-form :colon=\"false\" :label-col"
},
{
"path": "web/html/form/stream/stream_tcp.html",
"chars": 4160,
"preview": "{{define \"form/streamTCP\"}}\n<!-- tcp type -->\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {"
},
{
"path": "web/html/form/stream/stream_ws.html",
"chars": 1515,
"preview": "{{define \"form/streamWS\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\" :wrapper-col=\"{ md: {span:14} }\">\n <a-f"
},
{
"path": "web/html/form/stream/stream_xhttp.html",
"chars": 7584,
"preview": "{{define \"form/streamXHTTP\"}}\n<a-form :colon=\"false\" :label-col=\"{ md: {span:8} }\"\n :wrapper-col=\"{ md: {span:14} }\">"
},
{
"path": "web/html/form/tls_settings.html",
"chars": 6699,
"preview": "{{define \"form/tlsSettings\"}}\n<!-- tls enable -->\n<a-form v-if=\"inbound.canEnableTls()\" :colon=\"false\"\n :label-col=\"{ m"
},
{
"path": "web/html/inbounds.html",
"chars": 81810,
"preview": "{{ template \"page/head_start\" .}}\n{{ template \"page/head_end\" .}}\n\n{{ template \"page/body_start\" .}}\n<a-layout id=\"app\" "
},
{
"path": "web/html/index.html",
"chars": 49143,
"preview": "{{ template \"page/head_start\" .}}\n{{ template \"page/head_end\" .}}\n\n{{ template \"page/body_start\" .}}\n<a-layout id=\"app\" "
},
{
"path": "web/html/login.html",
"chars": 9718,
"preview": "{{ template \"page/head_start\" .}}\n{{ template \"page/head_end\" .}}\n\n{{ template \"page/body_start\" .}}\n<a-layout id=\"app\" "
},
{
"path": "web/html/modals/client_bulk_modal.html",
"chars": 11730,
"preview": "{{define \"modals/clientsBulkModal\"}}\n<a-modal id=\"client-bulk-modal\" v-model=\"clientsBulkModal.visible\" :title=\"clientsB"
},
{
"path": "web/html/modals/client_modal.html",
"chars": 7211,
"preview": "{{define \"modals/clientsModal\"}}\n<a-modal id=\"client-modal\" v-model=\"clientModal.visible\" :title=\"clientModal.title\" @ok"
},
{
"path": "web/html/modals/dns_presets_modal.html",
"chars": 2842,
"preview": "{{define \"modals/dnsPresetsModal\"}}\n<a-modal id=\"dnsPresets-modal\" v-model=\"dnsPresetsModal.visible\" :title=\"dnsPresetsM"
},
{
"path": "web/html/modals/inbound_info_modal.html",
"chars": 31008,
"preview": "{{define \"modals/inboundInfoModal\"}}\n<a-modal id=\"inbound-info-modal\" v-model=\"infoModal.visible\"\n title='{{ i18n \"page"
},
{
"path": "web/html/modals/inbound_modal.html",
"chars": 13991,
"preview": "{{define \"modals/inboundModal\"}}\n<a-modal id=\"inbound-modal\" v-model=\"inModal.visible\" :title=\"inModal.title\" :dialog-st"
},
{
"path": "web/html/modals/prompt_modal.html",
"chars": 2084,
"preview": "{{define \"modals/promptModal\"}}\n<a-modal id=\"prompt-modal\" v-model=\"promptModal.visible\" :title=\"promptModal.title\"\n "
},
{
"path": "web/html/modals/qrcode_modal.html",
"chars": 9777,
"preview": "{{define \"modals/qrcodeModal\"}}\n<a-modal id=\"qrcode-modal\" v-model=\"qrModal.visible\" :closable=\"true\" :class=\"themeSwitc"
},
{
"path": "web/html/modals/text_modal.html",
"chars": 1635,
"preview": "{{define \"modals/textModal\"}}\n<a-modal id=\"text-modal\" v-model=\"txtModal.visible\" :title=\"txtModal.title\" :closable=\"tru"
},
{
"path": "web/html/modals/two_factor_modal.html",
"chars": 4313,
"preview": "{{define \"modals/twoFactorModal\"}}\n<a-modal id=\"two-factor-modal\" v-model=\"twoFactorModal.visible\" :title=\"twoFactorModa"
},
{
"path": "web/html/modals/warp_modal.html",
"chars": 10732,
"preview": "{{define \"modals/warpModal\"}}\n<a-modal id=\"warp-modal\" v-model=\"warpModal.visible\" title=\"Cloudflare WARP\"\n :con"
},
{
"path": "web/html/modals/xray_balancer_modal.html",
"chars": 4837,
"preview": "{{define \"modals/balancerModal\"}}\n<a-modal \n id=\"balancer-modal\"\n v-model=\"balancerModal.visible\"\n :title=\"bala"
},
{
"path": "web/html/modals/xray_dns_modal.html",
"chars": 4661,
"preview": "{{define \"modals/dnsModal\"}}\n<a-modal id=\"dns-modal\" v-model=\"dnsModal.visible\" :title=\"dnsModal.title\" @ok=\"dnsModal.ok"
},
{
"path": "web/html/modals/xray_fakedns_modal.html",
"chars": 1681,
"preview": "{{define \"modals/fakednsModal\"}}\n<a-modal id=\"fakedns-modal\" v-model=\"fakednsModal.visible\" :title=\"fakednsModal.title\" "
},
{
"path": "web/html/modals/xray_outbound_modal.html",
"chars": 4364,
"preview": "{{define \"modals/outModal\"}}\n<a-modal id=\"out-modal\" v-model=\"outModal.visible\" :title=\"outModal.title\" @ok=\"outModal.ok"
},
{
"path": "web/html/modals/xray_reverse_modal.html",
"chars": 6260,
"preview": "{{define \"modals/reverseModal\"}}\n<a-modal id=\"reverse-modal\" v-model=\"reverseModal.visible\" :title=\"reverseModal.title\" "
},
{
"path": "web/html/modals/xray_rule_modal.html",
"chars": 10281,
"preview": "{{define \"modals/ruleModal\"}}\n<a-modal id=\"rule-modal\" v-model=\"ruleModal.visible\" :title=\"ruleModal.title\" @ok=\"ruleMod"
},
{
"path": "web/html/settings/panel/general.html",
"chars": 15001,
"preview": "{{define \"settings/panel/general\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='{{ i18n \"p"
},
{
"path": "web/html/settings/panel/security.html",
"chars": 2207,
"preview": "{{define \"settings/panel/security\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='{{ i18n \""
},
{
"path": "web/html/settings/panel/subscription/general.html",
"chars": 8118,
"preview": "{{define \"settings/panel/subscription/general\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" heade"
},
{
"path": "web/html/settings/panel/subscription/json.html",
"chars": 11595,
"preview": "{{define \"settings/panel/subscription/json\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='"
},
{
"path": "web/html/settings/panel/subscription/subpage.html",
"chars": 15493,
"preview": "{{ template \"page/head_start\" .}}\n<script src=\"{{ .base_path }}assets/moment/moment.min.js\"></script>\n<script src=\"{{ .b"
},
{
"path": "web/html/settings/panel/telegram.html",
"chars": 4795,
"preview": "{{define \"settings/panel/telegram\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='{{ i18n \""
},
{
"path": "web/html/settings/xray/advanced.html",
"chars": 938,
"preview": "{{define \"settings/xray/advanced\"}}\n<a-space direction=\"vertical\" size=\"small\" :style=\"{ marginTop: '20px' }\">\n <a-li"
},
{
"path": "web/html/settings/xray/balancers.html",
"chars": 3308,
"preview": "{{define \"settings/xray/balancers\"}}\n<template v-if=\"balancersData.length > 0\">\n <a-space direction=\"vertical\" size=\""
},
{
"path": "web/html/settings/xray/basics.html",
"chars": 15421,
"preview": "{{define \"settings/xray/basics\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='{{ i18n \"pag"
},
{
"path": "web/html/settings/xray/dns.html",
"chars": 9887,
"preview": "{{define \"settings/xray/dns\"}}\n<a-collapse default-active-key=\"1\">\n <a-collapse-panel key=\"1\" header='{{ i18n \"pages."
},
{
"path": "web/html/settings/xray/outbounds.html",
"chars": 6315,
"preview": "{{define \"settings/xray/outbounds\"}}\n<a-space direction=\"vertical\" size=\"middle\">\n <a-row>\n <a-col :xs=\"12\" :s"
},
{
"path": "web/html/settings/xray/reverse.html",
"chars": 1975,
"preview": "{{define \"settings/xray/reverse\"}}\n<template v-if=\"reverseData.length > 0\">\n <a-space direction=\"vertical\" size=\"midd"
},
{
"path": "web/html/settings/xray/routing.html",
"chars": 6849,
"preview": "{{define \"settings/xray/routing\"}}\n<a-space direction=\"vertical\" size=\"middle\">\n <a-button type=\"primary\" icon=\"plus\""
},
{
"path": "web/html/settings.html",
"chars": 24411,
"preview": "{{ template \"page/head_start\" .}}\n{{ template \"page/head_end\" .}}\n\n{{ template \"page/body_start\" .}}\n<a-layout id=\"app\" "
},
{
"path": "web/html/xray.html",
"chars": 64792,
"preview": "{{ template \"page/head_start\" .}}\n<link rel=\"stylesheet\"\n href=\"{{ .base_path }}assets/codemirror/codemirror.min.css?{{"
},
{
"path": "web/job/check_client_ip_job.go",
"chars": 10197,
"preview": "package job\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"sort\"\n\t\"time\"\n\n\t\"gi"
},
{
"path": "web/job/check_cpu_usage.go",
"chars": 1118,
"preview": "package job\n\nimport (\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/web/service\"\n\n\t\"github.com/shirou/gopsutil/v4/c"
},
{
"path": "web/job/check_hash_storage.go",
"chars": 611,
"preview": "package job\n\nimport (\n\t\"github.com/mhsanaei/3x-ui/v2/web/service\"\n)\n\n// CheckHashStorageJob periodically cleans up expir"
},
{
"path": "web/job/check_xray_running_job.go",
"chars": 1032,
"preview": "// Package job provides background job implementations for the 3x-ui web panel,\n// including traffic monitoring, system "
},
{
"path": "web/job/clear_logs_job.go",
"chars": 2142,
"preview": "package job\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n\t\"github.com/mhsanaei/3x-ui/v"
},
{
"path": "web/job/ldap_sync_job.go",
"chars": 9714,
"preview": "package job\n\nimport (\n\t\"time\"\n\n\t\"strings\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/database/model\"\n\t\"github.com/mhsanaei/3x-ui/v2"
},
{
"path": "web/job/periodic_traffic_reset_job.go",
"chars": 1717,
"preview": "package job\n\nimport (\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n\t\"github.com/mhsanaei/3x-ui/v2/web/service\"\n)\n\n// Period re"
},
{
"path": "web/job/stats_notify_job.go",
"chars": 758,
"preview": "package job\n\nimport (\n\t\"github.com/mhsanaei/3x-ui/v2/web/service\"\n)\n\n// LoginStatus represents the status of a login att"
},
{
"path": "web/job/xray_traffic_job.go",
"chars": 3860,
"preview": "package job\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n\t\"github.com/mhsanaei/3x-ui/v2/web/servic"
},
{
"path": "web/locale/locale.go",
"chars": 5126,
"preview": "// Package locale provides internationalization (i18n) support for the 3x-ui web panel,\n// including translation loading"
},
{
"path": "web/middleware/domainValidator.go",
"chars": 862,
"preview": "// Package middleware provides HTTP middleware functions for the 3x-ui web panel,\n// including domain validation and URL"
},
{
"path": "web/middleware/redirect.go",
"chars": 897,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// RedirectMiddleware returns a Gin "
},
{
"path": "web/network/auto_https_conn.go",
"chars": 1923,
"preview": "// Package network provides network utilities for the 3x-ui web panel,\n// including automatic HTTP to HTTPS redirection "
},
{
"path": "web/network/auto_https_listener.go",
"chars": 843,
"preview": "package network\n\nimport \"net\"\n\n// AutoHttpsListener wraps a net.Listener to provide automatic HTTPS redirection.\n// It r"
},
{
"path": "web/service/config.json",
"chars": 1565,
"preview": "{\n \"log\": {\n \"access\": \"none\",\n \"dnsLog\": false,\n \"error\": \"\",\n \"loglevel\": \"warning\",\n \"maskAddress\": \""
},
{
"path": "web/service/inbound.go",
"chars": 68147,
"preview": "// Package service provides business logic services for the 3x-ui web panel,\n// including inbound/outbound management, u"
},
{
"path": "web/service/outbound.go",
"chars": 11845,
"preview": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "web/service/panel.go",
"chars": 570,
"preview": "package service\n\nimport (\n\t\"os\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n)\n\n// PanelService provides b"
},
{
"path": "web/service/server.go",
"chars": 33930,
"preview": "package service\n\nimport (\n\t\"archive/zip\"\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"mime/multipart\"\n\t\"ne"
},
{
"path": "web/service/setting.go",
"chars": 21486,
"preview": "package service\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n"
},
{
"path": "web/service/tgbot.go",
"chars": 144838,
"preview": "package service\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"html"
},
{
"path": "web/service/user.go",
"chars": 3960,
"preview": "package service\n\nimport (\n\t\"errors\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/database\"\n\t\"github.com/mhsanaei/3x-ui/v2/database/mo"
},
{
"path": "web/service/warp.go",
"chars": 4208,
"preview": "package service\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/log"
},
{
"path": "web/service/xray.go",
"chars": 7102,
"preview": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"runtime\"\n\t\"sync\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/logger\"\n\t\"github"
},
{
"path": "web/service/xray_setting.go",
"chars": 844,
"preview": "package service\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\n\t\"github.com/mhsanaei/3x-ui/v2/util/common\"\n\t\"github.com/mhsanaei"
},
{
"path": "web/session/session.go",
"chars": 2017,
"preview": "// Package session provides session management utilities for the 3x-ui web panel.\n// It handles user authentication stat"
},
{
"path": "web/translation/translate.ar_EG.toml",
"chars": 32682,
"preview": "\"username\" = \"اسم المستخدم\"\n\"password\" = \"الباسورد\"\n\"login\" = \"تسجيل الدخول\"\n\"confirm\" = \"تأكيد\"\n\"cancel\" = \"إلغاء\"\n\"clo"
},
{
"path": "web/translation/translate.en_US.toml",
"chars": 34990,
"preview": "\"username\" = \"Username\"\n\"password\" = \"Password\"\n\"login\" = \"Log In\"\n\"confirm\" = \"Confirm\"\n\"cancel\" = \"Cancel\"\n\"close\" = \""
},
{
"path": "web/translation/translate.es_ES.toml",
"chars": 40466,
"preview": "\"username\" = \"Nombre de Usuario\"\r\n\"password\" = \"Contraseña\"\r\n\"login\" = \"Acceder\"\r\n\"confirm\" = \"Confirmar\"\r\n\"cancel\" = \"C"
},
{
"path": "web/translation/translate.fa_IR.toml",
"chars": 34314,
"preview": "\"username\" = \"نامکاربری\"\n\"password\" = \"رمزعبور\"\n\"login\" = \"ورود\"\n\"confirm\" = \"تایید\"\n\"cancel\" = \"انصراف\"\n\"close\" = \"بست"
},
{
"path": "web/translation/translate.id_ID.toml",
"chars": 36122,
"preview": "\"username\" = \"Nama Pengguna\"\n\"password\" = \"Kata Sandi\"\n\"login\" = \"Masuk\"\n\"confirm\" = \"Konfirmasi\"\n\"cancel\" = \"Batal\"\n\"cl"
},
{
"path": "web/translation/translate.ja_JP.toml",
"chars": 27162,
"preview": "\"username\" = \"ユーザー名\"\n\"password\" = \"パスワード\"\n\"login\" = \"ログイン\"\n\"confirm\" = \"確認\"\n\"cancel\" = \"キャンセル\"\n\"close\" = \"閉じる\"\n\"create\" "
},
{
"path": "web/translation/translate.pt_BR.toml",
"chars": 37327,
"preview": "\"username\" = \"Nome de Usuário\"\n\"password\" = \"Senha\"\n\"login\" = \"Entrar\"\n\"confirm\" = \"Confirmar\"\n\"cancel\" = \"Cancelar\"\n\"cl"
},
{
"path": "web/translation/translate.ru_RU.toml",
"chars": 36921,
"preview": "\"username\" = \"Имя пользователя\"\n\"password\" = \"Пароль\"\n\"login\" = \"Войти\"\n\"confirm\" = \"Подтвердить\"\n\"cancel\" = \"Отмена\"\n\"c"
},
{
"path": "web/translation/translate.tr_TR.toml",
"chars": 36216,
"preview": "\"username\" = \"Kullanıcı Adı\"\n\"password\" = \"Şifre\"\n\"login\" = \"Giriş Yap\"\n\"confirm\" = \"Onayla\"\n\"cancel\" = \"İptal\"\n\"close\" "
},
{
"path": "web/translation/translate.uk_UA.toml",
"chars": 37172,
"preview": "\"username\" = \"Ім'я користувача\"\n\"password\" = \"Пароль\"\n\"login\" = \"Увійти\"\n\"confirm\" = \"Підтвердити\"\n\"cancel\" = \"Скасувати"
},
{
"path": "web/translation/translate.vi_VN.toml",
"chars": 37807,
"preview": "\"username\" = \"Tên người dùng\"\r\n\"password\" = \"Mật khẩu\"\r\n\"login\" = \"Đăng nhập\"\r\n\"confirm\" = \"Xác nhận\"\r\n\"cancel\" = \"Hủy b"
},
{
"path": "web/translation/translate.zh_CN.toml",
"chars": 23507,
"preview": "\"username\" = \"用户名\"\n\"password\" = \"密码\"\n\"login\" = \"登录\"\n\"confirm\" = \"确定\"\n\"cancel\" = \"取消\"\n\"close\" = \"关闭\"\n\"create\" = \"创建\"\n\"upd"
},
{
"path": "web/translation/translate.zh_TW.toml",
"chars": 23582,
"preview": "\"username\" = \"使用者名稱\"\n\"password\" = \"密碼\"\n\"login\" = \"登入\"\n\"confirm\" = \"確定\"\n\"cancel\" = \"取消\"\n\"close\" = \"關閉\"\n\"create\" = \"建立\"\n\"u"
},
{
"path": "web/web.go",
"chars": 12655,
"preview": "// Package web provides the main web server implementation for the 3x-ui panel,\n// including HTTP/HTTPS serving, routing"
},
{
"path": "web/websocket/hub.go",
"chars": 9435,
"preview": "// Package websocket provides WebSocket hub for real-time updates and notifications.\npackage websocket\n\nimport (\n\t\"conte"
},
{
"path": "web/websocket/notifier.go",
"chars": 2030,
"preview": "// Package websocket provides WebSocket hub for real-time updates and notifications.\npackage websocket\n\nimport (\n\t\"githu"
},
{
"path": "windows_files/readme.txt",
"chars": 374,
"preview": "you can't install fail2ban on windows\nwe don't have bash menu for windows\nif you forgot your password you need to check "
},
{
"path": "x-ui.rc",
"chars": 286,
"preview": "#!/sbin/openrc-run\n\ncommand=\"/usr/local/x-ui/x-ui\"\ncommand_background=true\npidfile=\"/run/x-ui.pid\"\ndescription=\"x-ui Ser"
},
{
"path": "x-ui.service.arch",
"chars": 337,
"preview": "[Unit]\nDescription=x-ui Service\nAfter=network.target\nWants=network.target\n\n[Service]\nEnvironmentFile=-/etc/conf.d/x-ui\nE"
},
{
"path": "x-ui.service.debian",
"chars": 342,
"preview": "[Unit]\nDescription=x-ui Service\nAfter=network.target\nWants=network.target\n\n[Service]\nEnvironmentFile=-/etc/default/x-ui\n"
},
{
"path": "x-ui.service.rhel",
"chars": 344,
"preview": "[Unit]\nDescription=x-ui Service\nAfter=network.target\nWants=network.target\n\n[Service]\nEnvironmentFile=-/etc/sysconfig/x-u"
},
{
"path": "x-ui.sh",
"chars": 79888,
"preview": "#!/bin/bash\n\nred='\\033[0;31m'\ngreen='\\033[0;32m'\nblue='\\033[0;34m'\nyellow='\\033[0;33m'\nplain='\\033[0m'\n\n#Add some basic "
},
{
"path": "xray/api.go",
"chars": 8808,
"preview": "// Package xray provides integration with the Xray proxy core.\n// It includes API client functionality, configuration ma"
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the MHSanaei/3x-ui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 206 files (3.3 MB), approximately 871.0k tokens, and a symbol index with 2091 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.