From 78cd6539bb127324c52a10e2ed58faa955a2bab6 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Wed, 4 Jun 2025 18:36:16 +0000
Subject: [PATCH] Jules was unable to complete the task in time. Please review
the work done so far and provide feedback for Jules to continue.
---
Dockerfile | 87 +-
Dockerfile.backend | 48 +
README.md | 642 +---
docker-compose.yml | 52 +-
install.sh | 496 +--
new-frontend/next.config.js | 8 +
new-frontend/next.config.ts | 7 -
.../src/app/inbounds/[id]/clients/page.tsx | 10 +-
.../inbounds/[id]/manage-clients-client.tsx | 299 ++
.../edit/[id]/edit-inbound-client.tsx | 97 +
.../src/app/inbounds/edit/[id]/page.tsx | 6 +-
new-frontend/src/services/api.ts | 61 +-
new-frontend/tsconfig.json | 4 +-
new-frontend/yarn.lock | 3028 -----------------
new_install_sh_components.txt | 279 ++
15 files changed, 1268 insertions(+), 3856 deletions(-)
create mode 100644 Dockerfile.backend
create mode 100644 new-frontend/next.config.js
delete mode 100644 new-frontend/next.config.ts
create mode 100644 new-frontend/src/app/inbounds/[id]/manage-clients-client.tsx
create mode 100644 new-frontend/src/app/inbounds/edit/[id]/edit-inbound-client.tsx
delete mode 100644 new-frontend/yarn.lock
create mode 100644 new_install_sh_components.txt
diff --git a/Dockerfile b/Dockerfile
index 4a15f98d..52453541 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,54 +1,57 @@
-# ========================================================
-# Stage: Builder
-# ========================================================
-FROM golang:1.24-alpine AS builder
+# Stage 1: Build the Next.js application
+FROM node:20-alpine AS builder
+
+# Set working directory
WORKDIR /app
-ARG TARGETARCH
-RUN apk --no-cache --update add \
- build-base \
- gcc \
- wget \
- unzip
+# Install dependencies
+# Copy package.json and yarn.lock (or package-lock.json if using npm)
+COPY package.json yarn.lock ./
+# Ensure corepack is enabled to use yarn specified in package.json
+RUN corepack enable
+RUN yarn install --frozen-lockfile --network-timeout 600000
+# Copy the rest of the application source code
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"
+# Build the Next.js application
+# NEXT_PUBLIC_API_BASE_URL can be set here if it's fixed,
+# or passed as an ARG during docker build, or as an ENV var at runtime.
+# For flexibility, runtime ENV var is often preferred.
+# ARG NEXT_PUBLIC_API_BASE_URL
+# ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}
+RUN yarn build
+
+# Stage 2: Production environment
+FROM node:20-alpine AS runner
-# ========================================================
-# 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
+# Set environment variables
+# ENV NODE_ENV=production # Already set by `next start`
+# NEXT_PUBLIC_API_BASE_URL will be set at runtime via docker-compose or run command
+# ENV PORT=3000 # Next.js default port is 3000, can be overridden
-COPY --from=builder /app/build/ /app/
-COPY --from=builder /app/DockerEntrypoint.sh /app/
-COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
+# Copy built assets from the builder stage
+# This includes the .next folder (production build) and public folder.
+# For a standard Next.js build (not standalone or static export),
+# we also need node_modules and package.json to run `next start`.
+COPY --from=builder /app/.next ./.next
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/package.json ./package.json
+# If yarn.lock is needed for `yarn start` with specific versions, copy it too.
+# Usually for `yarn start` just package.json and production node_modules are needed.
+# For yarn, yarn.lock is good practice to ensure consistent prod dependencies if any are direct.
+COPY --from=builder /app/yarn.lock ./
+# Install production dependencies only
+# Ensure corepack is enabled
+RUN corepack enable
+RUN yarn install --production --frozen-lockfile --network-timeout 600000
-# 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
+# Expose port 3000 (default for Next.js)
+EXPOSE 3000
-RUN chmod +x \
- /app/DockerEntrypoint.sh \
- /app/x-ui \
- /usr/bin/x-ui
-
-ENV XUI_ENABLE_FAIL2BAN="true"
-VOLUME [ "/etc/x-ui" ]
-CMD [ "./x-ui" ]
-ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
+# The "start" script in package.json runs "next start"
+# This will serve the application from the .next folder.
+CMD ["yarn", "start"]
diff --git a/Dockerfile.backend b/Dockerfile.backend
new file mode 100644
index 00000000..70f94f7a
--- /dev/null
+++ b/Dockerfile.backend
@@ -0,0 +1,48 @@
+# Stage 1: Build the Go application
+FROM golang:1.21-alpine AS builder
+
+WORKDIR /app
+
+# Copy go.mod and go.sum and download dependencies
+COPY go.mod go.sum ./
+RUN go mod download
+
+# Copy the rest of the application source code
+COPY . .
+
+# Build the Go application
+# Assuming the main package is in the root and output is 'x-ui' or 'main'
+# The original entrypoint seems to be related to x-ui.sh or DockerEntrypoint.sh
+# We need to ensure the binary is built correctly.
+# For 3x-ui, the main.go seems to be the entry point.
+RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app/x-ui main.go
+
+# Stage 2: Production environment
+FROM alpine:latest
+
+WORKDIR /app
+
+# Copy the binary from the builder stage
+COPY --from=builder /app/x-ui /app/x-ui
+COPY --from=builder /app/x-ui.sh /app/x-ui.sh
+COPY --from=builder /app/DockerEntrypoint.sh /app/DockerEntrypoint.sh
+COPY --from=builder /app/config/name /app/config/name
+COPY --from=builder /app/config/version /app/config/version
+
+
+# Ensure necessary directories exist and have correct permissions if needed by the app
+# The original compose file mounts $PWD/db/:/etc/x-ui/ and $PWD/cert/:/root/cert/
+# So, these paths should be available or created by the entrypoint script.
+RUN mkdir -p /etc/x-ui && \
+ mkdir -p /root/cert && \
+ chmod +x /app/x-ui.sh /app/DockerEntrypoint.sh /app/x-ui
+
+# Expose default panel port (e.g., 2053, but this will be handled by docker-compose)
+# The original compose uses network_mode: host, so ports are directly from the app.
+# If we move away from network_mode: host, we'll need to EXPOSE the correct port here.
+# Let's assume the Go app listens on a port defined by an ENV or config, e.g., 2053
+EXPOSE 2053
+
+# Entrypoint
+ENTRYPOINT ["/app/DockerEntrypoint.sh"]
+CMD ["/app/x-ui"] # Default command if DockerEntrypoint.sh doesn't override
diff --git a/README.md b/README.md
index db5d6a18..4613530a 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
-**An Advanced Web Panel β’ Built on Xray Core**
+**An Advanced Web Panel β’ Built on Xray Core β’ With a Modern React/Next.js Frontend**
[](https://github.com/MHSanaei/3x-ui/releases)
[](#)
@@ -15,13 +15,13 @@
[](#)
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
-> **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment
+> **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes. Using it in a production environment is at your own risk.
**If this project is helpful to you, you may wish to give it a**:star2:
@@ -29,569 +29,155 @@
- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A`
- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv`
-## Install & Upgrade
+## β¨ New Frontend!
-```
-bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.6.0/install.sh)
-```
+This version of 3X-UI features a completely revamped frontend built with **React, Next.js, and Tailwind CSS**, offering a modern, responsive, and user-friendly experience. Key improvements include:
+- Enhanced User Interface (UI) and User Experience (UX).
+- Improved responsiveness for mobile and tablet devices.
+- Integrated Dark Mode.
+- Streamlined management of inbounds, clients, and settings.
-## Install legacy Version (we don't recommend)
+## π Installation (Docker Based - Recommended)
-To install your desired version, use following installation command. e.g., ver `v1.7.9`:
+This new version is designed to be run using Docker and Docker Compose for ease of installation, updates, and management.
+**Prerequisites:**
+- A Linux server (Ubuntu 22.04+, Debian 12+, CentOS 8+, Fedora 36+, or other compatible distributions).
+- Root or sudo privileges.
+- `curl` and `git` installed (the installation script will attempt to install them if missing).
+- Docker and Docker Compose plugin (the installation script will attempt to install them if missing).
+
+**Quick Install Command:**
+
+```bash
+bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh)
```
-VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
-```
+*(Note: The URL should point to the new `install.sh` in the `main` branch or a specific release tag once updated).*
+
+**Installation Steps Explained by the Script:**
+1. Checks for root privileges and essential tools (`curl`, `git`).
+2. Installs/Updates Docker and Docker Compose plugin if they are not present.
+3. Prompts you for an installation directory (default: `/opt/3x-ui-docker`).
+4. Clones (or updates an existing clone of) the 3x-ui repository into a source subdirectory (e.g., `3x-ui-source`).
+5. Navigates into the source directory.
+6. Creates necessary data directories (`db`, `cert`) for persistent storage.
+7. Prompts you for HOST ports for the new frontend (default: 3000) and the backend panel (default: 2053).
+8. Generates a `.env` file with these port configurations and sets up `NEXT_PUBLIC_API_BASE_URL` for communication between the frontend and backend containers.
+9. Runs `docker compose up -d --build --remove-orphans` to build the Docker images (for both backend and the new frontend) and start the services in detached mode.
+
+After installation, the new frontend panel should be accessible at `http://:[FRONTEND_PORT]` (e.g., `http://:3000`).
+
+## π³ Managing Services with Docker Compose
+
+Once installed, you can manage the panel services using standard `docker compose` commands from within the source directory (e.g., `/opt/3x-ui-docker/3x-ui-source`):
+
+- **Start services:** `docker compose up -d`
+- **Stop services:** `docker compose stop`
+- **Restart services:** `docker compose restart`
+- **View logs for backend:** `docker compose logs -f backend`
+- **View logs for frontend:** `docker compose logs -f frontend`
+- **Update (rebuild and restart):**
+ ```bash
+ git pull # Get latest source
+ docker compose up -d --build --remove-orphans # Rebuild images and restart
+ ```
+- **Uninstall:**
+ ```bash
+ docker compose down -v # Stops and removes containers, networks, and volumes
+ # Then remove the installation directory if desired
+ # cd .. && rm -rf /opt/3x-ui-docker
+ ```
+
+## βοΈ Panel Configuration after Install
+
+- **Initial Login:** Default credentials for the panel (if not changed during install script or via backend ENV) are usually `admin`/`admin`. Please change these immediately via the panel's "Settings" > "User Account" tab.
+- **API URL:** The `NEXT_PUBLIC_API_BASE_URL` for the frontend is configured in the `.env` file created by the `install.sh` script. It points to the backend service within the Docker network (e.g., `http://backend:2053`).
+- **Ports:** Host ports for frontend and backend are also defined in the `.env` file and used by `docker-compose.yml`.
+- All other panel settings can be configured through the web interface in the "Settings" section.
## SSL Certificate
- Click for SSL Certificate details
+ Click for SSL Certificate details (General Guidance)
-### ACME
+To secure your panel with an SSL certificate when using Docker:
-To manage SSL certificates using ACME:
-
-1. Ensure your domain is correctly resolved to the server.
-2. Run the `x-ui` command in the terminal, then choose `SSL Certificate Management`.
-3. You will be presented with the following options:
-
- - **Get SSL:** Obtain SSL certificates.
- - **Revoke:** Revoke existing SSL certificates.
- - **Force Renew:** Force renewal of SSL certificates.
- - **Show Existing Domains:** Display all domain certificates available on the server.
- - **Set Certificate Paths for the Panel:** Specify the certificate for your domain to be used by the panel.
-
-### Certbot
-
-To install and use Certbot:
-
-```sh
-apt-get install certbot -y
-certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
-certbot renew --dry-run
-```
-
-### Cloudflare
-
-The management script includes a built-in SSL certificate application for Cloudflare. To use this script to apply for a certificate, you need the following:
-
-- Cloudflare registered email
-- Cloudflare Global API Key
-- The domain name must be resolved to the current server through Cloudflare
-
-**How to get the Cloudflare Global API Key:**
-
-1. Run the `x-ui` command in the terminal, then choose `Cloudflare SSL Certificate`.
-2. Visit the link: [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens).
-3. Click on "View Global API Key" (see the screenshot below):
- 
-4. You may need to re-authenticate your account. After that, the API Key will be shown (see the screenshot below):
- 
-
-When using, just enter your `domain name`, `email`, and `API KEY`. The diagram is as follows:
- 
+1. **Obtain SSL Certificates:** Use tools like Certbot (running on your host machine or in a separate Docker container) to obtain SSL certificates for your domain. Make sure your domain correctly resolves to your server's IP address.
+2. **Mount Certificates into Backend Container:**
+ * Place your certificate (`fullchain.pem` or `cert.pem`) and private key (`privkey.pem` or `key.pem`) files in a directory on your host machine (e.g., the `cert` directory created by `install.sh` at `$INSTALL_DIR/3x-ui-source/cert/`).
+ * The `docker-compose.yml` already mounts `./cert/:/root/cert/` into the backend container.
+3. **Configure Panel Settings:**
+ * In the 3X-UI panel settings (under "Panel Settings" tab), set:
+ * **Panel Domain:** Your domain (e.g., `panel.yourdomain.com`).
+ * **SSL Certificate File Path:** `/root/cert/your_cert.pem` (adjust filename accordingly).
+ * **SSL Key File Path:** `/root/cert/your_key.pem` (adjust filename accordingly).
+ * Save settings and restart the panel (the "Restart Panel" button in settings, or `docker compose restart backend`).
+4. **Access via HTTPS:** You should now be able to access your panel via `https://yourdomain.com:[FRONTEND_PORT]`. You might also need a reverse proxy (like Nginx or Traefik) in front of the frontend Docker container to handle SSL termination directly for the frontend port if desired, or configure the Next.js server itself for HTTPS if running standalone with more complex setup (not covered by default Dockerfile).
+**Note:** The old ACME script (`x-ui ... SSL Certificate Management`) run directly from the binary might not work as expected within the Docker setup without further adaptation. Managing SSL certificates on the host or via a dedicated SSL reverse proxy container is generally recommended with Dockerized applications.
-## Manual Install & Upgrade
-
+## Old Installation Methods (Legacy - Not Recommended for New Frontend)
- Click for manual install details
+ Click for legacy installation details (references old version)
-#### Usage
+The following installation methods refer to the previous version of 3X-UI with the older frontend. For the new React/Next.js frontend, please use the Docker-based installation described above.
-1. To download the latest version of the compressed package directly to your server, run the following command:
-
-```sh
-ARCH=$(uname -m)
-case "${ARCH}" in
- x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
- i*86 | x86) XUI_ARCH="386" ;;
- armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
- armv7* | armv7) XUI_ARCH="armv7" ;;
- armv6* | armv6) XUI_ARCH="armv6" ;;
- armv5* | armv5) XUI_ARCH="armv5" ;;
- s390x) echo 's390x' ;;
- *) XUI_ARCH="amd64" ;;
-esac
-
-
-wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
+```
+# Legacy main install command
+bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.6.0/install.sh)
```
-2. Once the compressed package is downloaded, execute the following commands to install or upgrade x-ui:
-
-```sh
-ARCH=$(uname -m)
-case "${ARCH}" in
- x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
- i*86 | x86) XUI_ARCH="386" ;;
- armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
- armv7* | armv7) XUI_ARCH="armv7" ;;
- armv6* | armv6) XUI_ARCH="armv6" ;;
- armv5* | armv5) XUI_ARCH="armv5" ;;
- s390x) echo 's390x' ;;
- *) XUI_ARCH="amd64" ;;
-esac
-
-cd /root/
-rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui
-tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz
-chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh
-cp x-ui/x-ui.sh /usr/bin/x-ui
-cp -f x-ui/x-ui.service /etc/systemd/system/
-mv x-ui/ /usr/local/
-systemctl daemon-reload
-systemctl enable x-ui
-systemctl restart x-ui
+To install a specific legacy version, use following installation command. e.g., ver `v1.7.9`:
```
+VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
+```
+
+Manual installation of legacy versions also involved downloading pre-compiled binaries and setting up systemd services manually. This is no longer the recommended approach.
-## Install with Docker
-
- Click for Docker details
-
-#### Usage
-
-1. **Install Docker:**
-
- ```sh
- bash <(curl -sSL https://get.docker.com)
- ```
-
-2. **Clone the Project Repository:**
-
- ```sh
- git clone https://github.com/MHSanaei/3x-ui.git
- cd 3x-ui
- ```
-
-3. **Start the Service:**
-
- ```sh
- docker compose up -d
- ```
-
- Add ```--pull always``` flag to make docker automatically recreate container if a newer image is pulled. See https://docs.docker.com/reference/cli/docker/container/run/#pull for more info.
-
- **OR**
-
- ```sh
- docker run -itd \
- -e XRAY_VMESS_AEAD_FORCED=false \
- -v $PWD/db/:/etc/x-ui/ \
- -v $PWD/cert/:/root/cert/ \
- --network=host \
- --restart=unless-stopped \
- --name 3x-ui \
- ghcr.io/mhsanaei/3x-ui:latest
- ```
-
-4. **Update to the Latest Version:**
-
- ```sh
- cd 3x-ui
- docker compose down
- docker compose pull 3x-ui
- docker compose up -d
- ```
-
-5. **Remove 3x-ui from Docker:**
-
- ```sh
- docker stop 3x-ui
- docker rm 3x-ui
- cd --
- rm -r 3x-ui
- ```
-
-
-
-## Nginx Settings
-
- Click for Reverse Proxy Configuration
-
-#### Nginx Reverse Proxy
-```nginx
-location / {
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header Range $http_range;
- proxy_set_header If-Range $http_if_range;
- proxy_redirect off;
- proxy_pass http://127.0.0.1:2053;
-}
-```
-
-#### Nginx sub-path
-- Ensure that the "URI Path" in the `/sub` panel settings is the same.
-- The `url` in the panel settings needs to end with `/`.
-
-```nginx
-location /sub {
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header Range $http_range;
- proxy_set_header If-Range $http_if_range;
- proxy_redirect off;
- proxy_pass http://127.0.0.1:2053;
-}
-```
-
-
-## Recommended OS
+## Recommended OS (for Host running Docker)
- Ubuntu 22.04+
- Debian 12+
-- CentOS 8+
-- OpenEuler 22.03+
+- CentOS 8+ / RHEL 8+ (or derivatives like AlmaLinux, Rocky Linux)
- Fedora 36+
-- Arch Linux
-- Parch Linux
-- Manjaro
-- Armbian
-- AlmaLinux 9.5+
-- Rocky Linux 9.5+
-- Oracle Linux 8+
-- OpenSUSE Tubleweed
-- Amazon Linux 2023
-- Virtuozzo Linux 8+
-- Windows x64
-
-## Supported Architectures and Devices
-
-
- Click for Supported Architectures and devices details
-
-Our platform offers compatibility with a diverse range of architectures and devices, ensuring flexibility across various computing environments. The following are key architectures that we support:
-
-- **amd64**: This prevalent architecture is the standard for personal computers and servers, accommodating most modern operating systems seamlessly.
-
-- **x86 / i386**: Widely adopted in desktop and laptop computers, this architecture enjoys broad support from numerous operating systems and applications, including but not limited to Windows, macOS, and Linux systems.
-
-- **armv8 / arm64 / aarch64**: Tailored for contemporary mobile and embedded devices, such as smartphones and tablets, this architecture is exemplified by devices like Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS, and more.
-
-- **armv7 / arm / arm32**: Serving as the architecture for older mobile and embedded devices, it remains widely utilized in devices like Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2, among others.
-
-- **armv6 / arm / arm32**: Geared towards very old embedded devices, this architecture, while less prevalent, is still in use. Devices such as Raspberry Pi 1, Raspberry Pi Zero/Zero W, rely on this architecture.
-
-- **armv5 / arm / arm32**: An older architecture primarily associated with early embedded systems, it is less common today but may still be found in legacy devices like early Raspberry Pi versions and some older smartphones.
-
-- **s390x**: This architecture is commonly used in IBM mainframe computers and offers high performance and reliability for enterprise workloads.
-
-
-## Languages
-
-- Arabic
-- English
-- Persian
-- Traditional Chinese
-- Simplified Chinese
-- Japanese
-- Russian
-- Vietnamese
-- Spanish
-- Indonesian
-- Ukrainian
-- Turkish
-- PortuguΓͺs (Brazil)
-
-
-## Features
-
-- System Status Monitoring
-- Search within all inbounds and clients
-- Dark/Light theme
-- Supports multi-user and multi-protocol
-- Supports protocols, including VMESS, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard
-- Supports XTLS native Protocols, including RPRX-Direct, Vision, REALITY
-- Traffic statistics, traffic limit, expiration time limit
-- Customizable Xray configuration templates
-- Supports HTTPS access panel (self-provided domain name + SSL certificate)
-- Supports One-Click SSL certificate application and automatic renewal
-- For more advanced configuration items, please refer to the panel
-- Fixes API routes (user setting will be created with API)
-- Supports changing configs by different items provided in the panel.
-- Supports export/import database from the panel
-
-
-## Default Panel Settings
-
-
- Click for default settings details
-
-### Username, Password, Port, and Web Base Path
-
-If you choose not to modify these settings, they will be generated randomly (this does not apply to Docker).
-
-**Default Settings for Docker:**
-- **Username:** admin
-- **Password:** admin
-- **Port:** 2053
-
-### Database Management:
-
- You can conveniently perform database Backups and Restores directly from the panel.
-
-- **Database Path:**
- - `/etc/x-ui/x-ui.db`
-
-
-### Web Base Path
-
-1. **Reset Web Base Path:**
- - Open your terminal.
- - Run the `x-ui` command.
- - Select the option to `Reset Web Base Path`.
-
-2. **Generate or Customize Path:**
- - The path will be randomly generated, or you can enter a custom path.
-
-3. **View Current Settings:**
- - To view your current settings, use the `x-ui settings` command in the terminal or `View Current Settings` in `x-ui`
-
-### Security Recommendation:
-- For enhanced security, use a long, random word in your URL structure.
-
-**Examples:**
-- `http://ip:port/*webbasepath*/panel`
-- `http://domain:port/*webbasepath*/panel`
-
-
-
-## WARP Configuration
-
-
- Click for WARP configuration details
-
-#### Usage
-
-**For versions `v2.1.0` and later:**
-
-WARP is built-in, and no additional installation is required. Simply turn on the necessary configuration in the panel.
-
-
-
-## IP Limit
-
-
- Click for IP limit details
-
-#### Usage
-
-**Note:** IP Limit won't work correctly when using IP Tunnel.
-
-- **For versions up to `v1.6.1`:**
- - The IP limit is built-in to the panel
-
-**For versions `v1.7.0` and newer:**
-
-To enable the IP Limit functionality, you need to install `fail2ban` and its required files by following these steps:
-
-1. Run the `x-ui` command in the terminal, then choose `IP Limit Management`.
-2. You will see the following options:
-
- - **Change Ban Duration:** Adjust the duration of bans.
- - **Unban Everyone:** Lift all current bans.
- - **Check Logs:** Review the logs.
- - **Fail2ban Status:** Check the status of `fail2ban`.
- - **Restart Fail2ban:** Restart the `fail2ban` service.
- - **Uninstall Fail2ban:** Uninstall Fail2ban with configuration.
-
-3. Add a path for the access log on the panel by setting `Xray Configs/log/Access log` to `./access.log` then save and restart xray.
-
-- **For versions before `v2.1.3`:**
- - You need to set the access log path manually in your Xray configuration:
-
- ```sh
- "log": {
- "access": "./access.log",
- "dnsLog": false,
- "loglevel": "warning"
- },
- ```
-
-- **For versions `v2.1.3` and newer:**
- - There is an option for configuring `access.log` directly from the panel.
-
-
-
-## Telegram Bot
-
-
- Click for Telegram bot details
-
-#### Usage
-
-The web panel supports daily traffic, panel login, database backup, system status, client info, and other notification and functions through the Telegram Bot. To use the bot, you need to set the bot-related parameters in the panel, including:
-
-- Telegram Token
-- Admin Chat ID(s)
-- Notification Time (in cron syntax)
-- Expiration Date Notification
-- Traffic Cap Notification
-- Database Backup
-- CPU Load Notification
-
-
-**Reference syntax:**
-
-- `30 \* \* \* \* \*` - Notify at the 30s of each point
-- `0 \*/10 \* \* \* \*` - Notify at the first second of each 10 minutes
-- `@hourly` - Hourly notification
-- `@daily` - Daily notification (00:00 in the morning)
-- `@weekly` - weekly notification
-- `@every 8h` - Notify every 8 hours
-
-### Telegram Bot Features
-
-- Report periodic
-- Login notification
-- CPU threshold notification
-- Threshold for Expiration time and Traffic to report in advance
-- Support client report menu if client's telegram username added to the user's configurations
-- Support telegram traffic report searched with UUID (VMESS/VLESS) or Password (TROJAN) - anonymously
-- Menu-based bot
-- Search client by email (only admin)
-- Check all inbounds
-- Check server status
-- Check depleted users
-- Receive backup by request and in periodic reports
-- Multi-language bot
-
-### Setting up Telegram bot
-
-- Start [Botfather](https://t.me/BotFather) in your Telegram account:
- 
-
-- Create a new Bot using /newbot command: It will ask you 2 questions, A name and a username for your bot. Note that the username has to end with the word "bot".
- 
-
-- Start the bot you've just created. You can find the link to your bot here.
- 
-
-- Enter your panel and config Telegram bot settings like below:
-
-
-Enter your bot token in input field number 3.
-Enter the user ID in input field number 4. The Telegram accounts with this id will be the bot admin. (You can enter more than one, Just separate them with ,)
-
-- How to get Telegram user ID? Use this [bot](https://t.me/useridinfobot), Start the bot and it will give you the Telegram user ID.
-
-
-
-
-## API Routes
-
-
- Click for API routes details
-
-#### Usage
-
-- [API Documentation](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
-- `/login` with `POST` user data: `{username: '', password: ''}` for login
-- `/panel/api/inbounds` base for following actions:
-
-| Method | Path | Action |
-| :----: | ---------------------------------- | ------------------------------------------- |
-| `GET` | `"/list"` | Get all inbounds |
-| `GET` | `"/get/:id"` | Get inbound with inbound.id |
-| `GET` | `"/getClientTraffics/:email"` | Get Client Traffics with email |
-| `GET` | `"/getClientTrafficsById/:id"` | Get client's traffic By ID |
-| `GET` | `"/createbackup"` | Telegram bot sends backup to admins |
-| `POST` | `"/add"` | Add inbound |
-| `POST` | `"/del/:id"` | Delete Inbound |
-| `POST` | `"/update/:id"` | Update Inbound |
-| `POST` | `"/clientIps/:email"` | Client Ip address |
-| `POST` | `"/clearClientIps/:email"` | Clear Client Ip address |
-| `POST` | `"/addClient"` | Add Client to inbound |
-| `POST` | `"/:id/delClient/:clientId"` | Delete Client by clientId\* |
-| `POST` | `"/updateClient/:clientId"` | Update Client by clientId\* |
-| `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic |
-| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
-| `POST` | `"/resetAllClientTraffics/:id"` | Reset traffics of all clients in an inbound |
-| `POST` | `"/delDepletedClients/:id"` | Delete inbound depleted clients (-1: all) |
-| `POST` | `"/onlines"` | Get Online users ( list of emails ) |
-
-\*- The field `clientId` should be filled by:
-
-- `client.id` for VMESS and VLESS
-- `client.password` for TROJAN
-- `client.email` for Shadowsocks
-
-- [](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
-
-
-## Environment Variables
-
-
- Click for environment variables details
-
-#### Usage
-
-| Variable | Type | Default |
-| -------------- | :--------------------------------------------: | :------------ |
-| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` |
-| XUI_DEBUG | `boolean` | `false` |
-| XUI_BIN_FOLDER | `string` | `"bin"` |
-| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
-| XUI_LOG_FOLDER | `string` | `"/var/log"` |
-
-Example:
-
-```sh
-XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
-```
-
-
-
-## Preview
+- Other Linux distributions that support recent versions of Docker and Docker Compose.
+
+## Features (New Frontend Highlights)
+
+- System Status Monitoring (Real-time Dashboard)
+- Comprehensive Inbound Management with dedicated UIs for VMESS, VLESS, Trojan, Shadowsocks clients.
+- Streamlined Client Management within Inbounds (Add, Edit, Delete, Reset Traffic, QR Codes, Share Links).
+- Full-Featured Settings Management via a tabbed interface (Panel, User Account & 2FA, Telegram Bot, Subscription Links, Other).
+- Xray Log Viewer with filtering and download.
+- Xray Version Management and GeoIP/GeoSite file updater.
+- Dark/Light Theme.
+- Responsive design for desktop, tablet, and mobile.
+- Search within all inbounds and clients (To be verified/enhanced in new UI).
+- Customizable Xray configuration templates (Via direct JSON editing in settings for now).
+- Supports HTTPS access (Configuration via panel settings, assuming certs are mounted).
+- Traffic statistics, traffic limit, expiration time limit for inbounds and clients.
+- Export/Import database from the panel (API exists, UI to be added in "Other Features").
+
+## Preview (New Frontend)
+
+*(Screenshots for the new React/Next.js frontend will be added here soon. The existing screenshots below are for the older version.)*
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
## 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._
+- All contributors and users of 3x-ui.
## Stargazers over Time
diff --git a/docker-compose.yml b/docker-compose.yml
index e27a735e..ace2c405 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,14 +1,42 @@
+version: '3.8'
+
services:
- 3x-ui:
- image: ghcr.io/mhsanaei/3x-ui:latest
- container_name: 3x-ui
- hostname: yourhostname
- 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
+ backend:
+ build:
+ context: .
+ dockerfile: Dockerfile.backend
+ container_name: 3x-ui-backend
restart: unless-stopped
+ volumes:
+ - ./db/:/etc/x-ui/
+ - ./cert/:/root/cert/
+ ports:
+ - "2053:2053"
+ environment:
+ - XRAY_VMESS_AEAD_FORCED=false
+ - XUI_ENABLE_FAIL2BAN=true
+ # - XUI_PORT=2053 # Example if backend reads port from ENV
+
+ frontend:
+ build:
+ context: ./new-frontend
+ dockerfile: Dockerfile
+ container_name: 3x-ui-frontend
+ restart: unless-stopped
+ ports:
+ - "3000:3000"
+ environment:
+ - NODE_ENV=production
+ - NEXT_PUBLIC_API_BASE_URL=http://backend:2053
+ depends_on:
+ - backend
+ # networks: # Uncomment if using a custom network defined below
+ # - xui-net
+
+# volumes:
+ # db_data:
+ # cert_data:
+
+# networks:
+# xui-net:
+# driver: bridge
diff --git a/install.sh b/install.sh
index 0398285d..27c589af 100644
--- a/install.sh
+++ b/install.sh
@@ -1,225 +1,327 @@
#!/bin/bash
-red='\033[0;31m'
-green='\033[0;32m'
-blue='\033[0;34m'
-yellow='\033[0;33m'
-plain='\033[0m'
+# Color definitions
+red='[0;31m'
+green='[0;32m'
+blue='[0;34m'
+yellow='[0;33m'
+plain='[0m'
-cur_dir=$(pwd)
+# Global variables
+ARCH=$(uname -m)
+OS_RELEASE_ID=""
+OS_RELEASE_VERSION_ID=""
+INSTALL_DIR="/opt/3x-ui-docker" # Main directory for installation
+REPO_DIR_NAME="3x-ui-source" # Subdirectory for the cloned/copied repository
+REPO_URL="https://github.com/MHSanaei/3x-ui.git" # Default repository URL
+# REPO_BRANCH="main" # Default branch, can be overridden by script argument
-# 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)"
-
-check_glibc_version() {
- glibc_version=$(ldd --version | head -n1 | awk '{print $NF}')
-
- required_version="2.32"
- if [[ "$(printf '%s\n' "$required_version" "$glibc_version" | sort -V | head -n1)" != "$required_version" ]]; then
- echo -e "${red}GLIBC version $glibc_version is too old! Required: 2.32 or higher${plain}"
- echo "Please upgrade to a newer version of your operating system to get a higher GLIBC version."
+# --- Utility Functions ---
+detect_os() {
+ if [[ -f /etc/os-release ]]; then
+ source /etc/os-release
+ OS_RELEASE_ID=$ID
+ OS_RELEASE_VERSION_ID=$VERSION_ID
+ elif [[ -f /usr/lib/os-release ]]; then
+ source /usr/lib/os-release
+ OS_RELEASE_ID=$ID
+ OS_RELEASE_VERSION_ID=$VERSION_ID
+ else
+ echo -e "${red}Failed to detect the operating system!${plain}" >&2
exit 1
fi
- echo "GLIBC version: $glibc_version (meets requirement of 2.32+)"
-}
-check_glibc_version
-
-install_base() {
- case "${release}" in
- ubuntu | debian | armbian)
- apt-get update && apt-get install -y -q wget curl tar tzdata
- ;;
- centos | almalinux | rocky | ol)
- yum -y update && yum install -y -q wget curl tar tzdata
- ;;
- fedora | amzn | virtuozzo)
- dnf -y update && dnf install -y -q wget curl tar tzdata
- ;;
- arch | manjaro | parch)
- pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata
- ;;
- opensuse-tumbleweed)
- zypper refresh && zypper -q install -y wget curl tar timezone
- ;;
- *)
- apt-get update && apt install -y -q wget curl tar tzdata
- ;;
- esac
+ echo -e "${blue}Detected OS: $OS_RELEASE_ID $OS_RELEASE_VERSION_ID${plain}"
}
-gen_random_string() {
- local length="$1"
- local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' /dev/null 2>&1
+}
- 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
+# --- Installation Functions ---
+install_dependencies() {
+ print_colored "$blue" "Installing essential dependencies (curl, tar, git)..."
+ local pkgs_to_install=""
+ if ! check_command curl; then pkgs_to_install+="curl "; fi
+ if ! check_command tar; then pkgs_to_install+="tar "; fi
+ if ! check_command git; then pkgs_to_install+="git "; fi
- /usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
- echo -e "This is a fresh installation, generating random login info for security concerns:"
- echo -e "###############################################"
- 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: http://${server_ip}:${config_port}/${config_webBasePath}${plain}"
- echo -e "###############################################"
- else
- local config_webBasePath=$(gen_random_string 15)
- echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
- /usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
- echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
- echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
+ if [[ -n "$pkgs_to_install" ]]; then
+ case "$OS_RELEASE_ID" in
+ ubuntu | debian | armbian)
+ apt-get update -y && apt-get install -y $pkgs_to_install
+ ;;
+ centos | almalinux | rocky | ol)
+ yum install -y $pkgs_to_install
+ ;;
+ fedora)
+ dnf install -y $pkgs_to_install
+ ;;
+ arch | manjaro)
+ pacman -Syu --noconfirm $pkgs_to_install
+ ;;
+ *)
+ print_colored "$yellow" "Unsupported OS for automatic dependency installation: $OS_RELEASE_ID. Please install curl, tar, and git manually."
+ # exit 1 # Or proceed with caution
+ ;;
+ esac
+ # Verify installation
+ if ! check_command curl || ! check_command tar || ! check_command git; then
+ print_colored "$red" "Failed to install essential dependencies. Please install them manually and re-run."
+ exit 1
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}"
- /usr/local/x-ui/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. Exiting...${plain}"
- fi
+ print_colored "$green" "Essential dependencies are already installed."
fi
-
- /usr/local/x-ui/x-ui migrate
}
-install_x-ui() {
- cd /usr/local/
+# --- Function to check and install Docker ---
+fn_install_docker() {
+ echo "Checking for Docker..."
+ if command -v docker &> /dev/null; then
+ echo "Docker is already installed."
+ docker --version
+ return 0
+ fi
- 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 "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
+ echo "Docker not found. Attempting to install Docker..."
+ if [[ -x "$(command -v apt-get)" ]]; then
+ echo "Attempting Docker installation for Debian/Ubuntu..."
+ apt-get update -y
+ apt-get install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
+ install -m 0755 -d /etc/apt/keyrings # Ensure keyring directory exists
+ curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
+ chmod a+r /etc/apt/keyrings/docker.gpg
+ echo \
+ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") \
+ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
+ apt-get update -y
+ apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin || {
+ echo -e "${red}Docker installation via apt failed. Trying convenience script...${plain}"
+ curl -fsSL https://get.docker.com -o get-docker.sh
+ sh get-docker.sh || {
+ echo -e "${red}Docker installation failed. Please install Docker manually and re-run this script.${plain}"
+ exit 1
+ }
+ rm get-docker.sh
+ }
+ elif [[ -x "$(command -v yum)" ]]; then
+ echo "Attempting Docker installation for CentOS/RHEL..."
+ yum install -y yum-utils
+ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
+ yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin || {
+ echo -e "${red}Docker installation via yum failed. Trying convenience script...${plain}"
+ curl -fsSL https://get.docker.com -o get-docker.sh
+ sh get-docker.sh || {
+ echo -e "${red}Docker installation failed. Please install Docker manually and re-run this script.${plain}"
+ exit 1
+ }
+ rm get-docker.sh
+ }
+ elif [[ -x "$(command -v dnf)" ]]; then
+ echo "Attempting Docker installation for Fedora..."
+ dnf -y install dnf-plugins-core
+ dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
+ dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin || {
+ echo -e "${red}Docker installation via dnf failed. Please install Docker manually and re-run this script.${plain}"
exit 1
- fi
- echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
- wget -N -O /usr/local/x-ui-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}"
+ echo -e "${red}Unsupported package manager for Docker auto-installation. Trying convenience script...${plain}"
+ curl -fsSL https://get.docker.com -o get-docker.sh
+ sh get-docker.sh || {
+ echo -e "${red}Docker installation failed. Please install Docker manually and re-run this script.${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"
- wget -N -O /usr/local/x-ui-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
+ }
+ rm get-docker.sh
fi
- if [[ -e /usr/local/x-ui/ ]]; then
- systemctl stop x-ui
- rm /usr/local/x-ui/ -rf
+ if ! systemctl is-active --quiet docker; then
+ systemctl start docker || echo -e "${yellow}Attempt to start Docker via systemctl failed. It might already be running or need manual intervention.${plain}"
fi
-
- tar zxvf x-ui-linux-$(arch).tar.gz
- rm x-ui-linux-$(arch).tar.gz -f
- cd x-ui
- chmod +x x-ui
-
- # 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
+ if ! systemctl is-enabled --quiet docker; then
+ systemctl enable docker || echo -e "${yellow}Attempt to enable Docker via systemctl failed.${plain}"
fi
-
- chmod +x x-ui bin/xray-linux-$(arch)
- cp -f x-ui.service /etc/systemd/system/
- wget -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
- chmod +x /usr/local/x-ui/x-ui.sh
- chmod +x /usr/bin/x-ui
- config_after_install
-
- systemctl daemon-reload
- systemctl enable x-ui
- systemctl start x-ui
- 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 "Docker installed and started."
+ docker --version
}
-echo -e "${green}Running...${plain}"
-install_base
-install_x-ui $1
+# --- Function to check and install Docker Compose ---
+fn_install_docker_compose() {
+ echo "Checking for Docker Compose..."
+ if docker compose version &> /dev/null; then
+ echo "Docker Compose (plugin) is already installed."
+ docker compose version
+ return 0
+ fi
+
+ echo "Docker Compose (plugin) not found."
+ echo "It's usually included with recent Docker Engine installations (as docker-compose-plugin)."
+ echo "If Docker was just installed, it might already be there as part of docker-ce or docker-ce-cli."
+ echo "Verifying if 'docker-compose-plugin' can be installed or is part of 'docker-ce-cli' update..."
+
+ # Attempt to install or update packages that might include the compose plugin
+ if [[ -x "$(command -v apt-get)" ]]; then
+ apt-get install -y docker-compose-plugin docker-ce-cli || echo -e "${yellow}Attempt to install/update docker-compose-plugin via apt failed or already up-to-date.${plain}"
+ elif [[ -x "$(command -v yum)" ]]; then
+ yum install -y docker-compose-plugin docker-ce-cli || echo -e "${yellow}Attempt to install/update docker-compose-plugin via yum failed or already up-to-date.${plain}"
+ elif [[ -x "$(command -v dnf)" ]]; then
+ dnf install -y docker-compose-plugin docker-ce-cli || echo -e "${yellow}Attempt to install/update docker-compose-plugin via dnf failed or already up-to-date.${plain}"
+ fi
+
+ # Re-check after attempting plugin install
+ if docker compose version &> /dev/null; then
+ echo "Docker Compose (plugin) is now available."
+ docker compose version
+ return 0
+ fi
+
+ echo -e "${yellow}Docker Compose (plugin) still not found. Checking for legacy docker-compose (standalone)...${plain}"
+ if command -v docker-compose &> /dev/null; then
+ echo "Legacy docker-compose found."
+ docker-compose --version
+ echo -e "${yellow}Warning: Legacy docker-compose is deprecated. Consider upgrading your Docker setup to use the Docker Compose plugin (docker compose).${plain}"
+ return 0
+ fi
+
+ echo "Attempting to install legacy docker-compose as a fallback..."
+ LATEST_COMPOSE_VERSION=\$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\\\" -f4)
+ if [ -z "\$LATEST_COMPOSE_VERSION" ]; then
+ echo -e "${red}Failed to fetch latest docker-compose version. Please install Docker Compose manually.${plain}"
+ exit 1
+ fi
+
+ INSTALL_PATH1="/usr/local/lib/docker/cli-plugins"
+ INSTALL_PATH2="/usr/libexec/docker/cli-plugins"
+ INSTALL_PATH3="/usr/local/bin"
+
+ mkdir -p \$INSTALL_PATH1 && \
+ curl -SL https://github.com/docker/compose/releases/download/\$LATEST_COMPOSE_VERSION/docker-compose-\$(uname -s)-\$(uname -m) -o \$INSTALL_PATH1/docker-compose && \
+ chmod +x \$INSTALL_PATH1/docker-compose || \
+ (mkdir -p \$INSTALL_PATH2 && \
+ curl -SL https://github.com/docker/compose/releases/download/\$LATEST_COMPOSE_VERSION/docker-compose-\$(uname -s)-\$(uname -m) -o \$INSTALL_PATH2/docker-compose && \
+ chmod +x \$INSTALL_PATH2/docker-compose) || \
+ (curl -SL https://github.com/docker/compose/releases/download/\$LATEST_COMPOSE_VERSION/docker-compose-\$(uname -s)-\$(uname -m) -o \$INSTALL_PATH3/docker-compose && \
+ chmod +x \$INSTALL_PATH3/docker-compose) || \
+ {
+ echo -e "${red}Failed to download and install legacy docker-compose in standard paths. Please install Docker Compose manually.${plain}"
+ exit 1
+ }
+
+ if docker compose version &> /dev/null; then
+ echo "Docker Compose (plugin) became available after legacy install attempt (possibly due to PATH or Docker restart)."
+ elif command -v docker-compose &> /dev/null; then
+ echo "Legacy docker-compose installed successfully."
+ docker-compose --version
+ else
+ echo -e "${red}Failed to make legacy docker-compose command available. Please check your PATH or install manually.${plain}"
+ exit 1
+ fi
+}
+
+
+# --- Main Installation Logic ---
+main() {
+ check_root
+ detect_os # Call detect_os to populate OS_RELEASE_ID
+
+ print_colored "$blue" "Starting 3X-UI New Frontend Docker-based Installation..."
+
+ install_dependencies # Call install_dependencies
+ fn_install_docker # Renamed from install_docker to avoid conflict if sourcing other scripts
+ fn_install_docker_compose # Renamed from install_docker_compose_plugin
+
+ # Handle installation directory
+ if [ -n "$1" ]; then
+ print_colored "$yellow" "Argument \$1 ($1) detected. This script primarily installs from main/master branch for Docker setup. Argument will be ignored for repo cloning."
+ fi
+
+ local user_install_dir
+ read -rp "Enter installation directory (default: $INSTALL_DIR): " user_install_dir
+ INSTALL_DIR=\${user_install_dir:-$INSTALL_DIR}
+
+ print_colored "$blue" "Panel will be installed in: $INSTALL_DIR"
+ mkdir -p "$INSTALL_DIR"
+ # cd "$INSTALL_DIR" # We cd into REPO_PATH later
+
+ # Clone or update the repository
+ local REPO_PATH="\$INSTALL_DIR/\$REPO_DIR_NAME" # Made REPO_PATH local
+ if [ -d "\$REPO_PATH" ] && [ -d "\$REPO_PATH/.git" ]; then
+ print_colored "$yellow" "Existing repository found at \$REPO_PATH. Attempting to update..."
+ cd "\$REPO_PATH" || { print_colored "$red" "Failed to cd to $REPO_PATH"; exit 1; }
+ git fetch --all
+ print_colored "$yellow" "Resetting to origin/main to ensure latest of main branch..."
+ git reset --hard origin/main # Or use a specific tag/branch if versioning is set up
+ git pull origin main || print_colored "$red" "Failed to pull latest changes for main branch. Continuing with local version."
+ else
+ print_colored "$blue" "Cloning repository from $REPO_URL into $REPO_PATH..."
+ rm -rf "\$REPO_PATH"
+ git clone --depth 1 "$REPO_URL" "\$REPO_PATH" || { print_colored "$red" "Failed to clone repository."; exit 1; }
+ fi
+
+ cd "\$REPO_PATH" || { print_colored "$red" "Failed to cd into repository directory '\$REPO_PATH'."; exit 1; }
+
+ if [ ! -f "docker-compose.yml" ] || [ ! -f "new-frontend/Dockerfile" ] || [ ! -f "Dockerfile.backend" ]; then
+ print_colored "$red" "Essential Docker configuration files not found in the repository. Aborting."
+ exit 1
+ fi
+
+ print_colored "$blue" "Creating data directories (db, cert) if they don't exist..."
+ mkdir -p db
+ mkdir -p cert
+
+ local DEFAULT_FRONTEND_PORT=3000
+ local DEFAULT_BACKEND_PANEL_PORT=2053
+ local HOST_FRONTEND_PORT
+ local HOST_BACKEND_PANEL_PORT
+ local INTERNAL_BACKEND_PORT=2053
+
+ print_colored "$yellow" "Configuring .env file..."
+ read -rp "Enter HOST port for Frontend (default: $DEFAULT_FRONTEND_PORT): " HOST_FRONTEND_PORT
+ HOST_FRONTEND_PORT=\${HOST_FRONTEND_PORT:-$DEFAULT_FRONTEND_PORT}
+
+ read -rp "Enter HOST port for Backend Panel (default: $DEFAULT_BACKEND_PANEL_PORT): " HOST_BACKEND_PANEL_PORT
+ HOST_BACKEND_PANEL_PORT=\${HOST_BACKEND_PANEL_PORT:-$DEFAULT_BACKEND_PANEL_PORT}
+
+ cat << EOF_ENV > .env
+# .env for 3x-ui docker-compose
+PANEL_NAME=3x-ui
+FRONTEND_HOST_PORT=$HOST_FRONTEND_PORT
+BACKEND_HOST_PORT=$HOST_BACKEND_PANEL_PORT
+BACKEND_INTERNAL_PORT=$INTERNAL_BACKEND_PORT
+XRAY_VMESS_AEAD_FORCED=false
+XUI_ENABLE_FAIL2BAN=true
+NEXT_PUBLIC_API_BASE_URL=http://backend:\${BACKEND_INTERNAL_PORT:-2053}
+EOF_ENV
+ print_colored "$green" ".env file configured."
+ print_colored "$yellow" "Note: Frontend will be accessible on host at port $HOST_FRONTEND_PORT."
+ print_colored "$yellow" "Backend panel (API) will be accessible on host at port $HOST_BACKEND_PANEL_PORT."
+
+ print_colored "$blue" "Building and starting services with Docker Compose..."
+ print_colored "$yellow" "This may take a few minutes for the first build..."
+ if docker compose up -d --build --remove-orphans; then
+ print_colored "$green" "3X-UI services (new frontend & backend) started successfully!"
+ print_colored "$green" "Frontend should be accessible at: http://:$HOST_FRONTEND_PORT"
+ print_colored "$yellow" "Please allow a moment for services to initialize fully."
+ print_colored "$blue" "To manage services, navigate to '\$(pwd)' and use 'docker compose' commands (e.g., docker compose logs -f, docker compose stop)."
+ else
+ print_colored "$red" "Failed to start services with Docker Compose. Please check logs above and run 'docker compose logs' for details."
+ exit 1
+ fi
+
+ print_colored "$green" "Installation finished."
+}
+
+# --- Script Execution ---
+main "\$@"
+
+exit 0
diff --git a/new-frontend/next.config.js b/new-frontend/next.config.js
new file mode 100644
index 00000000..6ba69df4
--- /dev/null
+++ b/new-frontend/next.config.js
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ images: {
+ unoptimized: true,
+ },
+};
+
+module.exports = nextConfig;
diff --git a/new-frontend/next.config.ts b/new-frontend/next.config.ts
deleted file mode 100644
index e9ffa308..00000000
--- a/new-frontend/next.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { NextConfig } from "next";
-
-const nextConfig: NextConfig = {
- /* config options here */
-};
-
-export default nextConfig;
diff --git a/new-frontend/src/app/inbounds/[id]/clients/page.tsx b/new-frontend/src/app/inbounds/[id]/clients/page.tsx
index d422f5a0..35935677 100644
--- a/new-frontend/src/app/inbounds/[id]/clients/page.tsx
+++ b/new-frontend/src/app/inbounds/[id]/clients/page.tsx
@@ -7,7 +7,7 @@ import { post } from '@/services/api';
import { Inbound, ClientSetting, Protocol } from '@/types/inbound';
import { formatBytes } from '@/lib/formatters';
import ClientFormModal from '@/components/inbounds/ClientFormModal';
-import ClientShareModal from '@/components/inbounds/ClientShareModal'; // Import Share Modal
+import ClientShareModal from '@/components/inbounds/ClientShareModal';
// Define button styles locally for consistency
const btnPrimaryStyles = "px-4 py-2 bg-primary-500 text-white font-semibold rounded-lg shadow-md hover:bg-primary-600 disabled:opacity-50 transition-colors text-sm";
@@ -104,11 +104,10 @@ const ManageClientsPage: React.FC = () => {
const openEditModal = (client: DisplayClient) => {
setEditingClient(client); setClientFormModalError(null); setIsClientFormModalOpen(true);
};
- const openShareModal = (client: ClientSetting) => { // ClientSetting is enough for link generation
+ const openShareModal = (client: ClientSetting) => {
setSharingClient(client); setIsShareModalOpen(true);
};
-
const handleClientFormSubmit = async (submittedClientData: ClientSetting) => {
if (!inbound) { setClientFormModalError("Inbound data not available."); return; }
setClientFormModalLoading(true); setClientFormModalError(null); setActionError(null);
@@ -211,7 +210,7 @@ const ManageClientsPage: React.FC = () => {
{inbound?.protocol === 'shadowsocks' &&
For Shadowsocks, client configuration (method and password) is part of the main inbound settings.
- The QR code / subscription link below uses these global settings.
+ Use the 'QR' button below to get the share link for the inbound.
+ For Shadowsocks, client configuration (method and password) is part of the main inbound settings.
+ The QR code / subscription link below uses these global settings.
+
+ );
+};
+export default EditInboundClientComponent;
diff --git a/new-frontend/src/app/inbounds/edit/[id]/page.tsx b/new-frontend/src/app/inbounds/edit/[id]/page.tsx
index 0face4f7..3aeb697d 100644
--- a/new-frontend/src/app/inbounds/edit/[id]/page.tsx
+++ b/new-frontend/src/app/inbounds/edit/[id]/page.tsx
@@ -23,10 +23,6 @@ const EditInboundPage: React.FC = () => {
setPageLoading(true);
setError(null);
try {
- // The /inbound/list endpoint returns all inbounds.
- // We then find the specific one by ID.
- // This is not ideal for a single item fetch but matches current backend capabilities shown.
- // A dedicated /inbound/get/{id} would be better.
const response = await post('/inbound/list', {});
if (response.success && response.data) {
const numericId = parseInt(id, 10);
@@ -82,7 +78,7 @@ const EditInboundPage: React.FC = () => {
Edit Inbound: {initialInboundData?.remark || id}
{error && initialInboundData && (
- {error} {/* Show non-critical errors above the form if data is loaded */}
+ {error}