Repository: sing-web/x-ui Branch: main Commit: 0cd2536025dd Files: 121 Total size: 74.2 MB Directory structure: gitextract_66_3d683/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── docker.yaml │ └── release.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README_CN.md ├── bin/ │ ├── xray-linux-amd64 │ ├── xray-linux-arm64 │ └── xray-linux-s390x ├── config/ │ ├── config.go │ ├── name │ └── version ├── database/ │ ├── db.go │ └── model/ │ └── model.go ├── go.mod ├── go.sum ├── install.sh ├── install_CN.sh ├── logger/ │ └── logger.go ├── main.go ├── util/ │ ├── common/ │ │ ├── err.go │ │ ├── format.go │ │ ├── multi_error.go │ │ ├── network.go │ │ └── stringUtil.go │ ├── context.go │ ├── json_util/ │ │ └── json.go │ ├── random/ │ │ └── random.go │ ├── reflect_util/ │ │ └── reflect.go │ └── sys/ │ ├── a.s │ ├── psutil.go │ ├── sys_darwin.go │ └── sys_linux.go ├── web/ │ ├── assets/ │ │ ├── ant-design-vue@1.7.2/ │ │ │ └── antd.less │ │ ├── css/ │ │ │ └── custom.css │ │ ├── element-ui@2.15.0/ │ │ │ └── theme-chalk/ │ │ │ └── display.css │ │ ├── js/ │ │ │ ├── axios-init.js │ │ │ ├── model/ │ │ │ │ ├── models.js │ │ │ │ └── xray.js │ │ │ └── util/ │ │ │ ├── common.js │ │ │ ├── date-util.js │ │ │ └── utils.js │ │ └── vue@2.6.12/ │ │ ├── vue.common.dev.js │ │ ├── vue.common.js │ │ ├── vue.common.prod.js │ │ ├── vue.esm.js │ │ ├── vue.runtime.common.dev.js │ │ ├── vue.runtime.common.js │ │ ├── vue.runtime.common.prod.js │ │ ├── vue.runtime.esm.js │ │ └── vue.runtime.js │ ├── controller/ │ │ ├── base.go │ │ ├── inbound.go │ │ ├── index.go │ │ ├── server.go │ │ ├── setting.go │ │ ├── util.go │ │ └── xui.go │ ├── entity/ │ │ └── entity.go │ ├── global/ │ │ └── global.go │ ├── html/ │ │ ├── common/ │ │ │ ├── head.html │ │ │ ├── js.html │ │ │ ├── prompt_modal.html │ │ │ ├── qrcode_modal.html │ │ │ └── text_modal.html │ │ ├── login.html │ │ └── xui/ │ │ ├── common_sider.html │ │ ├── component/ │ │ │ ├── inbound_info.html │ │ │ └── setting.html │ │ ├── form/ │ │ │ ├── inbound.html │ │ │ ├── protocol/ │ │ │ │ ├── dokodemo.html │ │ │ │ ├── http.html │ │ │ │ ├── shadowsocks.html │ │ │ │ ├── socks.html │ │ │ │ ├── trojan.html │ │ │ │ ├── vless.html │ │ │ │ └── vmess.html │ │ │ ├── reality_settings.html │ │ │ ├── sniffing.html │ │ │ ├── stream/ │ │ │ │ ├── stream_grpc.html │ │ │ │ ├── stream_http.html │ │ │ │ ├── stream_kcp.html │ │ │ │ ├── stream_quic.html │ │ │ │ ├── stream_settings.html │ │ │ │ ├── stream_tcp.html │ │ │ │ └── stream_ws.html │ │ │ ├── stream_sockopt.html │ │ │ └── tls_settings.html │ │ ├── inbound_info_modal.html │ │ ├── inbound_modal.html │ │ ├── inbounds.html │ │ ├── index.html │ │ └── setting.html │ ├── job/ │ │ ├── check_inbound_job.go │ │ ├── check_xray_running_job.go │ │ ├── stats_notify_job.go │ │ └── xray_traffic_job.go │ ├── network/ │ │ ├── auto_https_listener.go │ │ └── autp_https_conn.go │ ├── service/ │ │ ├── config.json │ │ ├── inbound.go │ │ ├── panel.go │ │ ├── server.go │ │ ├── setting.go │ │ ├── telegram.go │ │ ├── user.go │ │ └── xray.go │ ├── session/ │ │ └── session.go │ ├── translation/ │ │ ├── translate.en_US.toml │ │ ├── translate.zh_Hans.toml │ │ └── translate.zh_Hant.toml │ └── web.go ├── x-ui.service ├── x-ui.sh ├── x-ui_CN.sh └── xray/ ├── config.go ├── inbound.go ├── process.go └── traffic.go ================================================ FILE CONTENTS ================================================ ================================================ 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/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests target-branch: "dev" schedule: interval: "daily" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/docker.yaml ================================================ name: "Build and push images" on: push: tags: - v* workflow_dispatch: jobs: Building: runs-on: ubuntu-latest name: "Build images" env: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKERHUB_REPOSITORY: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:latest steps: - name: Set up QEMU uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.0.0 - name: Login to DockerHub uses: docker/login-action@v3.0.0 with: username: ${{ env.DOCKER_USERNAME }} password: ${{ env.DOCKER_PASSWORD }} - name: Build and push images to Docker hub uses: docker/build-push-action@v5.1.0 with: push: true platforms: linux/amd64, linux/arm64, linux/s390x tags: ${{ env.DOCKERHUB_REPOSITORY }} ================================================ FILE: .github/workflows/release.yaml ================================================ name: Release x-ui on: push: tags: - v* workflow_dispatch: jobs: release: runs-on: ubuntu-latest container: docker.io/ubuntu:18.04 outputs: upload_url: ${{ steps.create_release.outputs.upload_url }} steps: - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: ${{ github.ref }} draft: false prerelease: false linux386build: name: build x-ui 386 version needs: release runs-on: ubuntu-latest container: docker.io/ubuntu:18.04 steps: - uses: actions/checkout@v4.1.1 - name: Set up Go uses: actions/setup-go@v4 with: go-version: stable - name: build linux 386 version run: | apt-get update apt install -y sudo curl wget unzip ca-certificates build-essential gcc-multilib CGO_ENABLED=1 GOOS=linux GOARCH=386 go build -o xui-release -v main.go mkdir x-ui cp xui-release x-ui/xui-release cp x-ui.service x-ui/x-ui.service cp x-ui.sh x-ui/x-ui.sh cd x-ui mv xui-release x-ui mkdir bin cd bin wget https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-32.zip unzip Xray-linux-32.zip rm -f Xray-linux-32.zip mv xray xray-linux-386 rm -f geoip.dat geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat cd .. cd .. - name: package run: tar -zcvf x-ui-linux-386.tar.gz x-ui - name: upload uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.release.outputs.upload_url }} asset_path: x-ui-linux-386.tar.gz asset_name: x-ui-linux-386.tar.gz asset_content_type: application/gzip linuxamd64build: name: build x-ui amd64 version needs: release runs-on: ubuntu-latest container: docker.io/ubuntu:18.04 steps: - uses: actions/checkout@v4.1.1 - name: Set up Go uses: actions/setup-go@v4 with: go-version: stable - name: build linux amd64 version run: | apt-get update apt install -y sudo curl wget unzip ca-certificates build-essential CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o xui-release -v main.go mkdir x-ui cp xui-release x-ui/xui-release cp x-ui.service x-ui/x-ui.service cp x-ui.sh x-ui/x-ui.sh cd x-ui mv xui-release x-ui mkdir bin cd bin wget https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip unzip Xray-linux-64.zip rm -f Xray-linux-64.zip mv xray xray-linux-amd64 rm -f geoip.dat geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat cd .. cd .. - name: package run: tar -zcvf x-ui-linux-amd64.tar.gz x-ui - name: upload uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.release.outputs.upload_url }} asset_path: x-ui-linux-amd64.tar.gz asset_name: x-ui-linux-amd64.tar.gz asset_content_type: application/gzip linuxarm64build: name: build x-ui arm64 version needs: release runs-on: ubuntu-latest container: docker.io/ubuntu:18.04 steps: - uses: actions/checkout@v4.1.1 - name: Set up Go uses: actions/setup-go@v4 with: go-version: stable - name: build linux arm64 version run: | apt-get update apt install -y sudo curl wget unzip ca-certificates gcc-aarch64-linux-gnu CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc go build -o xui-release -v main.go mkdir x-ui cp xui-release x-ui/xui-release cp x-ui.service x-ui/x-ui.service cp x-ui.sh x-ui/x-ui.sh cd x-ui mv xui-release x-ui mkdir bin cd bin wget https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-arm64-v8a.zip unzip Xray-linux-arm64-v8a.zip rm -f Xray-linux-arm64-v8a.zip mv xray xray-linux-arm64 rm -f geoip.dat geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat cd .. cd .. - name: package run: tar -zcvf x-ui-linux-arm64.tar.gz x-ui - name: upload uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.release.outputs.upload_url }} asset_path: x-ui-linux-arm64.tar.gz asset_name: x-ui-linux-arm64.tar.gz asset_content_type: application/gzip linuxs390xbuild: name: build x-ui s390x version needs: release runs-on: ubuntu-latest container: docker.io/ubuntu:18.04 steps: - uses: actions/checkout@v4.1.1 - name: Set up Go uses: actions/setup-go@v4 with: go-version: stable - name: build linux s390x version run: | apt-get update apt install -y sudo curl wget unzip ca-certificates gcc-s390x-linux-gnu CGO_ENABLED=1 GOOS=linux GOARCH=s390x CC=s390x-linux-gnu-gcc go build -o xui-release -v main.go mkdir x-ui cp xui-release x-ui/xui-release cp x-ui.service x-ui/x-ui.service cp x-ui.sh x-ui/x-ui.sh cd x-ui mv xui-release x-ui mkdir bin cd bin wget https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-s390x.zip unzip Xray-linux-s390x.zip rm -f Xray-linux-s390x.zip geoip.dat geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat mv xray xray-linux-s390x cd .. cd .. - name: package run: tar -zcvf x-ui-linux-s390x.tar.gz x-ui - name: upload uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.release.outputs.upload_url }} asset_path: x-ui-linux-s390x.tar.gz asset_name: x-ui-linux-s390x.tar.gz asset_content_type: application/gzip ================================================ FILE: .gitignore ================================================ .idea tmp dist/ x-ui/ release.sh .sync* bin/config.json oryxBuildBinary ================================================ FILE: Dockerfile ================================================ FROM golang:latest AS builder WORKDIR /root COPY . . RUN go build main.go FROM debian:12-slim RUN apt-get update && apt-get install -y --no-install-recommends -y ca-certificates \ && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* WORKDIR /root COPY --from=builder /root/main /root/x-ui COPY bin/. /root/bin/. VOLUME [ "/etc/x-ui" ] CMD [ "./x-ui" ] ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . ================================================ FILE: README.md ================================================ # x-ui Multi-protocol, multi-user xray panel support > [中文文档请点击这里进行查看](./README_CN.md) # Features - System status monitoring - Support multi-user multi-protocol, multi-user on same port, web visualization operation - Supported protocols: vmess, vless, trojan, shadowsocks, dokodemo-door, socks, http - Support vless / trojan reality - Support for configuring more transport configurations - Traffic statistics, limit traffic, limit expiration time - Customizable xray configuration templates - Support https access panel (self-provided domain + ssl certificate) - Support one-click SSL certificate application and automatic renewal - More advanced configuration items, see panel for details # Installation & Upgrade ``` bash <(wget -qO- https://raw.githubusercontent.com/sing-web/x-ui/main/install.sh) ``` ## Manual installation & upgrade 1. First download the latest tarball from the project, usually choose the `amd64` architecture 2. Then upload the tarball to the `/root/` directory on the server and login to the server with the `root` user > If your server cpu architecture is not `amd64`, replace `amd64` in the command with another architecture ``` cd /root/ rm x-ui/ /usr/local/x-ui/ /usr/bin/x-ui -rf tar zxvf x-ui-linux-amd64.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 ``` ## Installing with docker > This docker tutorial and docker image is provided by [Chasing66](https://github.com/Chasing66) 1. Installing docker ```shell curl -fsSL https://get.docker.com | sh ``` 2. install x-ui ```shell mkdir x-ui && cd x-ui docker run -itd --network=host \ -v $PWD/db/:/etc/x-ui/ \ -v $PWD/cert/:/root/cert/ \ --name x-ui --restart=unless-stopped \ misakablog/x-ui:latest ``` > Build your own image ```shell docker build -t x-ui . ``` ## TG bot usage > This feature and tutorial is provided by [FranzKafkaYu](https://github.com/FranzKafkaYu) X-UI supports daily traffic notification and panel login reminder via Tg bot. The specific application tutorial can be found in [blog link](https://coderfan.net/how-to-use-telegram-bot-to-alarm-you-when-someone-login-into-your-vps.html) Instructions:Set the bot-related parameters in the background of the panel, including - Tg Bot Token - Tg Bot ChatId - Tg Bot cycle running time, using crontab syntax Reference syntax: - 30 * * * * * * //notify on the 30ths of every minute - @hourly // hourly notification - @daily //daily notification (at 00:00 am sharp) - @every 8h //notify every 8 hours TG notification content: - Node traffic usage - Panel login reminder - Node expiration reminder - Traffic alert reminder More features are being planned... ## Recommended Systems - CentOS 7+ - Ubuntu 16+ - Debian 8+ ## Credits * vaxilu's x-ui project: https://github.com/vaxilu/x-ui * qist's xray-ui project: https://github.com/qist/xray-ui * MHSanaei's 3x-ui project: https://github.com/MHSanaei/3x-ui ## Sponsorship afdian (for China mainland): https://afdian.net/a/Misaka-blog ![afdian-MisakaNo の 小破站](https://user-images.githubusercontent.com/122191366/211533469-351009fb-9ae8-4601-992a-abbf54665b68.jpg) ## Disclaimer * This program is for learning and understanding only, not for profit, please delete within 24 hours after downloading, not for any commercial use, text, data and images are copyrighted, if reproduced, please indicate the source. * Use of this program is subject to the deployment disclaimer. Use of this program is subject to the laws and regulations of the country where the server is deployed and the country where the user is located, and the author of the program is not responsible for any misconduct of the user. ================================================ FILE: README_CN.md ================================================ # x-ui 支持多协议多用户的 xray 面板 # 功能介绍 - 系统状态监控 - 支持多用户多协议,单端口多用户,网页可视化操作 - 支持的协议:vmess、vless、trojan、shadowsocks、dokodemo-door、socks、http - 支持 vless / trojan reality - 支持配置更多传输配置 - 流量统计,限制流量,限制到期时间 - 可自定义 xray 配置模板 - 支持 https 访问面板(自备域名 + ssl 证书) - 支持一键SSL证书申请且自动续签 - 更多高级配置项,详见面板 # 安装 & 升级 ``` bash <(wget -qO- https://raw.githubusercontent.com/sing-web/x-ui/main/install_CN.sh) ``` ## 手动安装&升级 1. 首先从项目中下载最新的压缩包,一般选择 `amd64`架构 2. 然后将这个压缩包上传到服务器的 `/root/`目录下,并使用 `root`用户登录服务器 > 如果你的服务器 cpu 架构不是 `amd64`,自行将命令中的 `amd64`替换为其他架构 ``` cd /root/ rm x-ui/ /usr/local/x-ui/ /usr/bin/x-ui -rf tar zxvf x-ui-linux-amd64.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 ``` ## 使用docker安装 > 此 docker 教程与 docker 镜像由[Chasing66](https://github.com/Chasing66)提供 1. 安装docker ```shell curl -fsSL https://get.docker.com | sh ``` 2. 安装x-ui ```shell mkdir x-ui && cd x-ui docker run -itd --network=host \ -v $PWD/db/:/etc/x-ui/ \ -v $PWD/cert/:/root/cert/ \ --name x-ui --restart=unless-stopped \ misakablog/x-ui:latest ``` > Build 自己的镜像 ```shell docker build -t x-ui . ``` ## TG 机器人使用 > 此功能与教程由[FranzKafkaYu](https://github.com/FranzKafkaYu)提供 X-UI支持通过Tg机器人实现每日流量通知,面板登录提醒等功能,使用Tg机器人,需要自行申请 具体申请教程可以参考[博客链接](https://coderfan.net/how-to-use-telegram-bot-to-alarm-you-when-someone-login-into-your-vps.html) 使用说明:在面板后台设置机器人相关参数,具体包括 - Tg机器人Token - Tg机器人ChatId - Tg机器人周期运行时间,采用crontab语法 参考语法: - 30 * * * * * //每一分的第30s进行通知 - @hourly //每小时通知 - @daily //每天通知(凌晨零点整) - @every 8h //每8小时通知 TG通知内容: - 节点流量使用 - 面板登录提醒 - 节点到期提醒 - 流量预警提醒 更多功能规划中... ## 建议系统 - CentOS 7+ - Ubuntu 16+ - Debian 8+ ## 鸣谢 * vaxilu 的 x-ui 项目:https://github.com/vaxilu/x-ui * qist 的 xray-ui 项目:https://github.com/qist/xray-ui * MHSanaei 的 3x-ui 项目:https://github.com/MHSanaei/3x-ui ## 赞助 爱发电:https://afdian.net/a/Misaka-blog ![afdian-MisakaNo の 小破站](https://user-images.githubusercontent.com/122191366/211533469-351009fb-9ae8-4601-992a-abbf54665b68.jpg) ## 免责声明 * 本程序仅供学习了解, 非盈利目的,请于下载后 24 小时内删除, 不得用作任何商业用途, 文字、数据及图片均有所属版权, 如转载须注明来源。 * 使用本程序必循遵守部署免责声明。使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责. ================================================ FILE: bin/xray-linux-amd64 ================================================ [File too large to display: 23.8 MB] ================================================ FILE: bin/xray-linux-arm64 ================================================ [File too large to display: 23.0 MB] ================================================ FILE: bin/xray-linux-s390x ================================================ [File too large to display: 25.6 MB] ================================================ FILE: config/config.go ================================================ package config import ( _ "embed" "fmt" "os" "strings" ) //go:embed version var version string //go:embed name var name string type LogLevel string const ( Debug LogLevel = "debug" Info LogLevel = "info" Warn LogLevel = "warn" Error LogLevel = "error" ) func GetVersion() string { return strings.TrimSpace(version) } func GetName() string { return strings.TrimSpace(name) } func GetLogLevel() LogLevel { if IsDebug() { return Debug } logLevel := os.Getenv("XUI_LOG_LEVEL") if logLevel == "" { return Info } return LogLevel(logLevel) } func IsDebug() bool { return os.Getenv("XUI_DEBUG") == "true" } func GetBinFolderPath() string { binFolderPath := os.Getenv("XUI_BIN_FOLDER") if binFolderPath == "" { binFolderPath = "bin" } return binFolderPath } func GetDBFolderPath() string { dbFolderPath := os.Getenv("XUI_DB_FOLDER") if dbFolderPath == "" { dbFolderPath = "/etc/x-ui" } return dbFolderPath } func GetDBPath() string { return fmt.Sprintf("/etc/%s/%s.db", GetName(), GetName()) } ================================================ FILE: config/name ================================================ x-ui ================================================ FILE: config/version ================================================ v0.3.3.14 ================================================ FILE: database/db.go ================================================ package database import ( "bytes" "io" "io/fs" "os" "path" "x-ui/config" "x-ui/database/model" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) var db *gorm.DB func initUser() error { err := db.AutoMigrate(&model.User{}) if err != nil { return err } var count int64 err = db.Model(&model.User{}).Count(&count).Error if err != nil { return err } if count == 0 { user := &model.User{ Username: "admin", Password: "admin", } return db.Create(user).Error } return nil } func initInbound() error { return db.AutoMigrate(&model.Inbound{}) } func initSetting() error { return db.AutoMigrate(&model.Setting{}) } func InitDB(dbPath string) error { dir := path.Dir(dbPath) err := os.MkdirAll(dir, fs.ModeDir) 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 } err = initUser() if err != nil { return err } err = initInbound() if err != nil { return err } err = initSetting() if err != nil { return err } return nil } func GetDB() *gorm.DB { return db } func IsNotFound(err error) bool { return err == gorm.ErrRecordNotFound } func IsSQLiteDB(file io.Reader) (bool, error) { signature := []byte("SQLite format 3\x00") buf := make([]byte, len(signature)) _, err := file.Read(buf) if err != nil { return false, err } return bytes.Equal(buf, signature), nil } ================================================ FILE: database/model/model.go ================================================ package model import ( "fmt" "x-ui/util/json_util" "x-ui/xray" ) type Protocol string const ( VMess Protocol = "vmess" VLESS Protocol = "vless" Dokodemo Protocol = "Dokodemo-door" Http Protocol = "http" Trojan Protocol = "trojan" Shadowsocks Protocol = "shadowsocks" ) type User struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` Username string `json:"username"` Password string `json:"password"` } type Inbound struct { Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` UserId int `json:"-"` Up int64 `json:"up" form:"up"` Down int64 `json:"down" form:"down"` Total int64 `json:"total" form:"total"` Remark string `json:"remark" form:"remark"` Enable bool `json:"enable" form:"enable"` ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // config part Listen string `json:"listen" form:"listen"` Port int `json:"port" form:"port" gorm:"unique"` 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"` } func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig { listen := i.Listen if listen != "" { 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), } } type Setting struct { Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Key string `json:"key" form:"key"` Value string `json:"value" form:"value"` } ================================================ FILE: go.mod ================================================ module x-ui go 1.20 require ( github.com/BurntSushi/toml v1.3.2 github.com/Workiva/go-datastructures v1.1.0 github.com/gin-contrib/sessions v0.0.4 github.com/gin-gonic/gin v1.9.1 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/nicksnyder/go-i18n/v2 v2.2.1 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v3 v3.23.6 github.com/xtls/xray-core v1.8.3 go.uber.org/atomic v1.11.0 golang.org/x/text v0.11.0 google.golang.org/grpc v1.56.2 gorm.io/driver/sqlite v1.5.2 gorm.io/gorm v1.25.2 ) require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ole/go-ole v1.2.6 // 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.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // 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/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sagernet/sing v0.2.5 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.10.0 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sys v0.9.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: go.sum ================================================ github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd5wAKUHEO/k= github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 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/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/gin-contrib/sessions v0.0.4 h1:gq4fNa1Zmp564iHP5G6EBuktilEos8VKhe2sza1KMgo= github.com/gin-contrib/sessions v0.0.4/go.mod h1:pQ3sIyviBBGcxgyR8mkeJuXbeV3h3NYmhJADQTq5+Vo= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 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.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 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.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 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/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw= github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc= 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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= 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/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo= github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= 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/sagernet/sing v0.2.5 h1:N8sUluR8GZvR9DqUiH3FA3vBb4m/EDdOVTYUrDzJvmY= github.com/sagernet/sing v0.2.5/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowsocks v0.2.2 h1:ezSdVhrmIcwDXmCZF3bOJVMuVtTQWpda+1Op+Ie2TA4= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08= github.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= 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 v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 h1:AMyzgjkh54WocjQSlCnT1LhDc/BKiUqtNOv40AkpURs= github.com/xtls/xray-core v1.8.3 h1:lxaVklPjLKqUU4ua4qH8SBaRcAaNHlH+LmXOx0U/Ejg= github.com/xtls/xray-core v1.8.3/go.mod h1:i7t4JFnq828P2+XK0XjGQ8W9x78iu+EJ7jI4l3sonIw= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 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.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc= gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho= gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= ================================================ FILE: install.sh ================================================ #!/bin/bash red='\033[0;31m' green='\033[0;32m' yellow='\033[0;33m' plain='\033[0m' cur_dir=$(pwd) # check root [[ $EUID -ne 0 ]] && echo -e "${red} error: ${plain} Must run this script with root user!\n" && exit 1 # check os if [[ -f /etc/redhat-release ]]; then release="centos" elif cat /etc/issue | grep -Eqi "debian"; then release="debian" elif cat /etc/issue | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /etc/issue | grep -Eqi "centos|red hat|redhat"; then release="centos" elif cat /proc/version | grep -Eqi "debian"; then release="debian" elif cat /proc/version | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /proc/version | grep -Eqi "centos|red hat|redhat"; then release="centos" else echo -e "${red}No system version detected, please contact the script author!${plain}\n" && exit 1 fi arch=$(arch) if [[ $arch == "i386" || $arch == "i686" ]]; then arch="386" elif [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then arch="amd64" elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then arch="arm64" elif [[ $arch == "s390x" ]]; then arch="s390x" else arch="amd64" echo -e "${red}Failed to detect architecture, use default architecture: ${arch}${plain}" fi echo "Arch: ${arch}" os_version="" # os version if [[ -f /etc/os-release ]]; then os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release) fi if [[ -z "$os_version" && -f /etc/lsb-release ]]; then os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release) fi if [[ x"${release}" == x"centos" ]]; then if [[ ${os_version} -le 6 ]]; then echo -e "${red}Please use CentOS 7 or higher!${plain}\n" && exit 1 fi elif [[ x"${release}" == x"ubuntu" ]]; then if [[ ${os_version} -lt 16 ]]; then echo -e "${red}Please use Ubuntu 16 or higher!${plain}\n" && exit 1 fi elif [[ x"${release}" == x"debian" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red}Please use Debian 8 or higher!${plain}\n" && exit 1 fi fi install_base() { if [[ x"${release}" == x"centos" ]]; then yum install wget curl tar -y else apt-get update apt install wget curl tar -y fi } # This function will be called when user installed x-ui out of sercurity config_after_install() { echo -e "${yellow}For security reasons, you need to force a port and account password change after the installation/update is complete ${plain}" read -p "Confirmation to continue? [y/n]: " config_confirm if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then read -p "Please set your account name (8 random characters if not filled in): " config_account [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) echo -e "${yellow} your account name will be set to: ${config_account}${plain}" read -p "Please set your account password (8 random characters if not filled in): " config_password [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) echo -e "${yellow} your account password will be set to:${config_password}${plain}" read -p "Please set the panel access port (or random port number if not filled in): " config_port [[ -z $config_port ]] && config_port=$(shuf -i 2000-65535 -n 1) until [[ -z $(ss -ntlp | awk '{print $4}' | sed 's/.*://g' | grep -w "$port") ]]; do if [[ -n $(ss -ntlp | awk '{print $4}' | sed 's/.*://g' | grep -w "$port") ]]; then echo -e "${red} $config_port ${plain} The port is already occupied by another program, please change the panel port number" read -p "Please set the panel access port (or random port number if not filled in): " config_port [[ -z $config_port ]] && config_port=$(shuf -i 2000-65535 -n 1) fi done echo -e "${yellow} your panel access port will be set to:${config_port}${plain}" echo -e "${yellow}Confirm setting, setting in ${plain}" /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} echo -e "${yellow} account password setting complete ${plain}" /usr/local/x-ui/x-ui setting -port ${config_port} echo -e "${yellow} panel port setting complete ${plain}" else config_port=$(/usr/local/x-ui/x-ui setting -show | sed -n 4p | awk -F ": " '{print $2}') echo -e "${red}Account setting got cancelled, all settings are default, please change it as soon as possible!${plain}" fi } install_x-ui() { systemctl stop x-ui cd /usr/local/ if [ $# == 0 ]; then last_version=$(curl -Ls "https://api.github.com/repos/sing-web/x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') if [[ ! -n "$last_version" ]]; then echo -e "${red}Failed to detect x-ui version, may be out of Github API limit, please try again later, or manually specify x-ui version to install${plain}" exit 1 fi echo -e "The latest version of x-ui is detected: ${last_version}, start installation" wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/sing-web/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz if [[ $? -ne 0 ]]; then echo -e "${red}Downloading x-ui failed, please make sure your server can download the Github file${plain}" exit 1 fi else last_version=$1 url="https://github.com/sing-web/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz" echo -e "Start installing x-ui $1" wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url} if [[ $? -ne 0 ]]; then echo -e "${red}Downloading x-ui v$1 failed, please make sure this version exists${plain}" exit 1 fi fi if [[ -e /usr/local/x-ui/ ]]; then rm /usr/local/x-ui/ -rf fi tar zxvf x-ui-linux-${arch}.tar.gz rm x-ui-linux-${arch}.tar.gz -f cd x-ui chmod +x x-ui bin/xray-linux-${arch} cp -f x-ui.service /etc/systemd/system/ wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/sing-web/x-ui/main/x-ui.sh chmod +x /usr/local/x-ui/x-ui.sh chmod +x /usr/bin/x-ui config_after_install #echo -e "如果是全新安装,默认网页端口为 ${green}54321${plain},用户名和密码默认都是 ${green}admin${plain}" #echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保 54321 端口已放行${plain}" # echo -e "若想将 54321 修改为其它端口,输入 x-ui 命令进行修改,同样也要确保你修改的端口也是放行的" #echo -e "" #echo -e "如果是更新面板,则按你之前的方式访问面板" #echo -e "" systemctl daemon-reload systemctl enable x-ui systemctl start x-ui systemctl stop warp-go >/dev/null 2>&1 wg-quick down wgcf >/dev/null 2>&1 ipv4=$(curl -s4m8 ip.p3terx.com -k | sed -n 1p) ipv6=$(curl -s6m8 ip.p3terx.com -k | sed -n 1p) systemctl start warp-go >/dev/null 2>&1 wg-quick up wgcf >/dev/null 2>&1 echo -e "${green}x-ui ${last_version}${plain} Installation completed, panel started" echo -e "" echo -e "How to use the x-ui administration script: " echo -e "----------------------------------------------" echo -e "x-ui - Show admin menu (more features)" echo -e "x-ui start - Start x-ui panel" echo -e "x-ui stop - stop the x-ui panel" echo -e "x-ui restart - restart the x-ui panel" echo -e "x-ui status - check x-ui status" echo -e "x-ui enable - set x-ui to start on its own" echo -e "x-ui disable - disable x-ui boot-up" echo -e "x-ui log - View x-ui logs" echo -e "x-ui update - Update the x-ui panel" echo -e "x-ui install - Install the x-ui panel" echo -e "x-ui uninstall - uninstall the x-ui panel" echo -e "----------------------------------------------" echo "" if [[ -n $ipv4 ]]; then echo -e "${yellow}The panel IPv4 access address is:${plain} ${green}http://$ipv4:$config_port ${plain}" fi if [[ -n $ipv6 ]]; then echo -e "${yellow}The panel IPv6 access address is:${plain} ${green}http://[$ipv6]:$config_port ${plain}" fi echo -e "Please make sure that this port is not occupied by another application, ${yellow} and that the ${plain} ${red} $config_port ${plain} ${yellow} port is released ${plain}" } echo -e "${green}Begin installation${plain}" install_base install_x-ui $1 ================================================ FILE: install_CN.sh ================================================ #!/bin/bash red='\033[0;31m' green='\033[0;32m' yellow='\033[0;33m' plain='\033[0m' cur_dir=$(pwd) # check root [[ $EUID -ne 0 ]] && echo -e "${red}错误:${plain} 必须使用root用户运行此脚本!\n" && exit 1 # check os if [[ -f /etc/redhat-release ]]; then release="centos" elif cat /etc/issue | grep -Eqi "debian"; then release="debian" elif cat /etc/issue | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /etc/issue | grep -Eqi "centos|red hat|redhat"; then release="centos" elif cat /proc/version | grep -Eqi "debian"; then release="debian" elif cat /proc/version | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /proc/version | grep -Eqi "centos|red hat|redhat"; then release="centos" else echo -e "${red}未检测到系统版本,请联系脚本作者!${plain}\n" && exit 1 fi arch=$(arch) if [[ $arch == "i386" || $arch == "i686" ]]; then arch="386" elif [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then arch="amd64" elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then arch="arm64" elif [[ $arch == "s390x" ]]; then arch="s390x" else arch="amd64" echo -e "${red}检测架构失败,使用默认架构: ${arch}${plain}" fi echo "架构: ${arch}" if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then echo "本软件不支持 32 位系统(x86),请使用 64 位系统(x86_64),如果检测有误,请联系作者" exit -1 fi os_version="" # os version if [[ -f /etc/os-release ]]; then os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release) fi if [[ -z "$os_version" && -f /etc/lsb-release ]]; then os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release) fi if [[ x"${release}" == x"centos" ]]; then if [[ ${os_version} -le 6 ]]; then echo -e "${red}请使用 CentOS 7 或更高版本的系统!${plain}\n" && exit 1 fi elif [[ x"${release}" == x"ubuntu" ]]; then if [[ ${os_version} -lt 16 ]]; then echo -e "${red}请使用 Ubuntu 16 或更高版本的系统!${plain}\n" && exit 1 fi elif [[ x"${release}" == x"debian" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red}请使用 Debian 8 或更高版本的系统!${plain}\n" && exit 1 fi fi install_base() { if [[ x"${release}" == x"centos" ]]; then yum install wget curl tar -y else apt-get update apt install wget curl tar -y fi } #This function will be called when user installed x-ui out of sercurity config_after_install() { echo -e "${yellow}出于安全考虑,安装/更新完成后需要强制修改端口与账户密码${plain}" read -p "确认是否继续? [y/n]: " config_confirm if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then read -p "请设置您的账户名(如未填写则随机8位字符): " config_account [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) echo -e "${yellow}您的账户名将设定为:${config_account}${plain}" read -p "请设置您的账户密码(如未填写则随机8位字符): " config_password [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) echo -e "${yellow}您的账户密码将设定为:${config_password}${plain}" read -p "请设置面板访问端口(如未填写则随机端口号): " config_port [[ -z $config_port ]] && config_port=$(shuf -i 2000-65535 -n 1) until [[ -z $(ss -ntlp | awk '{print $4}' | sed 's/.*://g' | grep -w "$port") ]]; do if [[ -n $(ss -ntlp | awk '{print $4}' | sed 's/.*://g' | grep -w "$port") ]]; then echo -e "${red} $config_port ${plain} 端口已经其他程序占用,请更换面板端口号" read -p "请设置面板访问端口(如未填写则随机端口号): " config_port [[ -z $config_port ]] && config_port=$(shuf -i 2000-65535 -n 1) fi done echo -e "${yellow}您的面板访问端口将设定为:${config_port}${plain}" echo -e "${yellow}确认设定,设定中${plain}" /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} echo -e "${yellow}账户密码设定完成${plain}" /usr/local/x-ui/x-ui setting -port ${config_port} echo -e "${yellow}面板端口设定完成${plain}" else config_port=$(/usr/local/x-ui/x-ui setting -show | sed -n 4p | awk -F ": " '{print $2}') echo -e "${red}已取消, 所有设置项均为默认设置, 请及时修改${plain}" fi } install_x-ui() { systemctl stop x-ui cd /usr/local/ if [ $# == 0 ]; then last_version=$(curl -Ls "https://api.github.com/repos/sing-web/x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') if [[ ! -n "$last_version" ]]; then echo -e "${red}检测 x-ui 版本失败,可能是超出 Github API 限制,请稍后再试,或手动指定 x-ui 版本安装${plain}" exit 1 fi echo -e "检测到 x-ui 最新版本:${last_version},开始安装" wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/sing-web/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz if [[ $? -ne 0 ]]; then echo -e "${red}下载 x-ui 失败,请确保你的服务器能够下载 Github 的文件${plain}" exit 1 fi else last_version=$1 url="https://github.com/sing-web/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz" echo -e "开始安装 x-ui $1" wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url} if [[ $? -ne 0 ]]; then echo -e "${red}下载 x-ui $1 失败,请确保此版本存在${plain}" exit 1 fi fi if [[ -e /usr/local/x-ui/ ]]; then rm /usr/local/x-ui/ -rf fi tar zxvf x-ui-linux-${arch}.tar.gz rm x-ui-linux-${arch}.tar.gz -f cd x-ui chmod +x x-ui bin/xray-linux-${arch} cp -f x-ui.service /etc/systemd/system/ wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/sing-web/x-ui/main/x-ui_CN.sh chmod +x /usr/local/x-ui/x-ui.sh chmod +x /usr/bin/x-ui config_after_install #echo -e "如果是全新安装,默认网页端口为 ${green}54321${plain},用户名和密码默认都是 ${green}admin${plain}" #echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保 54321 端口已放行${plain}" # echo -e "若想将 54321 修改为其它端口,输入 x-ui 命令进行修改,同样也要确保你修改的端口也是放行的" #echo -e "" #echo -e "如果是更新面板,则按你之前的方式访问面板" #echo -e "" systemctl daemon-reload systemctl enable x-ui systemctl start x-ui systemctl stop warp-go >/dev/null 2>&1 wg-quick down wgcf >/dev/null 2>&1 ipv4=$(curl -s4m8 ip.p3terx.com -k | sed -n 1p) ipv6=$(curl -s6m8 ip.p3terx.com -k | sed -n 1p) systemctl start warp-go >/dev/null 2>&1 wg-quick up wgcf >/dev/null 2>&1 echo -e "${green}x-ui ${last_version}${plain} 安装完成,面板已启动" echo -e "" echo -e "x-ui 管理脚本使用方法: " echo -e "----------------------------------------------" echo -e "x-ui - 显示管理菜单 (功能更多)" echo -e "x-ui start - 启动 x-ui 面板" echo -e "x-ui stop - 停止 x-ui 面板" echo -e "x-ui restart - 重启 x-ui 面板" echo -e "x-ui status - 查看 x-ui 状态" echo -e "x-ui enable - 设置 x-ui 开机自启" echo -e "x-ui disable - 取消 x-ui 开机自启" echo -e "x-ui log - 查看 x-ui 日志" echo -e "x-ui update - 更新 x-ui 面板" echo -e "x-ui install - 安装 x-ui 面板" echo -e "x-ui uninstall - 卸载 x-ui 面板" echo -e "----------------------------------------------" echo "" if [[ -n $ipv4 ]]; then echo -e "${yellow}面板IPv4访问地址为:${plain} ${green}http://$ipv4:$config_port ${plain}" fi if [[ -n $ipv6 ]]; then echo -e "${yellow}面板IPv6访问地址为:${plain} ${green}http://[$ipv6]:$config_port ${plain}" fi echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保${plain} ${red} $config_port ${plain} ${yellow}端口已放行${plain}" } echo -e "${green}开始安装${plain}" install_base install_x-ui $1 ================================================ FILE: logger/logger.go ================================================ package logger import ( "github.com/op/go-logging" "os" ) var logger *logging.Logger func init() { InitLogger(logging.INFO) } func InitLogger(level logging.Level) { format := logging.MustStringFormatter( `%{time:2006/01/02 15:04:05} %{level} - %{message}`, ) newLogger := logging.MustGetLogger("x-ui") backend := logging.NewLogBackend(os.Stderr, "", 0) backendFormatter := logging.NewBackendFormatter(backend, format) backendLeveled := logging.AddModuleLevel(backendFormatter) backendLeveled.SetLevel(level, "") newLogger.SetBackend(backendLeveled) logger = newLogger } func Debug(args ...interface{}) { logger.Debug(args...) } func Debugf(format string, args ...interface{}) { logger.Debugf(format, args...) } func Info(args ...interface{}) { logger.Info(args...) } func Infof(format string, args ...interface{}) { logger.Infof(format, args...) } func Warning(args ...interface{}) { logger.Warning(args...) } func Warningf(format string, args ...interface{}) { logger.Warningf(format, args...) } func Error(args ...interface{}) { logger.Error(args...) } func Errorf(format string, args ...interface{}) { logger.Errorf(format, args...) } ================================================ FILE: main.go ================================================ package main import ( "flag" "fmt" "log" "os" "os/signal" "syscall" _ "unsafe" "x-ui/config" "x-ui/database" "x-ui/logger" "x-ui/web" "x-ui/web/global" "x-ui/web/service" "github.com/op/go-logging" ) func runWebServer() { log.Printf("%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.Warn: logger.InitLogger(logging.WARNING) case config.Error: logger.InitLogger(logging.ERROR) default: log.Fatal("unknown log level:", config.GetLogLevel()) } err := database.InitDB(config.GetDBPath()) if err != nil { log.Fatal(err) } var server *web.Server server = web.NewServer() global.SetWebServer(server) err = server.Start() if err != nil { log.Println(err) return } sigCh := make(chan os.Signal, 1) //信号量捕获处理 signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL) for { sig := <-sigCh switch sig { case syscall.SIGHUP: err := server.Stop() if err != nil { logger.Warning("stop server err:", err) } server = web.NewServer() global.SetWebServer(server) err = server.Start() if err != nil { log.Println(err) return } default: server.Stop() return } } } func resetSetting() { err := database.InitDB(config.GetDBPath()) if err != nil { fmt.Println(err) return } settingService := service.SettingService{} err = settingService.ResetSettings() if err != nil { fmt.Println("reset setting failed:", err) } else { fmt.Println("reset setting success") } } func showSetting(show bool) { if show { settingService := service.SettingService{} port, err := settingService.GetPort() if err != nil { fmt.Println("get current port fialed,error info:", err) } userService := service.UserService{} userModel, err := userService.GetFirstUser() if err != nil { fmt.Println("get current user info failed,error info:", err) } username := userModel.Username userpasswd := userModel.Password if (username == "") || (userpasswd == "") { fmt.Println("current username or password is empty") } fmt.Println("当前面板信息设置如下:") fmt.Println("用户名:", username) fmt.Println("密码:", userpasswd) fmt.Println("端口:", port) } } 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) } } return } func updateTgbotSetting(tgBotToken string, tgBotChatid int, tgBotRuntime string) { err := database.InitDB(config.GetDBPath()) if err != nil { fmt.Println(err) return } settingService := service.SettingService{} if tgBotToken != "" { err := settingService.SetTgBotToken(tgBotToken) if err != nil { fmt.Println(err) return } else { logger.Info("updateTgbotSetting tgBotToken success") } } if tgBotRuntime != "" { err := settingService.SetTgbotRuntime(tgBotRuntime) if err != nil { fmt.Println(err) return } else { logger.Infof("updateTgbotSetting tgBotRuntime[%s] success", tgBotRuntime) } } if tgBotChatid != 0 { err := settingService.SetTgBotChatId(tgBotChatid) if err != nil { fmt.Println(err) return } else { logger.Info("updateTgbotSetting tgBotChatid success") } } } func updateSetting(port int, username string, password string, listen string) { err := database.InitDB(config.GetDBPath()) if err != nil { fmt.Println(err) return } settingService := service.SettingService{} if port > 0 { err := settingService.SetPort(port) if err != nil { fmt.Println("set port failed:", err) } else { fmt.Printf("set port %v success", port) } } if listen != "" { err := settingService.SetListen(listen) if err != nil { fmt.Println("set listen failed:", err) } else { fmt.Printf("set listen %v success", listen) } } if username != "" || password != "" { userService := service.UserService{} err := userService.UpdateFirstUser(username, password) if err != nil { fmt.Println("set username and password failed:", err) } else { fmt.Println("set username and password success") } } } 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 listen string var username string var password string var tgbottoken string var tgbotchatid int var enabletgbot bool var tgbotRuntime string var reset bool var show bool settingCmd.BoolVar(&reset, "reset", false, "reset all settings") settingCmd.BoolVar(&show, "show", false, "show current settings") settingCmd.IntVar(&port, "port", 0, "set panel port") settingCmd.StringVar(&listen, "listen", "", "set panel listen") settingCmd.StringVar(&username, "username", "", "set login username") settingCmd.StringVar(&password, "password", "", "set login password") settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegrame bot token") settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegrame bot cron time") settingCmd.IntVar(&tgbotchatid, "tgbotchatid", 0, "set telegrame bot chat id") settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "enable telegram bot notify") oldUsage := flag.Usage flag.Usage = func() { oldUsage() fmt.Println() fmt.Println("Commands:") fmt.Println(" run run web panel") 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 "setting": err := settingCmd.Parse(os.Args[2:]) if err != nil { fmt.Println(err) return } if reset { resetSetting() } else { updateSetting(port, username, password, listen) } if show { showSetting(show) } if (tgbottoken != "") || (tgbotchatid != 0) || (tgbotRuntime != "") { updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime) } default: fmt.Println("except 'run' or 'setting' subcommands") fmt.Println() runCmd.Usage() fmt.Println() fmt.Println() settingCmd.Usage() } } ================================================ FILE: util/common/err.go ================================================ package common import ( "errors" "fmt" "x-ui/logger" ) var CtxDone = errors.New("context done") func NewErrorf(format string, a ...interface{}) error { msg := fmt.Sprintf(format, a...) return errors.New(msg) } func NewError(a ...interface{}) error { msg := fmt.Sprintln(a...) return errors.New(msg) } func Recover(msg string) interface{} { panicErr := recover() if panicErr != nil { if msg != "" { logger.Error(msg, "panic:", panicErr) } } return panicErr } ================================================ FILE: util/common/format.go ================================================ package common import ( "fmt" ) func FormatTraffic(trafficBytes int64) (size string) { if trafficBytes < 1024 { return fmt.Sprintf("%.2fB", float64(trafficBytes)/float64(1)) } else if trafficBytes < (1024 * 1024) { return fmt.Sprintf("%.2fKB", float64(trafficBytes)/float64(1024)) } else if trafficBytes < (1024 * 1024 * 1024) { return fmt.Sprintf("%.2fMB", float64(trafficBytes)/float64(1024*1024)) } else if trafficBytes < (1024 * 1024 * 1024 * 1024) { return fmt.Sprintf("%.2fGB", float64(trafficBytes)/float64(1024*1024*1024)) } else if trafficBytes < (1024 * 1024 * 1024 * 1024 * 1024) { return fmt.Sprintf("%.2fTB", float64(trafficBytes)/float64(1024*1024*1024*1024)) } else { return fmt.Sprintf("%.2fEB", float64(trafficBytes)/float64(1024*1024*1024*1024*1024)) } } func FormatTime(timeseconds uint64) (timeStr string) { if timeseconds < 60 { return fmt.Sprintf("%d seconds", timeseconds) } else if timeseconds < 60*60 { return fmt.Sprintf("%d minutes", timeseconds/(60)) } else if timeseconds < 60*60*24 { return fmt.Sprintf("%d hours", timeseconds/(60*60)) } else { return fmt.Sprintf("%d days", timeseconds/(60*60*24)) } } ================================================ FILE: util/common/multi_error.go ================================================ package common import ( "strings" ) type multiError []error func (e multiError) Error() string { var r strings.Builder r.WriteString("multierr: ") for _, err := range e { r.WriteString(err.Error()) r.WriteString(" | ") } return r.String() } func Combine(maybeError ...error) error { var errs multiError for _, err := range maybeError { if err != nil { errs = append(errs, err) } } if len(errs) == 0 { return nil } return errs } ================================================ FILE: util/common/network.go ================================================ package common import ( "io/ioutil" "net/http" ) func GetMyIpAddr() string { resp, err := http.Get("https://api.ipify.org") if err != nil { resp, _ = http.Get("https://api64.ipify.org") } defer resp.Body.Close() s, _ := ioutil.ReadAll(resp.Body) return string(s) } ================================================ FILE: util/common/stringUtil.go ================================================ package common import ( "bytes" "sort" ) func IsSubString(target string, str_array []string) bool { sort.Strings(str_array) index := sort.SearchStrings(str_array, target) return index < len(str_array) && str_array[index] == target } func ByteToString(p []byte) string { for i := 0; i < len(p); i++ { if p[i] == '\n' { return string(p[0:i]) } } return string(p) } /* * if some byte slice have the '\n' we need clear these special characters * to get a standard string */ func ByteToStringWithOutNewLine(p []byte) string { return string(bytes.Replace(p, []byte("\n"), []byte(""), 1)) } ================================================ FILE: util/context.go ================================================ package util import "context" func IsDone(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } ================================================ FILE: util/json_util/json.go ================================================ package json_util import ( "errors" ) type RawMessage []byte // MarshalJSON 自定义 json.RawMessage 默认行为 func (m RawMessage) MarshalJSON() ([]byte, error) { if len(m) == 0 { return []byte("null"), nil } return m, nil } // UnmarshalJSON sets *m to a copy of data. func (m *RawMessage) UnmarshalJSON(data []byte) error { if m == nil { return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") } *m = append((*m)[0:0], data...) return nil } ================================================ FILE: util/random/random.go ================================================ package random import ( "math/rand" "time" ) var numSeq [10]rune var lowerSeq [26]rune var upperSeq [26]rune var numLowerSeq [36]rune var numUpperSeq [36]rune var allSeq [62]rune func init() { rand.Seed(time.Now().UnixNano()) for i := 0; i < 10; i++ { numSeq[i] = rune('0' + i) } for i := 0; i < 26; i++ { lowerSeq[i] = rune('a' + i) upperSeq[i] = rune('A' + i) } copy(numLowerSeq[:], numSeq[:]) copy(numLowerSeq[len(numSeq):], lowerSeq[:]) copy(numUpperSeq[:], numSeq[:]) copy(numUpperSeq[len(numSeq):], upperSeq[:]) copy(allSeq[:], numSeq[:]) copy(allSeq[len(numSeq):], lowerSeq[:]) copy(allSeq[len(numSeq)+len(lowerSeq):], upperSeq[:]) } func Seq(n int) string { runes := make([]rune, n) for i := 0; i < n; i++ { runes[i] = allSeq[rand.Intn(len(allSeq))] } return string(runes) } ================================================ FILE: util/reflect_util/reflect.go ================================================ package reflect_util import "reflect" func GetFields(t reflect.Type) []reflect.StructField { num := t.NumField() fields := make([]reflect.StructField, 0, num) for i := 0; i < num; i++ { fields = append(fields, t.Field(i)) } return fields } func GetFieldValues(v reflect.Value) []reflect.Value { num := v.NumField() fields := make([]reflect.Value, 0, num) for i := 0; i < num; i++ { fields = append(fields, v.Field(i)) } return fields } ================================================ FILE: util/sys/a.s ================================================ ================================================ FILE: util/sys/psutil.go ================================================ package sys import ( _ "unsafe" ) //go:linkname HostProc github.com/shirou/gopsutil/v3/internal/common.HostProc func HostProc(combineWith ...string) string ================================================ FILE: util/sys/sys_darwin.go ================================================ //go:build darwin // +build darwin package sys import ( "github.com/shirou/gopsutil/v3/net" ) func GetTCPCount() (int, error) { stats, err := net.Connections("tcp") if err != nil { return 0, err } return len(stats), nil } func GetUDPCount() (int, error) { stats, err := net.Connections("udp") if err != nil { return 0, err } return len(stats), nil } ================================================ FILE: util/sys/sys_linux.go ================================================ // +build linux package sys import ( "bytes" "fmt" "io" "os" ) func getLinesNum(filename string) (int, error) { file, err := os.Open(filename) if err != nil { return 0, err } defer file.Close() sum := 0 buf := make([]byte, 8192) for { n, err := file.Read(buf) var buffPosition int for { i := bytes.IndexByte(buf[buffPosition:], '\n') if i < 0 || n == buffPosition { break } buffPosition += i + 1 sum++ } if err == io.EOF { return sum, nil } else if err != nil { return sum, err } } } func GetTCPCount() (int, error) { root := HostProc() tcp4, err := getLinesNum(fmt.Sprintf("%v/net/tcp", root)) if err != nil { return tcp4, err } tcp6, err := getLinesNum(fmt.Sprintf("%v/net/tcp6", root)) if err != nil { return tcp4 + tcp6, nil } return tcp4 + tcp6, nil } func GetUDPCount() (int, error) { root := HostProc() udp4, err := getLinesNum(fmt.Sprintf("%v/net/udp", root)) if err != nil { return udp4, err } udp6, err := getLinesNum(fmt.Sprintf("%v/net/udp6", root)) if err != nil { return udp4 + udp6, nil } return udp4 + udp6, nil } ================================================ FILE: web/assets/ant-design-vue@1.7.2/antd.less ================================================ @import "../lib/style/index.less"; @import "../lib/style/components.less"; ================================================ FILE: web/assets/css/custom.css ================================================ #app { height: 100%; } .ant-space { width: 100%; } .ant-layout-sider-zero-width-trigger { display: none; } .ant-card { border-radius: 30px; } .ant-card-hoverable { cursor: auto; } .ant-card+.ant-card { margin-top: 20px; } .drawer-handle { position: absolute; top: 72px; width: 41px; height: 40px; cursor: pointer; z-index: 0; text-align: center; line-height: 40px; font-size: 16px; display: flex; justify-content: center; align-items: center; background: #fff; right: -40px; box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); border-radius: 0 4px 4px 0; } @media (min-width: 769px) { .drawer-handle { display: none; } } .fade-in-enter, .fade-in-leave-active, .fade-in-linear-enter, .fade-in-linear-leave, .fade-in-linear-leave-active, .fade-in-linear-enter, .fade-in-linear-leave, .fade-in-linear-leave-active { opacity: 0 } .fade-in-linear-enter-active, .fade-in-linear-leave-active { -webkit-transition: opacity .2s linear; transition: opacity .2s linear } .fade-in-linear-enter-active, .fade-in-linear-leave-active { -webkit-transition: opacity .2s linear; transition: opacity .2s linear } .fade-in-enter-active, .fade-in-leave-active { -webkit-transition: all .3s cubic-bezier(.55, 0, .1, 1); transition: all .3s cubic-bezier(.55, 0, .1, 1) } .zoom-in-center-enter-active, .zoom-in-center-leave-active { -webkit-transition: all .3s cubic-bezier(.55, 0, .1, 1); transition: all .3s cubic-bezier(.55, 0, .1, 1) } .zoom-in-center-enter, .zoom-in-center-leave-active { opacity: 0; -webkit-transform: scaleX(0); transform: scaleX(0) } .zoom-in-top-enter-active, .zoom-in-top-leave-active { opacity: 1; -webkit-transform: scaleY(1); transform: scaleY(1); -webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); -webkit-transform-origin: center top; transform-origin: center top } .zoom-in-top-enter, .zoom-in-top-leave-active { opacity: 0; -webkit-transform: scaleY(0); transform: scaleY(0) } .zoom-in-bottom-enter-active, .zoom-in-bottom-leave-active { opacity: 1; -webkit-transform: scaleY(1); transform: scaleY(1); -webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); -webkit-transform-origin: center bottom; transform-origin: center bottom } .zoom-in-bottom-enter, .zoom-in-bottom-leave-active { opacity: 0; -webkit-transform: scaleY(0); transform: scaleY(0) } .zoom-in-left-enter-active, .zoom-in-left-leave-active { opacity: 1; -webkit-transform: scale(1, 1); transform: scale(1, 1); -webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1); transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1); -webkit-transform-origin: top left; transform-origin: top left } .zoom-in-left-enter, .zoom-in-left-leave-active { opacity: 0; -webkit-transform: scale(.45, .45); transform: scale(.45, .45) } .list-enter-active, .list-leave-active { -webkit-transition: all .3s; transition: all .3s } .list-enter, .list-leave-active { opacity: 0; -webkit-transform: translateY(-30px); transform: translateY(-30px) } .ant-progress-inner { background-color: #EBEEF5; } ================================================ FILE: web/assets/element-ui@2.15.0/theme-chalk/display.css ================================================ @media only screen and (max-width:767px){.hidden-xs-only{display:none!important}}@media only screen and (min-width:768px){.hidden-sm-and-up{display:none!important}}@media only screen and (min-width:768px) and (max-width:991px){.hidden-sm-only{display:none!important}}@media only screen and (max-width:991px){.hidden-sm-and-down{display:none!important}}@media only screen and (min-width:992px){.hidden-md-and-up{display:none!important}}@media only screen and (min-width:992px) and (max-width:1199px){.hidden-md-only{display:none!important}}@media only screen and (max-width:1199px){.hidden-md-and-down{display:none!important}}@media only screen and (min-width:1200px){.hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919px){.hidden-lg-only{display:none!important}}@media only screen and (max-width:1919px){.hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1920px){.hidden-xl-only{display:none!important}} ================================================ FILE: web/assets/js/axios-init.js ================================================ axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; axios.interceptors.request.use( config => { config.data = Qs.stringify(config.data, { arrayFormat: 'repeat' }); return config; }, error => Promise.reject(error) ); ================================================ FILE: web/assets/js/model/models.js ================================================ class User { constructor() { this.username = ""; this.password = ""; } } class Msg { constructor(success, msg, obj) { this.success = false; this.msg = ""; this.obj = null; if (success != null) { this.success = success; } if (msg != null) { this.msg = msg; } if (obj != null) { this.obj = obj; } } } class DBInbound { constructor(data) { this.id = 0; this.userId = 0; this.up = 0; this.down = 0; this.total = 0; this.remark = ""; this.enable = true; this.expiryTime = 0; this.listen = ""; this.port = 0; this.protocol = ""; this.settings = ""; this.streamSettings = ""; this.tag = ""; this.sniffing = ""; if (data == null) { return; } ObjectUtil.cloneProps(this, data); } get totalGB() { return toFixed(this.total / ONE_GB, 2); } set totalGB(gb) { this.total = toFixed(gb * ONE_GB, 0); } get isVMess() { return this.protocol === Protocols.VMESS; } get isVLess() { return this.protocol === Protocols.VLESS; } get isTrojan() { return this.protocol === Protocols.TROJAN; } get isSS() { return this.protocol === Protocols.SHADOWSOCKS; } get isSocks() { return this.protocol === Protocols.SOCKS; } get isHTTP() { return this.protocol === Protocols.HTTP; } get address() { let address = location.hostname; if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") { address = this.listen; } return address; } get _expiryTime() { if (this.expiryTime === 0) { return null; } return moment(this.expiryTime); } set _expiryTime(t) { if (t == null) { this.expiryTime = 0; } else { this.expiryTime = t.valueOf(); } } get isExpiry() { return this.expiryTime < new Date().getTime(); } toInbound() { let settings = {}; if (!ObjectUtil.isEmpty(this.settings)) { settings = JSON.parse(this.settings); } let streamSettings = {}; if (!ObjectUtil.isEmpty(this.streamSettings)) { streamSettings = JSON.parse(this.streamSettings); } let sniffing = {}; if (!ObjectUtil.isEmpty(this.sniffing)) { sniffing = JSON.parse(this.sniffing); } const config = { port: this.port, listen: this.listen, protocol: this.protocol, settings: settings, streamSettings: streamSettings, tag: this.tag, sniffing: sniffing, }; return Inbound.fromJson(config); } hasLink() { switch (this.protocol) { case Protocols.VMESS: case Protocols.VLESS: case Protocols.TROJAN: case Protocols.SHADOWSOCKS: return true; default: return false; } } genLink() { const inbound = this.toInbound(); return inbound.genLink(this.address, this.remark); } } class AllSetting { constructor(data) { this.webListen = ""; this.webPort = 54321; this.webCertFile = ""; this.webKeyFile = ""; this.webBasePath = "/"; this.tgBotEnable = false; this.tgBotToken = ""; this.tgBotChatId = 0; this.tgRunTime = ""; this.xrayTemplateConfig = ""; this.timeLocation = "Asia/Shanghai"; if (data == null) { return } ObjectUtil.cloneProps(this, data); } equals(other) { return ObjectUtil.equals(this, other); } } ================================================ FILE: web/assets/js/model/xray.js ================================================ const Protocols = { VMESS: 'vmess', VLESS: 'vless', TROJAN: 'trojan', SHADOWSOCKS: 'shadowsocks', DOKODEMO: 'dokodemo-door', SOCKS: 'socks', HTTP: 'http', }; const VmessMethods = { AES_128_GCM: 'aes-128-gcm', CHACHA20_POLY1305: 'chacha20-poly1305', AUTO: 'auto', NONE: 'none', }; const SSMethods = { // AES_256_CFB: 'aes-256-cfb', // AES_128_CFB: 'aes-128-cfb', // CHACHA20: 'chacha20', // CHACHA20_IETF: 'chacha20-ietf', CHACHA20_POLY1305: 'chacha20-poly1305', AES_256_GCM: 'aes-256-gcm', AES_128_GCM: 'aes-128-gcm', BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm', BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm', BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', }; const RULE_IP = { PRIVATE: 'geoip:private', CN: 'geoip:cn', }; const RULE_DOMAIN = { ADS: 'geosite:category-ads', ADS_ALL: 'geosite:category-ads-all', CN: 'geosite:cn', GOOGLE: 'geosite:google', FACEBOOK: 'geosite:facebook', SPEEDTEST: 'geosite:speedtest', }; const FLOW_VISION = { RPRXVISION: "xtls-rprx-vision" }; const TLS_VERSION_OPTION = { TLS10: "1.0", TLS11: "1.1", TLS12: "1.2", TLS13: "1.3", }; const TLS_CIPHER_OPTION = { AES_128_GCM: "TLS_AES_128_GCM_SHA256", AES_256_GCM: "TLS_AES_256_GCM_SHA384", CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256", ECDHE_ECDSA_AES_128_CBC: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", ECDHE_ECDSA_AES_256_CBC: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", ECDHE_ECDSA_AES_128_GCM: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ECDHE_ECDSA_AES_256_GCM: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", ECDHE_RSA_AES_128_GCM: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", ECDHE_RSA_AES_256_GCM: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", ECDHE_ECDSA_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", ECDHE_RSA_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", }; const UTLS_FINGERPRINT = { UTLS_CHROME: "chrome", UTLS_FIREFOX: "firefox", UTLS_SAFARI: "safari", UTLS_IOS: "ios", UTLS_android: "android", UTLS_EDGE: "edge", UTLS_360: "360", UTLS_QQ: "qq", UTLS_RANDOM: "random", UTLS_RANDOMIZED: "randomized", }; const SNIFFING_OPTION = { HTTP: "http", TLS: "tls", QUIC: "quic", }; const ALPN_OPTION = { H3: "h3", H2: "h2", HTTP1: "http/1.1", }; const TCP_CONGESTION = { bbr: "bbr", cubic: "cubic", reno: "reno", }; const DOMAIN_STRATEGY = { AsIs: "AsIs", UseIP: "UseIP", UseIPv4: "UseIPv4", UseIPv6: "UseIPv6", }; const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join(''); const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16))); Object.freeze(Protocols); Object.freeze(VmessMethods); Object.freeze(SSMethods); Object.freeze(RULE_IP); Object.freeze(RULE_DOMAIN); Object.freeze(FLOW_VISION); Object.freeze(TLS_VERSION_OPTION); Object.freeze(TLS_CIPHER_OPTION); Object.freeze(ALPN_OPTION); Object.freeze(SNIFFING_OPTION); Object.freeze(TCP_CONGESTION); Object.freeze(DOMAIN_STRATEGY); class XrayCommonClass { static toJsonArray(arr) { return arr.map(obj => obj.toJson()); } static fromJson() { return new XrayCommonClass(); } toJson() { return this; } toString(format = true) { return format ? JSON.stringify(this.toJson(), null, 2) : JSON.stringify(this.toJson()); } static toHeaders(v2Headers) { let newHeaders = []; if (v2Headers) { Object.keys(v2Headers).forEach(key => { let values = v2Headers[key]; if (typeof (values) === 'string') { newHeaders.push({ name: key, value: values }); } else { for (let i = 0; i < values.length; ++i) { newHeaders.push({ name: key, value: values[i] }); } } }); } return newHeaders; } static toV2Headers(headers, arr = true) { let v2Headers = {}; for (let i = 0; i < headers.length; ++i) { let name = headers[i].name; let value = headers[i].value; if (ObjectUtil.isEmpty(name) || ObjectUtil.isEmpty(value)) { continue; } if (!(name in v2Headers)) { v2Headers[name] = arr ? [value] : value; } else { if (arr) { v2Headers[name].push(value); } else { v2Headers[name] = value; } } } return v2Headers; } } class TcpStreamSettings extends XrayCommonClass { constructor(type = 'none', request = new TcpStreamSettings.TcpRequest(), response = new TcpStreamSettings.TcpResponse(), ) { super(); this.type = type; this.request = request; this.response = response; } static fromJson(json = {}) { let header = json.header; if (!header) { header = {}; } return new TcpStreamSettings( header.type, TcpStreamSettings.TcpRequest.fromJson(header.request), TcpStreamSettings.TcpResponse.fromJson(header.response), ); } toJson() { return { header: { type: this.type, request: this.type === 'http' ? this.request.toJson() : undefined, response: this.type === 'http' ? this.response.toJson() : undefined, }, }; } } TcpStreamSettings.TcpRequest = class extends XrayCommonClass { constructor(version = '1.1', method = 'GET', path = ['/'], headers = [], ) { super(); this.version = version; this.method = method; this.path = path.length === 0 ? ['/'] : path; this.headers = headers; } addPath(path) { this.path.push(path); } removePath(index) { this.path.splice(index, 1); } addHeader(name, value) { this.headers.push({ name: name, value: value }); } getHeader(name) { for (const header of this.headers) { if (header.name.toLowerCase() === name.toLowerCase()) { return header.value; } } return null; } removeHeader(index) { this.headers.splice(index, 1); } static fromJson(json = {}) { return new TcpStreamSettings.TcpRequest( json.version, json.method, json.path, XrayCommonClass.toHeaders(json.headers), ); } toJson() { return { method: this.method, path: ObjectUtil.clone(this.path), headers: XrayCommonClass.toV2Headers(this.headers), }; } }; TcpStreamSettings.TcpResponse = class extends XrayCommonClass { constructor(version = '1.1', status = '200', reason = 'OK', headers = [], ) { super(); this.version = version; this.status = status; this.reason = reason; this.headers = headers; } addHeader(name, value) { this.headers.push({ name: name, value: value }); } removeHeader(index) { this.headers.splice(index, 1); } static fromJson(json = {}) { return new TcpStreamSettings.TcpResponse( json.version, json.status, json.reason, XrayCommonClass.toHeaders(json.headers), ); } toJson() { return { version: this.version, status: this.status, reason: this.reason, headers: XrayCommonClass.toV2Headers(this.headers), }; } }; class KcpStreamSettings extends XrayCommonClass { constructor(mtu = 1350, tti = 20, uplinkCapacity = 5, downlinkCapacity = 20, congestion = false, readBufferSize = 2, writeBufferSize = 2, type = 'none', seed = RandomUtil.randomSeq(10), ) { super(); this.mtu = mtu; this.tti = tti; this.upCap = uplinkCapacity; this.downCap = downlinkCapacity; this.congestion = congestion; this.readBuffer = readBufferSize; this.writeBuffer = writeBufferSize; this.type = type; this.seed = seed; } static fromJson(json = {}) { return new KcpStreamSettings( json.mtu, json.tti, json.uplinkCapacity, json.downlinkCapacity, json.congestion, json.readBufferSize, json.writeBufferSize, ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type, json.seed, ); } toJson() { return { mtu: this.mtu, tti: this.tti, uplinkCapacity: this.upCap, downlinkCapacity: this.downCap, congestion: this.congestion, readBufferSize: this.readBuffer, writeBufferSize: this.writeBuffer, header: { type: this.type, }, seed: this.seed, }; } } class WsStreamSettings extends XrayCommonClass { constructor(path = '/', headers = []) { super(); this.path = path; this.headers = headers; } addHeader(name, value) { this.headers.push({ name: name, value: value }); } getHeader(name) { for (const header of this.headers) { if (header.name.toLowerCase() === name.toLowerCase()) { return header.value; } } return null; } removeHeader(index) { this.headers.splice(index, 1); } static fromJson(json = {}) { return new WsStreamSettings( json.path, XrayCommonClass.toHeaders(json.headers), ); } toJson() { return { path: this.path, headers: XrayCommonClass.toV2Headers(this.headers, false), }; } } class HttpStreamSettings extends XrayCommonClass { constructor(path = '/', host = ['']) { super(); this.path = path; this.host = host.length === 0 ? [''] : host; } addHost(host) { this.host.push(host); } removeHost(index) { this.host.splice(index, 1); } static fromJson(json = {}) { return new HttpStreamSettings(json.path, json.host); } toJson() { let host = []; for (let i = 0; i < this.host.length; ++i) { if (!ObjectUtil.isEmpty(this.host[i])) { host.push(this.host[i]); } } return { path: this.path, host: host, } } } class QuicStreamSettings extends XrayCommonClass { constructor(security = VmessMethods.NONE, key = '', type = 'none',) { super(); this.security = security; this.key = key; this.type = type; } static fromJson(json = {}) { return new QuicStreamSettings( json.security, json.key, json.header ? json.header.type : 'none', ); } toJson() { return { security: this.security, key: this.key, header: { type: this.type, } } } } class GrpcStreamSettings extends XrayCommonClass { constructor(serviceName = "") { super(); this.serviceName = serviceName; } static fromJson(json = {}) { return new GrpcStreamSettings(json.serviceName); } toJson() { return { serviceName: this.serviceName, } } } class TlsStreamSettings extends XrayCommonClass { constructor(serverName = '', rejectUnknownSni = false, minVersion = TLS_VERSION_OPTION.TLS12, maxVersion = TLS_VERSION_OPTION.TLS13, cipherSuites = '', allowInsecure = false, fingerprint = UTLS_FINGERPRINT.UTLS_RANDOM, certificates = [new TlsStreamSettings.Cert()], alpn = ['']) { super(); this.server = serverName; this.rejectUnknownSni = rejectUnknownSni; this.minVersion = minVersion; this.maxVersion = maxVersion; this.cipherSuites = cipherSuites; this.allowInsecure = allowInsecure; this.fingerprint = fingerprint; this.certs = certificates; this.alpn = alpn; } addCert(cert) { this.certs.push(cert); } removeCert(index) { this.certs.splice(index, 1); } static fromJson(json = {}) { let certs; if (!ObjectUtil.isEmpty(json.certificates)) { certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert)); } return new TlsStreamSettings( json.serverName, json.rejectUnknownSni, json.minVersion, json.maxVersion, json.cipherSuites, json.allowInsecure, json.fingerprint, certs, json.alpn, ); } toJson() { return { serverName: this.server, rejectUnknownSni: this.rejectUnknownSni, minVersion: this.minVersion, maxVersion: this.maxVersion, cipherSuites: this.cipherSuites instanceof Array ? this.cipherSuites.join(':') : this.cipherSuites, allowInsecure: this.allowInsecure, fingerprint: this.fingerprint, certificates: TlsStreamSettings.toJsonArray(this.certs), alpn: this.alpn, }; } } TlsStreamSettings.Cert = class extends XrayCommonClass { constructor(useFile = true, ocspStapling = 3600, certificateFile = '', keyFile = '', certificate = '', key = '') { super(); this.useFile = useFile; this.ocspStapling = ocspStapling; this.certFile = certificateFile; this.keyFile = keyFile; this.cert = certificate instanceof Array ? certificate.join('\n') : certificate; this.key = key instanceof Array ? key.join('\n') : key; } static fromJson(json = {}) { if ('certificateFile' in json && 'keyFile' in json) { return new TlsStreamSettings.Cert( true, json.ocspStapling, json.certificateFile, json.keyFile, ); } else { return new TlsStreamSettings.Cert( false, json.ocspStapling, '', '', json.certificate.join('\n'), json.key.join('\n'), ); } } toJson() { if (this.useFile) { return { ocspStapling: this.ocspStapling, certificateFile: this.certFile, keyFile: this.keyFile, }; } else { return { ocspStapling: this.ocspStapling, certificate: this.cert.split('\n'), key: this.key.split('\n'), }; } } }; class RealityStreamSettings extends XrayCommonClass { constructor( show = false, dest = "www.sega.com:443", xver = 0, fingerprint = UTLS_FINGERPRINT.UTLS_RANDOM, serverNames = 'www.sega.com', privateKey = RandomUtil.randomX25519PrivateKey(), publicKey = '', minClientVer = "", maxClientVer = "", maxTimeDiff = 0, shortIds = RandomUtil.randowShortId() ) { super(); this.show = show; this.dest = dest; this.xver = xver; this.fingerprint = fingerprint; this.serverNames = serverNames instanceof Array ? serverNames.join('\n') : serverNames; this.privateKey = privateKey; this.publicKey = RandomUtil.randomX25519PublicKey(this.privateKey); this.minClientVer = minClientVer; this.maxClientVer = maxClientVer; this.maxTimeDiff = maxTimeDiff; this.shortIds = shortIds instanceof Array ? shortIds.join('\n') : shortIds; } static fromJson(json = {}) { return new RealityStreamSettings( json.show, json.dest, json.xver, json.fingerprint, json.serverNames, json.privateKey, json.publicKey, json.minClientVer, json.maxClientVer, json.maxTimeDiff, json.shortIds, ); } toJson() { return { show: this.show, dest: this.dest, xver: this.xver, fingerprint: this.fingerprint, serverNames: this.serverNames.split(/,|,|\s+/), privateKey: this.privateKey, publicKey: this.publicKey, minClientVer: this.minClientVer, maxClientVer: this.maxClientVer, maxTimeDiff: this.maxTimeDiff, shortIds: this.shortIds.split(/,|,|\s+/), } } } class SockoptStreamSettings extends XrayCommonClass { constructor(tcpMaxSeg = 1440, tcpFastOpen = false, domainStrategy = DOMAIN_STRATEGY.AsIs, tcpcongestion = '', acceptProxyProtocol = false, tcpKeepAliveIdle = 0, tcpKeepAliveInterval = 0, tcpUserTimeout = 10000, _interface = "", ) { super(); this.tcpMaxSeg = tcpMaxSeg; this.tcpFastOpen = tcpFastOpen; this.domainStrategy = domainStrategy; this.tcpcongestion = tcpcongestion; this.acceptProxyProtocol = acceptProxyProtocol; this.tcpKeepAliveIdle = tcpKeepAliveIdle; this.tcpKeepAliveInterval = tcpKeepAliveInterval; this.tcpUserTimeout = tcpUserTimeout; this.tcpcongestion = tcpcongestion; this.interface = _interface instanceof Array ? this.interface : _interface; } static fromJson(json = {}) { return new SockoptStreamSettings( json.tcpMaxSeg, json.tcpFastOpen, json.domainStrategy, json.tcpcongestion, json.acceptProxyProtocol, json.tcpKeepAliveIdle, json.tcpKeepAliveInterval, json.tcpUserTimeout, json.tcpcongestion, json.interface, ); } toJson() { return { tcpMaxSeg: this.tcpMaxSeg, tcpFastOpen: this.tcpFastOpen, domainStrategy: this.domainStrategy, tcpcongestion: this.tcpcongestion, acceptProxyProtocol: this.acceptProxyProtocol, tcpKeepAliveIdle: this.tcpKeepAliveIdle, tcpKeepAliveInterval: this.tcpKeepAliveInterval, tcpUserTimeout: this.tcpUserTimeout, tcpcongestion: this.tcpcongestion, interface: this.interface, }; } } class StreamSettings extends XrayCommonClass { constructor(network = 'tcp', security = 'none', tlsSettings = new TlsStreamSettings(), realitySettings = new RealityStreamSettings(), tcpSettings = new TcpStreamSettings(), kcpSettings = new KcpStreamSettings(), wsSettings = new WsStreamSettings(), httpSettings = new HttpStreamSettings(), quicSettings = new QuicStreamSettings(), grpcSettings = new GrpcStreamSettings(), sockopt = new SockoptStreamSettings(), ) { super(); this.network = network; this.security = security; this.tls = tlsSettings; this.reality = realitySettings; this.tcp = tcpSettings; this.kcp = kcpSettings; this.ws = wsSettings; this.http = httpSettings; this.quic = quicSettings; this.grpc = grpcSettings; this.sockopt = sockopt; } get isTls() { return this.security === 'tls'; } set isTls(isTls) { if (isTls) { this.security = 'tls'; } else { this.security = 'none'; } } get isReality() { return this.security === "reality"; } set isReality(isReality) { if (isReality) { this.security = 'reality'; } else { this.security = 'none'; } } get isSockopt() { return ['tcp', 'http', 'grpc', 'ws'].indexOf(this.network) !== -1; } set isSockopt(isSockopt) { if (isSockopt) { return ['tcp', 'http', 'grpc', 'ws'].indexOf(this.network) !== -1; } } static fromJson(json = {}) { return new StreamSettings( json.network, json.security, TlsStreamSettings.fromJson(json.tlsSettings), RealityStreamSettings.fromJson(json.realitySettings), TcpStreamSettings.fromJson(json.tcpSettings), KcpStreamSettings.fromJson(json.kcpSettings), WsStreamSettings.fromJson(json.wsSettings), HttpStreamSettings.fromJson(json.httpSettings), QuicStreamSettings.fromJson(json.quicSettings), GrpcStreamSettings.fromJson(json.grpcSettings), SockoptStreamSettings.fromJson(json.sockopt), ); } toJson() { const network = this.network; return { network: network, security: this.security, tlsSettings: this.isTls ? this.tls.toJson() : undefined, realitySettings: this.isReality ? this.reality.toJson() : undefined, tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, wsSettings: network === 'ws' ? this.ws.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined, quicSettings: network === 'quic' ? this.quic.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, sockopt: this.isSockopt ? this.sockopt.toJson() : undefined, }; } } class Sniffing extends XrayCommonClass { constructor(enabled = true, destOverride = ['http', 'tls', 'quic']) { super(); this.enabled = enabled; this.destOverride = destOverride; } static fromJson(json = {}) { let destOverride = ObjectUtil.clone(json.destOverride); if (!ObjectUtil.isEmpty(destOverride) && !ObjectUtil.isArrEmpty(destOverride)) { if (ObjectUtil.isEmpty(destOverride[0])) { destOverride = ['http', 'tls', 'quic']; } } return new Sniffing( !!json.enabled, destOverride, ); } } class Inbound extends XrayCommonClass { constructor(port = RandomUtil.randomIntRange(10000, 60000), listen = '', protocol = Protocols.VMESS, settings = null, streamSettings = new StreamSettings(), tag = '', sniffing = new Sniffing(), ) { super(); this.port = port; this.listen = listen; this._protocol = protocol; this.settings = ObjectUtil.isEmpty(settings) ? Inbound.Settings.getSettings(protocol) : settings; this.stream = streamSettings; this.tag = tag; this.sniffing = sniffing; } get protocol() { return this._protocol; } set protocol(protocol) { this._protocol = protocol; this.settings = Inbound.Settings.getSettings(protocol); if (protocol === Protocols.TROJAN) { if (this.network === "tcp") { this.tls = true; } } } get tls() { return this.stream.security === 'tls'; } set tls(isTls) { if (isTls) { this.stream.security = 'tls'; } else { this.stream.security = 'none'; } } get reality() { return this.stream.security === 'reality'; } set reality(isReality) { if (isReality) { this.stream.security = 'reality'; } else { this.stream.security = 'none'; } } get network() { return this.stream.network; } set network(network) { this.stream.network = network; } get isTcp() { return this.network === "tcp"; } get isWs() { return this.network === "ws"; } get isKcp() { return this.network === "kcp"; } get isQuic() { return this.network === "quic" } get isGrpc() { return this.network === "grpc"; } get isH2() { return this.network === "http"; } // VMess & VLess get uuid() { switch (this.protocol) { case Protocols.VMESS: return this.settings.vmesses[0].id; case Protocols.VLESS: return this.settings.vlesses[0].id; default: return ""; } } // VLess & Trojan get flow() { switch (this.protocol) { case Protocols.VLESS: return this.settings.vlesses[0].flow; case Protocols.TROJAN: return this.settings.clients[0].flow; default: return ""; } } // Socks & HTTP get username() { switch (this.protocol) { case Protocols.SOCKS: case Protocols.HTTP: return this.settings.accounts[0].user; default: return ""; } } // Trojan & Shadowsocks & Socks & HTTP get password() { switch (this.protocol) { case Protocols.TROJAN: return this.settings.clients[0].password; case Protocols.SHADOWSOCKS: return this.settings.password; case Protocols.SOCKS: case Protocols.HTTP: return this.settings.accounts[0].pass; default: return ""; } } // Shadowsocks get method() { switch (this.protocol) { case Protocols.SHADOWSOCKS: return this.settings.method; default: return ""; } } get serverName() { if (this.stream.isTls) { return this.stream.tls.server; } return ""; } get host() { if (this.isTcp) { return this.stream.tcp.request.getHeader("Host"); } else if (this.isWs) { return this.stream.ws.getHeader("Host"); } else if (this.isH2) { return this.stream.http.host[0]; } return null; } get path() { if (this.isTcp) { return this.stream.tcp.request.path[0]; } else if (this.isWs) { return this.stream.ws.path; } else if (this.isH2) { return this.stream.http.path[0]; } return null; } get quicSecurity() { return this.stream.quic.security; } get quicKey() { return this.stream.quic.key; } get quicType() { return this.stream.quic.type; } get kcpType() { return this.stream.kcp.type; } get kcpSeed() { return this.stream.kcp.seed; } get serviceName() { return this.stream.grpc.serviceName; } canEnableTls() { switch (this.protocol) { case Protocols.VMESS: case Protocols.VLESS: case Protocols.TROJAN: case Protocols.SHADOWSOCKS: break; default: return false; } switch (this.network) { case "tcp": case "ws": case "http": case "quic": case "grpc": return true; default: return false; } } canSetTls() { return this.canEnableTls(); } canEnableReality() { switch (this.protocol) { case Protocols.VLESS: case Protocols.TROJAN: break; default: return false; } return ['tcp', 'http', 'grpc'].indexOf(this.network) !== -1; //return this.network === "tcp"; } canSockopt() { switch (this.protocol) { case Protocols.VLESS: case Protocols.TROJAN: case Protocols.SHADOWSOCKS: case Protocols.VMESS: break; default: return false; } return ['tcp', 'http', 'grpc', 'ws'].indexOf(this.network) !== -1; //return this.network === "tcp"; } canEnableStream() { switch (this.protocol) { case Protocols.VMESS: case Protocols.VLESS: case Protocols.SHADOWSOCKS: case Protocols.TROJAN: return true; default: return false; } } canSniffing() { switch (this.protocol) { case Protocols.VMESS: case Protocols.VLESS: case Protocols.TROJAN: case Protocols.SHADOWSOCKS: return true; default: return false; } } reset() { this.port = RandomUtil.randomIntRange(10000, 60000); this.listen = ''; this.protocol = Protocols.VMESS; this.settings = Inbound.Settings.getSettings(Protocols.VMESS); this.stream = new StreamSettings(); this.tag = ''; this.sniffing = new Sniffing(); } genVmessLink(address = '', remark = '') { if (this.protocol !== Protocols.VMESS) { return ''; } let network = this.stream.network; let type = 'none'; let host = ''; let path = ''; let fingerprint = ''; if (network === 'tcp') { let tcp = this.stream.tcp; type = tcp.type; if (type === 'http') { let request = tcp.request; path = request.path.join(','); let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { host = request.headers[index].value; } } } else if (network === 'kcp') { let kcp = this.stream.kcp; type = kcp.type; path = kcp.seed; } else if (network === 'ws') { let ws = this.stream.ws; path = ws.path; let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { host = ws.headers[index].value; } } else if (network === 'http') { network = 'h2'; path = this.stream.http.path; host = this.stream.http.host.join(','); } else if (network === 'quic') { type = this.stream.quic.type; host = this.stream.quic.security; path = this.stream.quic.key; } else if (network === 'grpc') { path = this.stream.grpc.serviceName; } if (this.stream.security === 'tls') { if (!ObjectUtil.isEmpty(this.stream.tls.server)) { address = this.stream.tls.server; } if (this.stream.tls.fingerprint != "") { fingerprint = this.stream.reality.fingerprint } } let obj = { v: '2', ps: remark, add: address, port: this.port, id: this.settings.vmesses[0].id, aid: 0, net: network, type: type, host: host, path: path, tls: this.stream.security, sni: host, alpn: this.stream.tls.alpn.join(','), allowInsecure: this.stream.tls.allowInsecure, fp: fingerprint, }; return 'vmess://' + base64(JSON.stringify(obj, null, 2)); } genVLESSLink(address = '', remark = '') { const settings = this.settings; const uuid = settings.vlesses[0].id; const port = this.port; const type = this.stream.network; const params = new Map(); params.set("type", this.stream.network); if (this.reality) { params.set("security", "reality"); } else { params.set("security", this.stream.security); } switch (type) { case "tcp": const tcp = this.stream.tcp; if (tcp.type === 'http') { const request = tcp.request; params.set("path", request.path.join(',')); const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { const host = request.headers[index].value; params.set("host", host); } } break; case "kcp": const kcp = this.stream.kcp; params.set("headerType", kcp.type); params.set("seed", kcp.seed); break; case "ws": const ws = this.stream.ws; params.set("path", ws.path); const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { const host = ws.headers[index].value; params.set("host", host); } break; case "http": const http = this.stream.http; params.set("path", http.path); params.set("host", http.host); break; case "quic": const quic = this.stream.quic; params.set("quicSecurity", quic.security); params.set("key", quic.key); params.set("headerType", quic.type); break; case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); break; } if (this.stream.security === 'tls') { params.set("alpn", this.stream.tls.alpn); if (this.stream.tls.allowInsecure) { params.set("allowInsecure", "1"); } if (!ObjectUtil.isEmpty(this.stream.tls.server)) { address = this.stream.tls.server; params.set("sni", address); params.set("flow", this.settings.vlesses[0].flow); } if (this.stream.tls.fingerprint != "") { params.set("fp", this.stream.tls.fingerprint); } } if (this.stream.security === 'reality') { if (this.stream.network === 'tcp') { params.set("flow", this.settings.vlesses[0].flow); } if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); } if (this.stream.reality.publicKey != "") { params.set("pbk", this.stream.reality.publicKey); } if (this.stream.reality.shortIds != "") { params.set("sid", this.stream.reality.shortIds); } if (this.stream.reality.fingerprint != "") { params.set("fp", this.stream.reality.fingerprint); } } const link = `vless://${uuid}@${address}:${port}`; const url = new URL(link); for (const [key, value] of params) { url.searchParams.set(key, value) } url.hash = encodeURIComponent(remark); return url.toString(); } genSSLink(address = '', remark = '') { let settings = this.settings; const server = this.stream.tls.server; if (!ObjectUtil.isEmpty(server)) { address = server; } if (settings.method == SSMethods.BLAKE3_AES_128_GCM || settings.method == SSMethods.BLAKE3_AES_256_GCM || settings.method == SSMethods.BLAKE3_CHACHA20_POLY1305) { return `ss://${settings.method}:${settings.password}@${address}:${this.port}#${encodeURIComponent(remark)}`; } else { return 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port) + '#' + encodeURIComponent(remark); } } genTrojanLink(address = '', remark = '') { let settings = this.settings; const port = this.port; const type = this.stream.network; const params = new Map(); params.set("type", this.stream.network); if (this.reality) { params.set("security", "reality"); } else { params.set("security", this.stream.security); } switch (type) { case "tcp": const tcp = this.stream.tcp; if (tcp.type === 'http') { const request = tcp.request; params.set("path", request.path.join(',')); const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { const host = request.headers[index].value; params.set("host", host); } } break; case "kcp": const kcp = this.stream.kcp; params.set("headerType", kcp.type); params.set("seed", kcp.seed); break; case "ws": const ws = this.stream.ws; params.set("path", ws.path); const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); if (index >= 0) { const host = ws.headers[index].value; params.set("host", host); } break; case "http": const http = this.stream.http; params.set("path", http.path); params.set("host", http.host); break; case "quic": const quic = this.stream.quic; params.set("quicSecurity", quic.security); params.set("key", quic.key); params.set("headerType", quic.type); break; case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); break; } if (this.stream.security === 'tls') { params.set("alpn", this.stream.tls.alpn); if (this.stream.tls.allowInsecure) { params.set("allowInsecure", "1"); } if (!ObjectUtil.isEmpty(this.stream.tls.server)) { address = this.stream.tls.server; params.set("sni", address); } if (this.stream.tls.fingerprint != "") { params.set("fp", this.stream.tls.fingerprint); } } if (this.stream.security === 'reality') { if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); } if (this.stream.reality.publicKey != "") { params.set("pbk", this.stream.reality.publicKey); } if (this.stream.reality.shortIds != "") { params.set("sid", this.stream.reality.shortIds); } if (this.stream.reality.fingerprint != "") { params.set("fp", this.stream.reality.fingerprint); } } const link = `trojan://${settings.clients[0].password}@${address}:${port}`; const url = new URL(link); for (const [key, value] of params) { url.searchParams.set(key, value) } url.hash = encodeURIComponent(remark); return url.toString(); } genLink(address = '', remark = '') { switch (this.protocol) { case Protocols.VMESS: return this.genVmessLink(address, remark); case Protocols.VLESS: return this.genVLESSLink(address, remark); case Protocols.SHADOWSOCKS: return this.genSSLink(address, remark); case Protocols.TROJAN: return this.genTrojanLink(address, remark); default: return ''; } } static fromJson(json = {}) { return new Inbound( json.port, json.listen, json.protocol, Inbound.Settings.fromJson(json.protocol, json.settings), StreamSettings.fromJson(json.streamSettings), json.tag, Sniffing.fromJson(json.sniffing), ) } toJson() { let streamSettings; if (this.canEnableStream() || this.protocol === Protocols.TROJAN) { streamSettings = this.stream.toJson(); } return { port: this.port, listen: this.listen, protocol: this.protocol, settings: this.settings instanceof XrayCommonClass ? this.settings.toJson() : this.settings, streamSettings: streamSettings, tag: this.tag, sniffing: this.sniffing.toJson(), }; } } Inbound.Settings = class extends XrayCommonClass { constructor(protocol) { super(); this.protocol = protocol; } static getSettings(protocol) { switch (protocol) { case Protocols.VMESS: return new Inbound.VmessSettings(protocol); case Protocols.VLESS: return new Inbound.VLESSSettings(protocol); case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol); case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); case Protocols.HTTP: return new Inbound.HttpSettings(protocol); default: return null; } } static fromJson(protocol, json) { switch (protocol) { case Protocols.VMESS: return Inbound.VmessSettings.fromJson(json); case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json); case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json); case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json); case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); default: return null; } } toJson() { return {}; } }; Inbound.VmessSettings = class extends Inbound.Settings { constructor(protocol, vmesses = [new Inbound.VmessSettings.Vmess()], disableInsecureEncryption = false) { super(protocol); this.vmesses = vmesses; this.disableInsecure = disableInsecureEncryption; } indexOfVmessById(id) { return this.vmesses.findIndex(vmess => vmess.id === id); } addVmess() { this.vmesses.push(new Inbound.VmessSettings.Vmess()); } delVmess(index) { this.vmesses.splice(index, 1); } static fromJson(json = {}) { return new Inbound.VmessSettings( Protocols.VMESS, json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)), ObjectUtil.isEmpty(json.disableInsecureEncryption) ? false : json.disableInsecureEncryption, ); } toJson() { return { clients: Inbound.VmessSettings.toJsonArray(this.vmesses), disableInsecureEncryption: this.disableInsecure, }; } }; Inbound.VmessSettings.Vmess = class extends XrayCommonClass { constructor(id = RandomUtil.randomUUID()) { super(); this.id = id; } static fromJson(json = {}) { return new Inbound.VmessSettings.Vmess( json.id, ); } }; Inbound.VLESSSettings = class extends Inbound.Settings { constructor(protocol, vlesses = [new Inbound.VLESSSettings.VLESS()], decryption = 'none', fallbacks = [],) { super(protocol); this.vlesses = vlesses; this.decryption = decryption; this.fallbacks = fallbacks; } addVLESS() { this.vlesses.push(new Inbound.VLESSSettings.VLESS()); } delVLESS(index) { this.vlesses.splice(index, 1); } addFallback() { this.fallbacks.push(new Inbound.VLESSSettings.Fallback()); } delFallback(index) { this.fallbacks.splice(index, 1); } static fromJson(json = {}) { return new Inbound.VLESSSettings( Protocols.VLESS, json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)), json.decryption, Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks), ); } toJson() { return { clients: Inbound.VLESSSettings.toJsonArray(this.vlesses), decryption: this.decryption, fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks), }; } }; Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { constructor(id = RandomUtil.randomUUID(), flow = "") { super(); this.id = id; this.flow = flow; } static fromJson(json = {}) { return new Inbound.VLESSSettings.VLESS( json.id, json.flow, ); } }; Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { super(); this.name = name; this.alpn = alpn; this.path = path; this.dest = dest; this.xver = xver; } toJson() { let xver = this.xver; if (!Number.isInteger(xver)) { xver = 0; } return { name: this.name, alpn: this.alpn, path: this.path, dest: this.dest, xver: xver, } } static fromJson(json = []) { const fallbacks = []; for (let fallback of json) { fallbacks.push(new Inbound.VLESSSettings.Fallback( fallback.name, fallback.alpn, fallback.path, fallback.dest, fallback.xver, )) } return fallbacks; } }; Inbound.TrojanSettings = class extends Inbound.Settings { constructor(protocol, clients = [new Inbound.TrojanSettings.Client()], fallbacks = [],) { super(protocol); this.clients = clients; this.fallbacks = fallbacks; } addTrojan() { this.clients.push(new Inbound.TrojanSettings.Client()); } delTrojan(index) { this.clients.splice(index, 1); } addTrojanFallback() { this.fallbacks.push(new Inbound.TrojanSettings.Fallback()); } delTrojanFallback(index) { this.fallbacks.splice(index, 1); } toJson() { return { clients: Inbound.TrojanSettings.toJsonArray(this.clients), fallbacks: Inbound.TrojanSettings.toJsonArray(this.fallbacks), }; } static fromJson(json = {}) { const clients = []; for (const c of json.clients) { clients.push(Inbound.TrojanSettings.Client.fromJson(c)); } return new Inbound.TrojanSettings( Protocols.TROJAN, clients, Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks)); } }; Inbound.TrojanSettings.Client = class extends XrayCommonClass { constructor(password = RandomUtil.randomSeq(10)) { super(); this.password = password; //this.flow = flow; } toJson() { return { password: this.password, // flow: this.flow, }; } static fromJson(json = {}) { return new Inbound.TrojanSettings.Client( json.password, // json.flow, ); } }; Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { super(); this.name = name; this.alpn = alpn; this.path = path; this.dest = dest; this.xver = xver; } toJson() { let xver = this.xver; if (!Number.isInteger(xver)) { xver = 0; } return { name: this.name, alpn: this.alpn, path: this.path, dest: this.dest, xver: xver, } } static fromJson(json = []) { const fallbacks = []; for (let fallback of json) { fallbacks.push(new Inbound.TrojanSettings.Fallback( fallback.name, fallback.alpn, fallback.path, fallback.dest, fallback.xver, )) } return fallbacks; } }; Inbound.ShadowsocksSettings = class extends Inbound.Settings { constructor(protocol, method = SSMethods.AES_256_GCM, password = btoa(RandomUtil.randomSeq(64)), network = 'tcp,udp' ) { super(protocol); this.method = method; this.password = password; this.network = network; } static fromJson(json = {}) { return new Inbound.ShadowsocksSettings( Protocols.SHADOWSOCKS, json.method, json.password, json.network, ); } toJson() { return { method: this.method, password: this.password, network: this.network, }; } }; Inbound.DokodemoSettings = class extends Inbound.Settings { constructor(protocol, address, port, network = 'tcp,udp') { super(protocol); this.address = address; this.port = port; this.network = network; } static fromJson(json = {}) { return new Inbound.DokodemoSettings( Protocols.DOKODEMO, json.address, json.port, json.network, ); } toJson() { return { address: this.address, port: this.port, network: this.network, }; } }; Inbound.SocksSettings = class extends Inbound.Settings { constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { super(protocol); this.auth = auth; this.accounts = accounts; this.udp = udp; this.ip = ip; } addAccount(account) { this.accounts.push(account); } delAccount(index) { this.accounts.splice(index, 1); } static fromJson(json = {}) { let accounts; if (json.auth === 'password') { accounts = json.accounts.map( account => Inbound.SocksSettings.SocksAccount.fromJson(account) ) } return new Inbound.SocksSettings( Protocols.SOCKS, json.auth, accounts, json.udp, json.ip, ); } toJson() { return { auth: this.auth, accounts: this.auth === 'password' ? this.accounts.map(account => account.toJson()) : undefined, udp: this.udp, ip: this.ip, }; } }; Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { super(); this.user = user; this.pass = pass; } static fromJson(json = {}) { return new Inbound.SocksSettings.SocksAccount(json.user, json.pass); } }; Inbound.HttpSettings = class extends Inbound.Settings { constructor(protocol, accounts = [new Inbound.HttpSettings.HttpAccount()]) { super(protocol); this.accounts = accounts; } addAccount(account) { this.accounts.push(account); } delAccount(index) { this.accounts.splice(index, 1); } static fromJson(json = {}) { return new Inbound.HttpSettings( Protocols.HTTP, json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)), ); } toJson() { return { accounts: Inbound.HttpSettings.toJsonArray(this.accounts), }; } }; Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { super(); this.user = user; this.pass = pass; } static fromJson(json = {}) { return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); } }; ================================================ FILE: web/assets/js/util/common.js ================================================ const ONE_KB = 1024; const ONE_MB = ONE_KB * 1024; const ONE_GB = ONE_MB * 1024; const ONE_TB = ONE_GB * 1024; const ONE_PB = ONE_TB * 1024; function sizeFormat(size) { if (size < ONE_KB) { return size.toFixed(0) + " B"; } else if (size < ONE_MB) { return (size / ONE_KB).toFixed(2) + " KB"; } else if (size < ONE_GB) { return (size / ONE_MB).toFixed(2) + " MB"; } else if (size < ONE_TB) { return (size / ONE_GB).toFixed(2) + " GB"; } else if (size < ONE_PB) { return (size / ONE_TB).toFixed(2) + " TB"; } else { return (size / ONE_PB).toFixed(2) + " PB"; } } function base64(str) { return Base64.encode(str); } function safeBase64(str) { return base64(str) .replace(/\+/g, '-') .replace(/=/g, '') .replace(/\//g, '_'); } function formatSecond(second) { if (second < 60) { return second.toFixed(0) + ' 秒'; } else if (second < 3600) { return (second / 60).toFixed(0) + ' 分钟'; } else if (second < 3600 * 24) { return (second / 3600).toFixed(0) + ' 小时'; } else { return (second / 3600 / 24).toFixed(0) + ' 天'; } } function addZero(num) { if (num < 10) { return "0" + num; } else { return num; } } function toFixed(num, n) { n = Math.pow(10, n); return Math.round(num * n) / n; } ================================================ FILE: web/assets/js/util/date-util.js ================================================ const oneMinute = 1000 * 60; // 一分钟的毫秒数 const oneHour = oneMinute * 60; // 一小时的毫秒数 const oneDay = oneHour * 24; // 一天的毫秒数 const oneWeek = oneDay * 7; // 一星期的毫秒数 const oneMonth = oneDay * 30; // 一个月的毫秒数 /** * 按天数减少 * * @param days 要减少的天数 */ Date.prototype.minusDays = function (days) { return this.minusMillis(oneDay * days); }; /** * 按天数增加 * * @param days 要增加的天数 */ Date.prototype.plusDays = function (days) { return this.plusMillis(oneDay * days); }; /** * 按小时减少 * * @param hours 要减少的小时数 */ Date.prototype.minusHours = function (hours) { return this.minusMillis(oneHour * hours); }; /** * 按小时增加 * * @param hours 要增加的小时数 */ Date.prototype.plusHours = function (hours) { return this.plusMillis(oneHour * hours); }; /** * 按分钟减少 * * @param minutes 要减少的分钟数 */ Date.prototype.minusMinutes = function (minutes) { return this.minusMillis(oneMinute * minutes); }; /** * 按分钟增加 * * @param minutes 要增加的分钟数 */ Date.prototype.plusMinutes = function (minutes) { return this.plusMillis(oneMinute * minutes); }; /** * 按毫秒减少 * * @param millis 要减少的毫秒数 */ Date.prototype.minusMillis = function(millis) { let time = this.getTime() - millis; let newDate = new Date(); newDate.setTime(time); return newDate; }; /** * 按毫秒增加 * * @param millis 要增加的毫秒数 */ Date.prototype.plusMillis = function(millis) { let time = this.getTime() + millis; let newDate = new Date(); newDate.setTime(time); return newDate; }; /** * 设置时间为当天的 00:00:00.000 */ Date.prototype.setMinTime = function () { this.setHours(0); this.setMinutes(0); this.setSeconds(0); this.setMilliseconds(0); return this; }; /** * 设置时间为当天的 23:59:59.999 */ Date.prototype.setMaxTime = function () { this.setHours(23); this.setMinutes(59); this.setSeconds(59); this.setMilliseconds(999); return this; }; /** * 格式化日期 */ Date.prototype.formatDate = function () { return this.getFullYear() + "-" + addZero(this.getMonth() + 1) + "-" + addZero(this.getDate()); }; /** * 格式化时间 */ Date.prototype.formatTime = function () { return addZero(this.getHours()) + ":" + addZero(this.getMinutes()) + ":" + addZero(this.getSeconds()); }; /** * 格式化日期加时间 * * @param split 日期和时间之间的分隔符,默认是一个空格 */ Date.prototype.formatDateTime = function (split = ' ') { return this.formatDate() + split + this.formatTime(); }; class DateUtil { // 字符串转 Date 对象 static parseDate(str) { return new Date(str.replace(/-/g, '/')); } static formatMillis(millis) { return moment(millis).format('YYYY-M-D H:m:s') } static firstDayOfMonth() { const date = new Date(); date.setDate(1); date.setMinTime(); return date; } } ================================================ FILE: web/assets/js/util/utils.js ================================================ class HttpUtil { static _handleMsg(msg) { if (!(msg instanceof Msg)) { return; } if (msg.msg === "") { return; } if (msg.success) { Vue.prototype.$message.success(msg.msg); } else { Vue.prototype.$message.error(msg.msg); } } static _respToMsg(resp) { const data = resp.data; if (data == null) { return new Msg(true); } else if (typeof data === 'object') { if (data.hasOwnProperty('success')) { return new Msg(data.success, data.msg, data.obj); } else { return data; } } else { return new Msg(false, 'unknown data:', data); } } static async get(url, data, options) { let msg; try { const resp = await axios.get(url, data, options); msg = this._respToMsg(resp); } catch (e) { msg = new Msg(false, e.toString()); } this._handleMsg(msg); return msg; } static async post(url, data, options) { let msg; try { const resp = await axios.post(url, data, options); msg = this._respToMsg(resp); } catch (e) { msg = new Msg(false, e.toString()); } this._handleMsg(msg); return msg; } static async postWithModal(url, data, modal) { if (modal) { modal.loading(true); } const msg = await this.post(url, data); if (modal) { modal.loading(false); if (msg instanceof Msg && msg.success) { modal.close(); } } return msg; } } class PromiseUtil { static async sleep(timeout) { await new Promise(resolve => { setTimeout(resolve, timeout) }); } } const seq = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]; const shortIdSeq = [ 'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ]; const x25519Map = new Map( [ ['EH2FWe-Ij_FFAa2u9__-aiErLvVIneP601GOCdlyPWw', "goY3OtfaA4UYbiz7Hn0NysI5QJrK0VT_Chg6RLgUPQU"], ['cKI_6DoMSP1IepeWWXrG3G9nkehl94KYBhagU50g2U0', "VigpKFbSLnHLzBWobZaS1IBmw--giJ51w92y723ajnU"], ['qM2SNyK3NyHB6deWpEP3ITyCGKQFRTna_mlKP0w1QH0', "HYyIGuyNFslmcnNT7mrDdmuXwn4cm7smE_FZbYguKHQ"], ['qCWg5GMEDFd3n1nxDswlIpOHoPUXMLuMOIiLUVzubkI', "rJFC3dUjJxMnVZiUGzmf_LFsJUwFWY-CU5RQgFOHCWM"], ['4NOBxDrEsOhNI3Y3EnVIy_TN-uyBoAjQw6QM0YsOi0s', "CbcY9qc4YuMDJDyyL0OITlU824TBg1O84ClPy27e2RM"], ['eBvFb0M4HpSOwWjtXV8zliiEs_hg56zX4a2LpuuqpEI', "CjulQ2qVIky7ImIfysgQhNX7s_drGLheCGSkVHcLZhc"], ['yEpOzQV04NNcycWVeWtRNTzv5TS-ynTuKRacZCH-6U8', "O9RSr5gSdok2K_tobQnf_scyKVqnCx6C4Jrl7_rCZEQ"], ['CNt6TAUVCwqM6xIBHyni0K3Zqbn2htKQLvLb6XDgh0s', "d9cGLVBrDFS02L2OvkqyqwFZ1Ux3AHs28ehl4Rwiyl0"], ['EInKw-6Wr0rAHXlxxDuZU5mByIzcD3Z-_iWPzXlUL1k', "LlYD2nNVAvyjNvjZGZh4R8PkMIwkc6EycPTvR2LE0nQ"], ['GKIKo7rcXVyle-EUHtGIDtYnDsI6osQmOUl3DTJRAGc', "VcqHivYGGoBkcxOI6cSSjQmneltstkb2OhvO53dyhEM"], ['-FVDzv68IC17fJVlNDlhrrgX44WeBfbhwjWpCQVXGHE', "PGG2EYOvsFt2lAQTD7lqHeRxz2KxvllEDKcUrtizPBU"], ['0H3OJEYEu6XW7woqy7cKh2vzg6YHkbF_xSDTHKyrsn4', "mzevpYbS8kXengBY5p7tt56QE4tS3lwlwRemmkcQeyc"], ['8F8XywN6ci44ES6em2Z0fYYxyptB9uaXY9Hc1WSSPE4', "qCZUdWQZ2H33vWXnOkG8NpxBeq3qn5QWXlfCOWBNkkc"], ['IN0dqfkC10dj-ifRHrg2PmmOrzYs697ajGMwcLbu-1g', "2UW_EO3r7uczPGUUlpJBnMDpDmWUHE2yDzCmXS4sckE"], ['uIcmks5rAhvBe4dRaJOdeSqgxLGGMZhsGk4J4PEKL2s', "F9WJV_74IZp0Ide4hWjiJXk9FRtBUBkUr3mzU-q1lzk"], ] ); class RandomUtil { static randomIntRange(min, max) { return parseInt(Math.random() * (max - min) + min, 10); } static randomShortIdSeq(count) { let str = ''; for (let i = 0; i < count; ++i) { str += shortIdSeq[this.randomInt(16)]; } return str; } static randomInt(n) { return this.randomIntRange(0, n); } static randomSeq(count) { let str = ''; for (let i = 0; i < count; ++i) { str += seq[this.randomInt(62)]; } return str; } static randomLowerAndNum(count) { let str = ''; for (let i = 0; i < count; ++i) { str += seq[this.randomInt(36)]; } return str; } static randomMTSecret() { let str = ''; for (let i = 0; i < 32; ++i) { let index = this.randomInt(16); if (index <= 9) { str += index; } else { str += seq[index - 10]; } } return str; } static randomUUID() { let d = new Date().getTime(); return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { let r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16); }); } static randowShortId() { let str = ''; str += this.randomShortIdSeq(8) return str; } static randomX25519PrivateKey() { let num = x25519Map.size; let index = this.randomInt(num); let cntr = 0; for (let key of x25519Map.keys()) { if (cntr++ === index) { return key; } } } static randomX25519PublicKey(key) { return x25519Map.get(key) } } class ObjectUtil { static getPropIgnoreCase(obj, prop) { for (const name in obj) { if (!obj.hasOwnProperty(name)) { continue; } if (name.toLowerCase() === prop.toLowerCase()) { return obj[name]; } } return undefined; } static deepSearch(obj, key) { if (obj instanceof Array) { for (let i = 0; i < obj.length; ++i) { if (this.deepSearch(obj[i], key)) { return true; } } } else if (obj instanceof Object) { for (let name in obj) { if (!obj.hasOwnProperty(name)) { continue; } if (this.deepSearch(obj[name], key)) { return true; } } } else { return obj.toString().indexOf(key) >= 0; } return false; } static isEmpty(obj) { return obj === null || obj === undefined || obj === ''; } static isArrEmpty(arr) { return !this.isEmpty(arr) && arr.length === 0; } static copyArr(dest, src) { dest.splice(0); for (const item of src) { dest.push(item); } } static clone(obj) { let newObj; if (obj instanceof Array) { newObj = []; this.copyArr(newObj, obj); } else if (obj instanceof Object) { newObj = {}; for (const key of Object.keys(obj)) { newObj[key] = obj[key]; } } else { newObj = obj; } return newObj; } static deepClone(obj) { let newObj; if (obj instanceof Array) { newObj = []; for (const item of obj) { newObj.push(this.deepClone(item)); } } else if (obj instanceof Object) { newObj = {}; for (const key of Object.keys(obj)) { newObj[key] = this.deepClone(obj[key]); } } else { newObj = obj; } return newObj; } static cloneProps(dest, src, ...ignoreProps) { if (dest == null || src == null) { return; } const ignoreEmpty = this.isArrEmpty(ignoreProps); for (const key of Object.keys(src)) { if (!src.hasOwnProperty(key)) { continue; } else if (!dest.hasOwnProperty(key)) { continue; } else if (src[key] === undefined) { continue; } if (ignoreEmpty) { dest[key] = src[key]; } else { let ignore = false; for (let i = 0; i < ignoreProps.length; ++i) { if (key === ignoreProps[i]) { ignore = true; break; } } if (!ignore) { dest[key] = src[key]; } } } } static delProps(obj, ...props) { for (const prop of props) { if (prop in obj) { delete obj[prop]; } } } static execute(func, ...args) { if (!this.isEmpty(func) && typeof func === 'function') { func(...args); } } static orDefault(obj, defaultValue) { if (obj == null) { return defaultValue; } return obj; } static equals(a, b) { for (const key in a) { if (!a.hasOwnProperty(key)) { continue; } if (!b.hasOwnProperty(key)) { return false; } else if (a[key] !== b[key]) { return false; } } return true; } } ================================================ FILE: web/assets/vue@2.6.12/vue.common.dev.js ================================================ /*! * Vue.js v2.6.12 * (c) 2014-2020 Evan You * Released under the MIT License. */ 'use strict'; /* */ var emptyObject = Object.freeze({}); // These helpers produce better VM code in JS engines due to their // explicitness and function inlining. function isUndef (v) { return v === undefined || v === null } function isDef (v) { return v !== undefined && v !== null } function isTrue (v) { return v === true } function isFalse (v) { return v === false } /** * Check if value is primitive. */ function isPrimitive (value) { return ( typeof value === 'string' || typeof value === 'number' || // $flow-disable-line typeof value === 'symbol' || typeof value === 'boolean' ) } /** * Quick object check - this is primarily used to tell * Objects from primitive values when we know the value * is a JSON-compliant type. */ function isObject (obj) { return obj !== null && typeof obj === 'object' } /** * Get the raw type string of a value, e.g., [object Object]. */ var _toString = Object.prototype.toString; function toRawType (value) { return _toString.call(value).slice(8, -1) } /** * Strict object type check. Only returns true * for plain JavaScript objects. */ function isPlainObject (obj) { return _toString.call(obj) === '[object Object]' } function isRegExp (v) { return _toString.call(v) === '[object RegExp]' } /** * Check if val is a valid array index. */ function isValidArrayIndex (val) { var n = parseFloat(String(val)); return n >= 0 && Math.floor(n) === n && isFinite(val) } function isPromise (val) { return ( isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function' ) } /** * Convert a value to a string that is actually rendered. */ function toString (val) { return val == null ? '' : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ? JSON.stringify(val, null, 2) : String(val) } /** * Convert an input value to a number for persistence. * If the conversion fails, return original string. */ function toNumber (val) { var n = parseFloat(val); return isNaN(n) ? val : n } /** * Make a map and return a function for checking if a key * is in that map. */ function makeMap ( str, expectsLowerCase ) { var map = Object.create(null); var list = str.split(','); for (var i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? function (val) { return map[val.toLowerCase()]; } : function (val) { return map[val]; } } /** * Check if a tag is a built-in tag. */ var isBuiltInTag = makeMap('slot,component', true); /** * Check if an attribute is a reserved attribute. */ var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); /** * Remove an item from an array. */ function remove (arr, item) { if (arr.length) { var index = arr.indexOf(item); if (index > -1) { return arr.splice(index, 1) } } } /** * Check whether an object has the property. */ var hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) } /** * Create a cached version of a pure function. */ function cached (fn) { var cache = Object.create(null); return (function cachedFn (str) { var hit = cache[str]; return hit || (cache[str] = fn(str)) }) } /** * Camelize a hyphen-delimited string. */ var camelizeRE = /-(\w)/g; var camelize = cached(function (str) { return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) }); /** * Capitalize a string. */ var capitalize = cached(function (str) { return str.charAt(0).toUpperCase() + str.slice(1) }); /** * Hyphenate a camelCase string. */ var hyphenateRE = /\B([A-Z])/g; var hyphenate = cached(function (str) { return str.replace(hyphenateRE, '-$1').toLowerCase() }); /** * Simple bind polyfill for environments that do not support it, * e.g., PhantomJS 1.x. Technically, we don't need this anymore * since native bind is now performant enough in most browsers. * But removing it would mean breaking code that was able to run in * PhantomJS 1.x, so this must be kept for backward compatibility. */ /* istanbul ignore next */ function polyfillBind (fn, ctx) { function boundFn (a) { var l = arguments.length; return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx) } boundFn._length = fn.length; return boundFn } function nativeBind (fn, ctx) { return fn.bind(ctx) } var bind = Function.prototype.bind ? nativeBind : polyfillBind; /** * Convert an Array-like object to a real Array. */ function toArray (list, start) { start = start || 0; var i = list.length - start; var ret = new Array(i); while (i--) { ret[i] = list[i + start]; } return ret } /** * Mix properties into target object. */ function extend (to, _from) { for (var key in _from) { to[key] = _from[key]; } return to } /** * Merge an Array of Objects into a single Object. */ function toObject (arr) { var res = {}; for (var i = 0; i < arr.length; i++) { if (arr[i]) { extend(res, arr[i]); } } return res } /* eslint-disable no-unused-vars */ /** * Perform no operation. * Stubbing args to make Flow happy without leaving useless transpiled code * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). */ function noop (a, b, c) {} /** * Always return false. */ var no = function (a, b, c) { return false; }; /* eslint-enable no-unused-vars */ /** * Return the same value. */ var identity = function (_) { return _; }; /** * Generate a string containing static keys from compiler modules. */ function genStaticKeys (modules) { return modules.reduce(function (keys, m) { return keys.concat(m.staticKeys || []) }, []).join(',') } /** * Check if two values are loosely equal - that is, * if they are plain objects, do they have the same shape? */ function looseEqual (a, b) { if (a === b) { return true } var isObjectA = isObject(a); var isObjectB = isObject(b); if (isObjectA && isObjectB) { try { var isArrayA = Array.isArray(a); var isArrayB = Array.isArray(b); if (isArrayA && isArrayB) { return a.length === b.length && a.every(function (e, i) { return looseEqual(e, b[i]) }) } else if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime() } else if (!isArrayA && !isArrayB) { var keysA = Object.keys(a); var keysB = Object.keys(b); return keysA.length === keysB.length && keysA.every(function (key) { return looseEqual(a[key], b[key]) }) } else { /* istanbul ignore next */ return false } } catch (e) { /* istanbul ignore next */ return false } } else if (!isObjectA && !isObjectB) { return String(a) === String(b) } else { return false } } /** * Return the first index at which a loosely equal value can be * found in the array (if value is a plain object, the array must * contain an object of the same shape), or -1 if it is not present. */ function looseIndexOf (arr, val) { for (var i = 0; i < arr.length; i++) { if (looseEqual(arr[i], val)) { return i } } return -1 } /** * Ensure a function is called only once. */ function once (fn) { var called = false; return function () { if (!called) { called = true; fn.apply(this, arguments); } } } var SSR_ATTR = 'data-server-rendered'; var ASSET_TYPES = [ 'component', 'directive', 'filter' ]; var LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated', 'errorCaptured', 'serverPrefetch' ]; /* */ var config = ({ /** * Option merge strategies (used in core/util/options) */ // $flow-disable-line optionMergeStrategies: Object.create(null), /** * Whether to suppress warnings. */ silent: false, /** * Show production mode tip message on boot? */ productionTip: "development" !== 'production', /** * Whether to enable devtools */ devtools: "development" !== 'production', /** * Whether to record perf */ performance: false, /** * Error handler for watcher errors */ errorHandler: null, /** * Warn handler for watcher warns */ warnHandler: null, /** * Ignore certain custom elements */ ignoredElements: [], /** * Custom user key aliases for v-on */ // $flow-disable-line keyCodes: Object.create(null), /** * Check if a tag is reserved so that it cannot be registered as a * component. This is platform-dependent and may be overwritten. */ isReservedTag: no, /** * Check if an attribute is reserved so that it cannot be used as a component * prop. This is platform-dependent and may be overwritten. */ isReservedAttr: no, /** * Check if a tag is an unknown element. * Platform-dependent. */ isUnknownElement: no, /** * Get the namespace of an element */ getTagNamespace: noop, /** * Parse the real tag name for the specific platform. */ parsePlatformTagName: identity, /** * Check if an attribute must be bound using property, e.g. value * Platform-dependent. */ mustUseProp: no, /** * Perform updates asynchronously. Intended to be used by Vue Test Utils * This will significantly reduce performance if set to false. */ async: true, /** * Exposed for legacy reasons */ _lifecycleHooks: LIFECYCLE_HOOKS }); /* */ /** * unicode letters used for parsing html tags, component names and property paths. * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname * skipping \u10000-\uEFFFF due to it freezing up PhantomJS */ var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/; /** * Check if a string starts with $ or _ */ function isReserved (str) { var c = (str + '').charCodeAt(0); return c === 0x24 || c === 0x5F } /** * Define a property. */ function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); } /** * Parse simple path. */ var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]")); function parsePath (path) { if (bailRE.test(path)) { return } var segments = path.split('.'); return function (obj) { for (var i = 0; i < segments.length; i++) { if (!obj) { return } obj = obj[segments[i]]; } return obj } } /* */ // can we use __proto__? var hasProto = '__proto__' in {}; // Browser environment sniffing var inBrowser = typeof window !== 'undefined'; var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); var UA = inBrowser && window.navigator.userAgent.toLowerCase(); var isIE = UA && /msie|trident/.test(UA); var isIE9 = UA && UA.indexOf('msie 9.0') > 0; var isEdge = UA && UA.indexOf('edge/') > 0; var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; var isPhantomJS = UA && /phantomjs/.test(UA); var isFF = UA && UA.match(/firefox\/(\d+)/); // Firefox has a "watch" function on Object.prototype... var nativeWatch = ({}).watch; var supportsPassive = false; if (inBrowser) { try { var opts = {}; Object.defineProperty(opts, 'passive', ({ get: function get () { /* istanbul ignore next */ supportsPassive = true; } })); // https://github.com/facebook/flow/issues/285 window.addEventListener('test-passive', null, opts); } catch (e) {} } // this needs to be lazy-evaled because vue may be required before // vue-server-renderer can set VUE_ENV var _isServer; var isServerRendering = function () { if (_isServer === undefined) { /* istanbul ignore if */ if (!inBrowser && !inWeex && typeof global !== 'undefined') { // detect presence of vue-server-renderer and avoid // Webpack shimming the process _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'; } else { _isServer = false; } } return _isServer }; // detect devtools var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; /* istanbul ignore next */ function isNative (Ctor) { return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) } var hasSymbol = typeof Symbol !== 'undefined' && isNative(Symbol) && typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); var _Set; /* istanbul ignore if */ // $flow-disable-line if (typeof Set !== 'undefined' && isNative(Set)) { // use native Set when available. _Set = Set; } else { // a non-standard Set polyfill that only works with primitive keys. _Set = /*@__PURE__*/(function () { function Set () { this.set = Object.create(null); } Set.prototype.has = function has (key) { return this.set[key] === true }; Set.prototype.add = function add (key) { this.set[key] = true; }; Set.prototype.clear = function clear () { this.set = Object.create(null); }; return Set; }()); } /* */ var warn = noop; var tip = noop; var generateComponentTrace = (noop); // work around flow check var formatComponentName = (noop); { var hasConsole = typeof console !== 'undefined'; var classifyRE = /(?:^|[-_])(\w)/g; var classify = function (str) { return str .replace(classifyRE, function (c) { return c.toUpperCase(); }) .replace(/[-_]/g, ''); }; warn = function (msg, vm) { var trace = vm ? generateComponentTrace(vm) : ''; if (config.warnHandler) { config.warnHandler.call(null, msg, vm, trace); } else if (hasConsole && (!config.silent)) { console.error(("[Vue warn]: " + msg + trace)); } }; tip = function (msg, vm) { if (hasConsole && (!config.silent)) { console.warn("[Vue tip]: " + msg + ( vm ? generateComponentTrace(vm) : '' )); } }; formatComponentName = function (vm, includeFile) { if (vm.$root === vm) { return '' } var options = typeof vm === 'function' && vm.cid != null ? vm.options : vm._isVue ? vm.$options || vm.constructor.options : vm; var name = options.name || options._componentTag; var file = options.__file; if (!name && file) { var match = file.match(/([^/\\]+)\.vue$/); name = match && match[1]; } return ( (name ? ("<" + (classify(name)) + ">") : "") + (file && includeFile !== false ? (" at " + file) : '') ) }; var repeat = function (str, n) { var res = ''; while (n) { if (n % 2 === 1) { res += str; } if (n > 1) { str += str; } n >>= 1; } return res }; generateComponentTrace = function (vm) { if (vm._isVue && vm.$parent) { var tree = []; var currentRecursiveSequence = 0; while (vm) { if (tree.length > 0) { var last = tree[tree.length - 1]; if (last.constructor === vm.constructor) { currentRecursiveSequence++; vm = vm.$parent; continue } else if (currentRecursiveSequence > 0) { tree[tree.length - 1] = [last, currentRecursiveSequence]; currentRecursiveSequence = 0; } } tree.push(vm); vm = vm.$parent; } return '\n\nfound in\n\n' + tree .map(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") : formatComponentName(vm))); }) .join('\n') } else { return ("\n\n(found in " + (formatComponentName(vm)) + ")") } }; } /* */ var uid = 0; /** * A dep is an observable that can have multiple * directives subscribing to it. */ var Dep = function Dep () { this.id = uid++; this.subs = []; }; Dep.prototype.addSub = function addSub (sub) { this.subs.push(sub); }; Dep.prototype.removeSub = function removeSub (sub) { remove(this.subs, sub); }; Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); } }; Dep.prototype.notify = function notify () { // stabilize the subscriber list first var subs = this.subs.slice(); if (!config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort(function (a, b) { return a.id - b.id; }); } for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } }; // The current target watcher being evaluated. // This is globally unique because only one watcher // can be evaluated at a time. Dep.target = null; var targetStack = []; function pushTarget (target) { targetStack.push(target); Dep.target = target; } function popTarget () { targetStack.pop(); Dep.target = targetStack[targetStack.length - 1]; } /* */ var VNode = function VNode ( tag, data, children, text, elm, context, componentOptions, asyncFactory ) { this.tag = tag; this.data = data; this.children = children; this.text = text; this.elm = elm; this.ns = undefined; this.context = context; this.fnContext = undefined; this.fnOptions = undefined; this.fnScopeId = undefined; this.key = data && data.key; this.componentOptions = componentOptions; this.componentInstance = undefined; this.parent = undefined; this.raw = false; this.isStatic = false; this.isRootInsert = true; this.isComment = false; this.isCloned = false; this.isOnce = false; this.asyncFactory = asyncFactory; this.asyncMeta = undefined; this.isAsyncPlaceholder = false; }; var prototypeAccessors = { child: { configurable: true } }; // DEPRECATED: alias for componentInstance for backwards compat. /* istanbul ignore next */ prototypeAccessors.child.get = function () { return this.componentInstance }; Object.defineProperties( VNode.prototype, prototypeAccessors ); var createEmptyVNode = function (text) { if ( text === void 0 ) text = ''; var node = new VNode(); node.text = text; node.isComment = true; return node }; function createTextVNode (val) { return new VNode(undefined, undefined, undefined, String(val)) } // optimized shallow clone // used for static nodes and slot nodes because they may be reused across // multiple renders, cloning them avoids errors when DOM manipulations rely // on their elm reference. function cloneVNode (vnode) { var cloned = new VNode( vnode.tag, vnode.data, // #7975 // clone children array to avoid mutating original in case of cloning // a child. vnode.children && vnode.children.slice(), vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory ); cloned.ns = vnode.ns; cloned.isStatic = vnode.isStatic; cloned.key = vnode.key; cloned.isComment = vnode.isComment; cloned.fnContext = vnode.fnContext; cloned.fnOptions = vnode.fnOptions; cloned.fnScopeId = vnode.fnScopeId; cloned.asyncMeta = vnode.asyncMeta; cloned.isCloned = true; return cloned } /* * not type checking this file because flow doesn't play well with * dynamically accessing methods on Array prototype */ var arrayProto = Array.prototype; var arrayMethods = Object.create(arrayProto); var methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]; /** * Intercept mutating methods and emit events */ methodsToPatch.forEach(function (method) { // cache original method var original = arrayProto[method]; def(arrayMethods, method, function mutator () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; var result = original.apply(this, args); var ob = this.__ob__; var inserted; switch (method) { case 'push': case 'unshift': inserted = args; break case 'splice': inserted = args.slice(2); break } if (inserted) { ob.observeArray(inserted); } // notify change ob.dep.notify(); return result }); }); /* */ var arrayKeys = Object.getOwnPropertyNames(arrayMethods); /** * In some cases we may want to disable observation inside a component's * update computation. */ var shouldObserve = true; function toggleObserving (value) { shouldObserve = value; } /** * Observer class that is attached to each observed * object. Once attached, the observer converts the target * object's property keys into getter/setters that * collect dependencies and dispatch updates. */ var Observer = function Observer (value) { this.value = value; this.dep = new Dep(); this.vmCount = 0; def(value, '__ob__', this); if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods); } else { copyAugment(value, arrayMethods, arrayKeys); } this.observeArray(value); } else { this.walk(value); } }; /** * Walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. */ Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); } }; /** * Observe a list of Array items. */ Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); } }; // helpers /** * Augment a target Object or Array by intercepting * the prototype chain using __proto__ */ function protoAugment (target, src) { /* eslint-disable no-proto */ target.__proto__ = src; /* eslint-enable no-proto */ } /** * Augment a target Object or Array by defining * hidden properties. */ /* istanbul ignore next */ function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; def(target, key, src[key]); } } /** * Attempt to create an observer instance for a value, * returns the new observer if successfully observed, * or the existing observer if the value already has one. */ function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) { return } var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob } /** * Define a reactive property on an Object. */ function defineReactive$$1 ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (customSetter) { customSetter(); } // #7981: for accessor properties without setter if (getter && !setter) { return } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); } /** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */ function set (target, key, val) { if (isUndef(target) || isPrimitive(target) ) { warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target)))); } if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val } if (key in target && !(key in Object.prototype)) { target[key] = val; return val } var ob = (target).__ob__; if (target._isVue || (ob && ob.vmCount)) { warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ); return val } if (!ob) { target[key] = val; return val } defineReactive$$1(ob.value, key, val); ob.dep.notify(); return val } /** * Delete a property and trigger change if necessary. */ function del (target, key) { if (isUndef(target) || isPrimitive(target) ) { warn(("Cannot delete reactive property on undefined, null, or primitive value: " + ((target)))); } if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1); return } var ob = (target).__ob__; if (target._isVue || (ob && ob.vmCount)) { warn( 'Avoid deleting properties on a Vue instance or its root $data ' + '- just set it to null.' ); return } if (!hasOwn(target, key)) { return } delete target[key]; if (!ob) { return } ob.dep.notify(); } /** * Collect dependencies on array elements when the array is touched, since * we cannot intercept array element access like property getters. */ function dependArray (value) { for (var e = (void 0), i = 0, l = value.length; i < l; i++) { e = value[i]; e && e.__ob__ && e.__ob__.dep.depend(); if (Array.isArray(e)) { dependArray(e); } } } /* */ /** * Option overwriting strategies are functions that handle * how to merge a parent option value and a child option * value into the final value. */ var strats = config.optionMergeStrategies; /** * Options with restrictions */ { strats.el = strats.propsData = function (parent, child, vm, key) { if (!vm) { warn( "option \"" + key + "\" can only be used during instance " + 'creation with the `new` keyword.' ); } return defaultStrat(parent, child) }; } /** * Helper that recursively merges two data objects together. */ function mergeData (to, from) { if (!from) { return to } var key, toVal, fromVal; var keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from); for (var i = 0; i < keys.length; i++) { key = keys[i]; // in case the object is already observed... if (key === '__ob__') { continue } toVal = to[key]; fromVal = from[key]; if (!hasOwn(to, key)) { set(to, key, fromVal); } else if ( toVal !== fromVal && isPlainObject(toVal) && isPlainObject(fromVal) ) { mergeData(toVal, fromVal); } } return to } /** * Data */ function mergeDataOrFn ( parentVal, childVal, vm ) { if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } } else { return function mergedInstanceDataFn () { // instance merge var instanceData = typeof childVal === 'function' ? childVal.call(vm, vm) : childVal; var defaultData = typeof parentVal === 'function' ? parentVal.call(vm, vm) : parentVal; if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData } } } } strats.data = function ( parentVal, childVal, vm ) { if (!vm) { if (childVal && typeof childVal !== 'function') { warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm ); return parentVal } return mergeDataOrFn(parentVal, childVal) } return mergeDataOrFn(parentVal, childVal, vm) }; /** * Hooks and props are merged as arrays. */ function mergeHook ( parentVal, childVal ) { var res = childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal; return res ? dedupeHooks(res) : res } function dedupeHooks (hooks) { var res = []; for (var i = 0; i < hooks.length; i++) { if (res.indexOf(hooks[i]) === -1) { res.push(hooks[i]); } } return res } LIFECYCLE_HOOKS.forEach(function (hook) { strats[hook] = mergeHook; }); /** * Assets * * When a vm is present (instance creation), we need to do * a three-way merge between constructor options, instance * options and parent options. */ function mergeAssets ( parentVal, childVal, vm, key ) { var res = Object.create(parentVal || null); if (childVal) { assertObjectType(key, childVal, vm); return extend(res, childVal) } else { return res } } ASSET_TYPES.forEach(function (type) { strats[type + 's'] = mergeAssets; }); /** * Watchers. * * Watchers hashes should not overwrite one * another, so we merge them as arrays. */ strats.watch = function ( parentVal, childVal, vm, key ) { // work around Firefox's Object.prototype.watch... if (parentVal === nativeWatch) { parentVal = undefined; } if (childVal === nativeWatch) { childVal = undefined; } /* istanbul ignore if */ if (!childVal) { return Object.create(parentVal || null) } { assertObjectType(key, childVal, vm); } if (!parentVal) { return childVal } var ret = {}; extend(ret, parentVal); for (var key$1 in childVal) { var parent = ret[key$1]; var child = childVal[key$1]; if (parent && !Array.isArray(parent)) { parent = [parent]; } ret[key$1] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child]; } return ret }; /** * Other object hashes. */ strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal, childVal, vm, key ) { if (childVal && "development" !== 'production') { assertObjectType(key, childVal, vm); } if (!parentVal) { return childVal } var ret = Object.create(null); extend(ret, parentVal); if (childVal) { extend(ret, childVal); } return ret }; strats.provide = mergeDataOrFn; /** * Default strategy. */ var defaultStrat = function (parentVal, childVal) { return childVal === undefined ? parentVal : childVal }; /** * Validate component names */ function checkComponents (options) { for (var key in options.components) { validateComponentName(key); } } function validateComponentName (name) { if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) { warn( 'Invalid component name: "' + name + '". Component names ' + 'should conform to valid custom element name in html5 specification.' ); } if (isBuiltInTag(name) || config.isReservedTag(name)) { warn( 'Do not use built-in or reserved HTML elements as component ' + 'id: ' + name ); } } /** * Ensure all props option syntax are normalized into the * Object-based format. */ function normalizeProps (options, vm) { var props = options.props; if (!props) { return } var res = {}; var i, val, name; if (Array.isArray(props)) { i = props.length; while (i--) { val = props[i]; if (typeof val === 'string') { name = camelize(val); res[name] = { type: null }; } else { warn('props must be strings when using array syntax.'); } } } else if (isPlainObject(props)) { for (var key in props) { val = props[key]; name = camelize(key); res[name] = isPlainObject(val) ? val : { type: val }; } } else { warn( "Invalid value for option \"props\": expected an Array or an Object, " + "but got " + (toRawType(props)) + ".", vm ); } options.props = res; } /** * Normalize all injections into Object-based format */ function normalizeInject (options, vm) { var inject = options.inject; if (!inject) { return } var normalized = options.inject = {}; if (Array.isArray(inject)) { for (var i = 0; i < inject.length; i++) { normalized[inject[i]] = { from: inject[i] }; } } else if (isPlainObject(inject)) { for (var key in inject) { var val = inject[key]; normalized[key] = isPlainObject(val) ? extend({ from: key }, val) : { from: val }; } } else { warn( "Invalid value for option \"inject\": expected an Array or an Object, " + "but got " + (toRawType(inject)) + ".", vm ); } } /** * Normalize raw function directives into object format. */ function normalizeDirectives (options) { var dirs = options.directives; if (dirs) { for (var key in dirs) { var def$$1 = dirs[key]; if (typeof def$$1 === 'function') { dirs[key] = { bind: def$$1, update: def$$1 }; } } } } function assertObjectType (name, value, vm) { if (!isPlainObject(value)) { warn( "Invalid value for option \"" + name + "\": expected an Object, " + "but got " + (toRawType(value)) + ".", vm ); } } /** * Merge two option objects into a new one. * Core utility used in both instantiation and inheritance. */ function mergeOptions ( parent, child, vm ) { { checkComponents(child); } if (typeof child === 'function') { child = child.options; } normalizeProps(child, vm); normalizeInject(child, vm); normalizeDirectives(child); // Apply extends and mixins on the child options, // but only if it is a raw options object that isn't // the result of another mergeOptions call. // Only merged options has the _base property. if (!child._base) { if (child.extends) { parent = mergeOptions(parent, child.extends, vm); } if (child.mixins) { for (var i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm); } } } var options = {}; var key; for (key in parent) { mergeField(key); } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key); } } function mergeField (key) { var strat = strats[key] || defaultStrat; options[key] = strat(parent[key], child[key], vm, key); } return options } /** * Resolve an asset. * This function is used because child instances need access * to assets defined in its ancestor chain. */ function resolveAsset ( options, type, id, warnMissing ) { /* istanbul ignore if */ if (typeof id !== 'string') { return } var assets = options[type]; // check local registration variations first if (hasOwn(assets, id)) { return assets[id] } var camelizedId = camelize(id); if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } var PascalCaseId = capitalize(camelizedId); if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } // fallback to prototype chain var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; if (warnMissing && !res) { warn( 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, options ); } return res } /* */ function validateProp ( key, propOptions, propsData, vm ) { var prop = propOptions[key]; var absent = !hasOwn(propsData, key); var value = propsData[key]; // boolean casting var booleanIndex = getTypeIndex(Boolean, prop.type); if (booleanIndex > -1) { if (absent && !hasOwn(prop, 'default')) { value = false; } else if (value === '' || value === hyphenate(key)) { // only cast empty string / same name to boolean if // boolean has higher priority var stringIndex = getTypeIndex(String, prop.type); if (stringIndex < 0 || booleanIndex < stringIndex) { value = true; } } } // check default value if (value === undefined) { value = getPropDefaultValue(vm, prop, key); // since the default value is a fresh copy, // make sure to observe it. var prevShouldObserve = shouldObserve; toggleObserving(true); observe(value); toggleObserving(prevShouldObserve); } { assertProp(prop, key, value, vm, absent); } return value } /** * Get the default value of a prop. */ function getPropDefaultValue (vm, prop, key) { // no default, return undefined if (!hasOwn(prop, 'default')) { return undefined } var def = prop.default; // warn against non-factory defaults for Object & Array if (isObject(def)) { warn( 'Invalid default value for prop "' + key + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm ); } // the raw prop value was also undefined from previous render, // return previous default value to avoid unnecessary watcher trigger if (vm && vm.$options.propsData && vm.$options.propsData[key] === undefined && vm._props[key] !== undefined ) { return vm._props[key] } // call factory function for non-Function types // a value is Function if its prototype is function even across different execution context return typeof def === 'function' && getType(prop.type) !== 'Function' ? def.call(vm) : def } /** * Assert whether a prop is valid. */ function assertProp ( prop, name, value, vm, absent ) { if (prop.required && absent) { warn( 'Missing required prop: "' + name + '"', vm ); return } if (value == null && !prop.required) { return } var type = prop.type; var valid = !type || type === true; var expectedTypes = []; if (type) { if (!Array.isArray(type)) { type = [type]; } for (var i = 0; i < type.length && !valid; i++) { var assertedType = assertType(value, type[i]); expectedTypes.push(assertedType.expectedType || ''); valid = assertedType.valid; } } if (!valid) { warn( getInvalidTypeMessage(name, value, expectedTypes), vm ); return } var validator = prop.validator; if (validator) { if (!validator(value)) { warn( 'Invalid prop: custom validator check failed for prop "' + name + '".', vm ); } } } var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; function assertType (value, type) { var valid; var expectedType = getType(type); if (simpleCheckRE.test(expectedType)) { var t = typeof value; valid = t === expectedType.toLowerCase(); // for primitive wrapper objects if (!valid && t === 'object') { valid = value instanceof type; } } else if (expectedType === 'Object') { valid = isPlainObject(value); } else if (expectedType === 'Array') { valid = Array.isArray(value); } else { valid = value instanceof type; } return { valid: valid, expectedType: expectedType } } /** * Use function string name to check built-in types, * because a simple equality check will fail when running * across different vms / iframes. */ function getType (fn) { var match = fn && fn.toString().match(/^\s*function (\w+)/); return match ? match[1] : '' } function isSameType (a, b) { return getType(a) === getType(b) } function getTypeIndex (type, expectedTypes) { if (!Array.isArray(expectedTypes)) { return isSameType(expectedTypes, type) ? 0 : -1 } for (var i = 0, len = expectedTypes.length; i < len; i++) { if (isSameType(expectedTypes[i], type)) { return i } } return -1 } function getInvalidTypeMessage (name, value, expectedTypes) { var message = "Invalid prop: type check failed for prop \"" + name + "\"." + " Expected " + (expectedTypes.map(capitalize).join(', ')); var expectedType = expectedTypes[0]; var receivedType = toRawType(value); var expectedValue = styleValue(value, expectedType); var receivedValue = styleValue(value, receivedType); // check if we need to specify expected value if (expectedTypes.length === 1 && isExplicable(expectedType) && !isBoolean(expectedType, receivedType)) { message += " with value " + expectedValue; } message += ", got " + receivedType + " "; // check if we need to specify received value if (isExplicable(receivedType)) { message += "with value " + receivedValue + "."; } return message } function styleValue (value, type) { if (type === 'String') { return ("\"" + value + "\"") } else if (type === 'Number') { return ("" + (Number(value))) } else { return ("" + value) } } function isExplicable (value) { var explicitTypes = ['string', 'number', 'boolean']; return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; }) } function isBoolean () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; }) } /* */ function handleError (err, vm, info) { // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. // See: https://github.com/vuejs/vuex/issues/1505 pushTarget(); try { if (vm) { var cur = vm; while ((cur = cur.$parent)) { var hooks = cur.$options.errorCaptured; if (hooks) { for (var i = 0; i < hooks.length; i++) { try { var capture = hooks[i].call(cur, err, vm, info) === false; if (capture) { return } } catch (e) { globalHandleError(e, cur, 'errorCaptured hook'); } } } } } globalHandleError(err, vm, info); } finally { popTarget(); } } function invokeWithErrorHandling ( handler, context, args, vm, info ) { var res; try { res = args ? handler.apply(context, args) : handler.call(context); if (res && !res._isVue && isPromise(res) && !res._handled) { res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); }); // issue #9511 // avoid catch triggering multiple times when nested calls res._handled = true; } } catch (e) { handleError(e, vm, info); } return res } function globalHandleError (err, vm, info) { if (config.errorHandler) { try { return config.errorHandler.call(null, err, vm, info) } catch (e) { // if the user intentionally throws the original error in the handler, // do not log it twice if (e !== err) { logError(e, null, 'config.errorHandler'); } } } logError(err, vm, info); } function logError (err, vm, info) { { warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); } /* istanbul ignore else */ if ((inBrowser || inWeex) && typeof console !== 'undefined') { console.error(err); } else { throw err } } /* */ var isUsingMicroTask = false; var callbacks = []; var pending = false; function flushCallbacks () { pending = false; var copies = callbacks.slice(0); callbacks.length = 0; for (var i = 0; i < copies.length; i++) { copies[i](); } } // Here we have async deferring wrappers using microtasks. // In 2.5 we used (macro) tasks (in combination with microtasks). // However, it has subtle problems when state is changed right before repaint // (e.g. #6813, out-in transitions). // Also, using (macro) tasks in event handler would cause some weird behaviors // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). // So we now use microtasks everywhere, again. // A major drawback of this tradeoff is that there are some scenarios // where microtasks have too high a priority and fire in between supposedly // sequential events (e.g. #4521, #6690, which have workarounds) // or even between bubbling of the same event (#6566). var timerFunc; // The nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore next, $flow-disable-line */ if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve(); timerFunc = function () { p.then(flushCallbacks); // In problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) { setTimeout(noop); } }; isUsingMicroTask = true; } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // Use MutationObserver where native Promise is not available, // e.g. PhantomJS, iOS7, Android 4.4 // (#6466 MutationObserver is unreliable in IE11) var counter = 1; var observer = new MutationObserver(flushCallbacks); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = String(counter); }; isUsingMicroTask = true; } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // Fallback to setImmediate. // Technically it leverages the (macro) task queue, // but it is still a better choice than setTimeout. timerFunc = function () { setImmediate(flushCallbacks); }; } else { // Fallback to setTimeout. timerFunc = function () { setTimeout(flushCallbacks, 0); }; } function nextTick (cb, ctx) { var _resolve; callbacks.push(function () { if (cb) { try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); if (!pending) { pending = true; timerFunc(); } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }) } } /* */ var mark; var measure; { var perf = inBrowser && window.performance; /* istanbul ignore if */ if ( perf && perf.mark && perf.measure && perf.clearMarks && perf.clearMeasures ) { mark = function (tag) { return perf.mark(tag); }; measure = function (name, startTag, endTag) { perf.measure(name, startTag, endTag); perf.clearMarks(startTag); perf.clearMarks(endTag); // perf.clearMeasures(name) }; } } /* not type checking this file because flow doesn't play well with Proxy */ var initProxy; { var allowedGlobals = makeMap( 'Infinity,undefined,NaN,isFinite,isNaN,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + 'require' // for Webpack/Browserify ); var warnNonPresent = function (target, key) { warn( "Property or method \"" + key + "\" is not defined on the instance but " + 'referenced during render. Make sure that this property is reactive, ' + 'either in the data option, or for class-based components, by ' + 'initializing the property. ' + 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', target ); }; var warnReservedPrefix = function (target, key) { warn( "Property \"" + key + "\" must be accessed with \"$data." + key + "\" because " + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + 'prevent conflicts with Vue internals. ' + 'See: https://vuejs.org/v2/api/#data', target ); }; var hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy); if (hasProxy) { var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); config.keyCodes = new Proxy(config.keyCodes, { set: function set (target, key, value) { if (isBuiltInModifier(key)) { warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); return false } else { target[key] = value; return true } } }); } var hasHandler = { has: function has (target, key) { var has = key in target; var isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)); if (!has && !isAllowed) { if (key in target.$data) { warnReservedPrefix(target, key); } else { warnNonPresent(target, key); } } return has || !isAllowed } }; var getHandler = { get: function get (target, key) { if (typeof key === 'string' && !(key in target)) { if (key in target.$data) { warnReservedPrefix(target, key); } else { warnNonPresent(target, key); } } return target[key] } }; initProxy = function initProxy (vm) { if (hasProxy) { // determine which proxy handler to use var options = vm.$options; var handlers = options.render && options.render._withStripped ? getHandler : hasHandler; vm._renderProxy = new Proxy(vm, handlers); } else { vm._renderProxy = vm; } }; } /* */ var seenObjects = new _Set(); /** * Recursively traverse an object to evoke all converted * getters, so that every nested property inside the object * is collected as a "deep" dependency. */ function traverse (val) { _traverse(val, seenObjects); seenObjects.clear(); } function _traverse (val, seen) { var i, keys; var isA = Array.isArray(val); if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { return } if (val.__ob__) { var depId = val.__ob__.dep.id; if (seen.has(depId)) { return } seen.add(depId); } if (isA) { i = val.length; while (i--) { _traverse(val[i], seen); } } else { keys = Object.keys(val); i = keys.length; while (i--) { _traverse(val[keys[i]], seen); } } } /* */ var normalizeEvent = cached(function (name) { var passive = name.charAt(0) === '&'; name = passive ? name.slice(1) : name; var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first name = once$$1 ? name.slice(1) : name; var capture = name.charAt(0) === '!'; name = capture ? name.slice(1) : name; return { name: name, once: once$$1, capture: capture, passive: passive } }); function createFnInvoker (fns, vm) { function invoker () { var arguments$1 = arguments; var fns = invoker.fns; if (Array.isArray(fns)) { var cloned = fns.slice(); for (var i = 0; i < cloned.length; i++) { invokeWithErrorHandling(cloned[i], null, arguments$1, vm, "v-on handler"); } } else { // return handler return value for single handlers return invokeWithErrorHandling(fns, null, arguments, vm, "v-on handler") } } invoker.fns = fns; return invoker } function updateListeners ( on, oldOn, add, remove$$1, createOnceHandler, vm ) { var name, def$$1, cur, old, event; for (name in on) { def$$1 = cur = on[name]; old = oldOn[name]; event = normalizeEvent(name); if (isUndef(cur)) { warn( "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), vm ); } else if (isUndef(old)) { if (isUndef(cur.fns)) { cur = on[name] = createFnInvoker(cur, vm); } if (isTrue(event.once)) { cur = on[name] = createOnceHandler(event.name, cur, event.capture); } add(event.name, cur, event.capture, event.passive, event.params); } else if (cur !== old) { old.fns = cur; on[name] = old; } } for (name in oldOn) { if (isUndef(on[name])) { event = normalizeEvent(name); remove$$1(event.name, oldOn[name], event.capture); } } } /* */ function mergeVNodeHook (def, hookKey, hook) { if (def instanceof VNode) { def = def.data.hook || (def.data.hook = {}); } var invoker; var oldHook = def[hookKey]; function wrappedHook () { hook.apply(this, arguments); // important: remove merged hook to ensure it's called only once // and prevent memory leak remove(invoker.fns, wrappedHook); } if (isUndef(oldHook)) { // no existing hook invoker = createFnInvoker([wrappedHook]); } else { /* istanbul ignore if */ if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { // already a merged invoker invoker = oldHook; invoker.fns.push(wrappedHook); } else { // existing plain hook invoker = createFnInvoker([oldHook, wrappedHook]); } } invoker.merged = true; def[hookKey] = invoker; } /* */ function extractPropsFromVNodeData ( data, Ctor, tag ) { // we are only extracting raw values here. // validation and default values are handled in the child // component itself. var propOptions = Ctor.options.props; if (isUndef(propOptions)) { return } var res = {}; var attrs = data.attrs; var props = data.props; if (isDef(attrs) || isDef(props)) { for (var key in propOptions) { var altKey = hyphenate(key); { var keyInLowerCase = key.toLowerCase(); if ( key !== keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase) ) { tip( "Prop \"" + keyInLowerCase + "\" is passed to component " + (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + " \"" + key + "\". " + "Note that HTML attributes are case-insensitive and camelCased " + "props need to use their kebab-case equivalents when using in-DOM " + "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." ); } } checkProp(res, props, key, altKey, true) || checkProp(res, attrs, key, altKey, false); } } return res } function checkProp ( res, hash, key, altKey, preserve ) { if (isDef(hash)) { if (hasOwn(hash, key)) { res[key] = hash[key]; if (!preserve) { delete hash[key]; } return true } else if (hasOwn(hash, altKey)) { res[key] = hash[altKey]; if (!preserve) { delete hash[altKey]; } return true } } return false } /* */ // The template compiler attempts to minimize the need for normalization by // statically analyzing the template at compile time. // // For plain HTML markup, normalization can be completely skipped because the // generated render function is guaranteed to return Array. There are // two cases where extra normalization is needed: // 1. When the children contains components - because a functional component // may return an Array instead of a single root. In this case, just a simple // normalization is needed - if any child is an Array, we flatten the whole // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep // because functional components already normalize their own children. function simpleNormalizeChildren (children) { for (var i = 0; i < children.length; i++) { if (Array.isArray(children[i])) { return Array.prototype.concat.apply([], children) } } return children } // 2. When the children contains constructs that always generated nested Arrays, // e.g.