Compare commits
176 Commits
releases/v
...
v2.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dde7a4dff1 | ||
|
|
40601bd05b | ||
|
|
72d5ed908e | ||
|
|
72673a9d52 | ||
|
|
327ccdcf38 | ||
|
|
8c2f96d1aa | ||
|
|
34ba0bc95b | ||
|
|
ed162c2e66 | ||
|
|
40b5fe9a54 | ||
|
|
5a98fac395 | ||
|
|
0bab14cd72 | ||
|
|
b407cfd9d4 | ||
|
|
25dcdc652a | ||
|
|
950cb04534 | ||
|
|
c07d1286ef | ||
|
|
8ddd153022 | ||
|
|
870353c499 | ||
|
|
ecebbecd3b | ||
|
|
f39fbb2ce2 | ||
|
|
ec56c0bc45 | ||
|
|
20a6025075 | ||
|
|
707963c0d9 | ||
|
|
3c7837692e | ||
|
|
f890812577 | ||
|
|
47f3efe71b | ||
|
|
6d88b10b14 | ||
|
|
d34a51739f | ||
|
|
a6773aa549 | ||
|
|
0314c66635 | ||
|
|
3fb172b4d2 | ||
|
|
96fc19b803 | ||
|
|
9f7ba8ab8f | ||
|
|
e592e9f29a | ||
|
|
4608bca998 | ||
|
|
b5dfc7374c | ||
|
|
b469f8197a | ||
|
|
0a38a8ef4a | ||
|
|
e75be7801f | ||
|
|
6c49bb1865 | ||
|
|
f9c24bc205 | ||
|
|
d7c3179c6e | ||
|
|
b0fd37949a | ||
|
|
29994b663a | ||
|
|
fc397c35c5 | ||
|
|
0f2b214918 | ||
|
|
fec885c427 | ||
|
|
5a2fd4465c | ||
|
|
83d1ecc4da | ||
|
|
7c6daf7c56 | ||
|
|
28fe6257be | ||
|
|
99430983bc | ||
|
|
d758a4958f | ||
|
|
95b12dda5a | ||
|
|
2675cf2d00 | ||
|
|
72be46e8fa | ||
|
|
c5580feb64 | ||
|
|
7e3819be86 | ||
|
|
f0302f2be7 | ||
|
|
b5f60f843d | ||
|
|
6bdfb8b01f | ||
|
|
ef1d81a2a1 | ||
|
|
739b4ee106 | ||
|
|
6a038e8a88 | ||
|
|
72ea8a9f76 | ||
|
|
44d93648ee | ||
|
|
75f7865769 | ||
|
|
01e3ad99ca | ||
|
|
3c0d85c9db | ||
|
|
b38991a14e | ||
|
|
465269566b | ||
|
|
f103fc13d9 | ||
|
|
e5917fad4e | ||
|
|
de8c89eb03 | ||
|
|
c142db301a | ||
|
|
8dc8c7d9e2 | ||
|
|
2b909e04ea | ||
|
|
e130c3f2e4 | ||
|
|
3ad754879f | ||
|
|
fd2b3768e1 | ||
|
|
67cff12c76 | ||
|
|
c5ea7848b3 | ||
|
|
34365a096e | ||
|
|
d880dfbbca | ||
|
|
b46a200f8d | ||
|
|
81490d0662 | ||
|
|
3d1e841cc5 | ||
|
|
f52936a103 | ||
|
|
23f69ce6a4 | ||
|
|
f84ae228fc | ||
|
|
74c716ccaa | ||
|
|
445b02b2ca | ||
|
|
bb17ffa9fc | ||
|
|
389ea709ce | ||
|
|
c2f535ead4 | ||
|
|
0318f55322 | ||
|
|
1f4340e82f | ||
|
|
ed08707c98 | ||
|
|
7397abcb94 | ||
|
|
98d321f8ac | ||
|
|
e78b0ef869 | ||
|
|
8d654330ac | ||
|
|
00d61333d3 | ||
|
|
03b55b61e7 | ||
|
|
745e44cc87 | ||
|
|
24213a874a | ||
|
|
155f8a2ba2 | ||
|
|
568dca6f9c | ||
|
|
673c34cf5a | ||
|
|
2050ed78d0 | ||
|
|
2632c44195 | ||
|
|
5449eabf2a | ||
|
|
dd5b00faf4 | ||
|
|
0caec3e4da | ||
|
|
e48e62cac0 | ||
|
|
06ebda2e2f | ||
|
|
53c449b9fb | ||
|
|
51e0fac72c | ||
|
|
32b1fe0893 | ||
|
|
2af3b82e32 | ||
|
|
eca1231831 | ||
|
|
e833c2a28b | ||
|
|
8b89a037e8 | ||
|
|
1e821a03fe | ||
|
|
66051967fe | ||
|
|
a63778854f | ||
|
|
4aea0821dd | ||
|
|
08546925cc | ||
|
|
d0f26d9303 | ||
|
|
2a5d5ea4df | ||
|
|
b69b122c8d | ||
|
|
55a39491cb | ||
|
|
1194ee1c2d | ||
|
|
c23b544c34 | ||
|
|
9d76b86f49 | ||
|
|
bb0ccca3e5 | ||
|
|
306817ae9a | ||
|
|
d2ec60e108 | ||
|
|
e016aeddeb | ||
|
|
a4419a31fd | ||
|
|
34e4e907a9 | ||
|
|
2f4a097787 | ||
|
|
f3de00be37 | ||
|
|
4cf61f0d4a | ||
|
|
4e5915f98e | ||
|
|
870eca9e9f | ||
|
|
25ed41caf5 | ||
|
|
4bb72b5606 | ||
|
|
c4d8ea4fec | ||
|
|
8588c9201a | ||
|
|
dd2236c697 | ||
|
|
bc7c4d8cd0 | ||
|
|
aed54f7318 | ||
|
|
86600c6315 | ||
|
|
3f47f37470 | ||
|
|
1324e6163e | ||
|
|
89093167c6 | ||
|
|
15ad92aef2 | ||
|
|
6cdea38284 | ||
|
|
9d455e22fa | ||
|
|
4fc3ff8ce8 | ||
|
|
88e6de9d7e | ||
|
|
e948dbfcc1 | ||
|
|
8aca5851f2 | ||
|
|
18da94bf33 | ||
|
|
1ac2e1c8e3 | ||
|
|
a78b759741 | ||
|
|
b5c3726e67 | ||
|
|
efee3707da | ||
|
|
bbd3453f36 | ||
|
|
0bf42c53cc | ||
|
|
2134bc9139 | ||
|
|
4df8d7e976 | ||
|
|
70708b34cc | ||
|
|
949003ee1b | ||
|
|
db9df1df94 | ||
|
|
4dca25db86 |
@@ -6,72 +6,84 @@ rustflags = ["-C", "linker-flavor=ld.lld"]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
linker = "aarch64-unknown-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
[target.'cfg(all(windows, target_env = "msvc"))']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
[target.mipsel-unknown-linux-musl]
|
||||
linker = "mipsel-linux-muslsf-gcc"
|
||||
linker = "mipsel-unknown-linux-muslsf-gcc"
|
||||
rustflags = [
|
||||
"-C",
|
||||
"target-feature=+crt-static",
|
||||
"-L",
|
||||
"./musl_gcc/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib",
|
||||
"./musl_gcc/mipsel-unknown-linux-muslsf/mipsel-unknown-linux-muslsf/lib",
|
||||
"-L",
|
||||
"./musl_gcc/mipsel-linux-muslsf-cross/lib/gcc/mipsel-linux-muslsf/11.2.1",
|
||||
"./musl_gcc/mipsel-unknown-linux-muslsf/mipsel-unknown-linux-muslsf/sysroot/usr/lib",
|
||||
"-L",
|
||||
"./musl_gcc/mipsel-unknown-linux-muslsf/lib/gcc/mipsel-unknown-linux-muslsf/15.1.0",
|
||||
"-l",
|
||||
"atomic",
|
||||
"-l",
|
||||
"ctz",
|
||||
"-l",
|
||||
"gcc",
|
||||
]
|
||||
|
||||
[target.mips-unknown-linux-musl]
|
||||
linker = "mips-linux-muslsf-gcc"
|
||||
linker = "mips-unknown-linux-muslsf-gcc"
|
||||
rustflags = [
|
||||
"-C",
|
||||
"target-feature=+crt-static",
|
||||
"-L",
|
||||
"./musl_gcc/mips-linux-muslsf-cross/mips-linux-muslsf/lib",
|
||||
"./musl_gcc/mips-unknown-linux-muslsf/mips-unknown-linux-muslsf/lib",
|
||||
"-L",
|
||||
"./musl_gcc/mips-linux-muslsf-cross/lib/gcc/mips-linux-muslsf/11.2.1",
|
||||
"./musl_gcc/mips-unknown-linux-muslsf/mips-unknown-linux-muslsf/sysroot/usr/lib",
|
||||
"-L",
|
||||
"./musl_gcc/mips-unknown-linux-muslsf/lib/gcc/mips-unknown-linux-muslsf/15.1.0",
|
||||
"-l",
|
||||
"atomic",
|
||||
"-l",
|
||||
"ctz",
|
||||
"-l",
|
||||
"gcc",
|
||||
]
|
||||
|
||||
[target.armv7-unknown-linux-musleabihf]
|
||||
linker = "armv7l-linux-musleabihf-gcc"
|
||||
linker = "armv7-unknown-linux-musleabihf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
[target.armv7-unknown-linux-musleabi]
|
||||
linker = "armv7m-linux-musleabi-gcc"
|
||||
linker = "armv7-unknown-linux-musleabi-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
[target.arm-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-musleabihf-gcc"
|
||||
linker = "arm-unknown-linux-musleabihf-gcc"
|
||||
rustflags = [
|
||||
"-C",
|
||||
"target-feature=+crt-static",
|
||||
"-L",
|
||||
"./musl_gcc/arm-linux-musleabihf-cross/arm-linux-musleabihf/lib",
|
||||
"./musl_gcc/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/lib",
|
||||
"-L",
|
||||
"./musl_gcc/arm-linux-musleabihf-cross/lib/gcc/arm-linux-musleabihf/11.2.1",
|
||||
"./musl_gcc/arm-unknown-linux-musleabihf/lib/gcc/arm-unknown-linux-musleabihf/15.1.0",
|
||||
"-l",
|
||||
"atomic",
|
||||
"-l",
|
||||
"gcc",
|
||||
]
|
||||
|
||||
[target.arm-unknown-linux-musleabi]
|
||||
linker = "arm-linux-musleabi-gcc"
|
||||
linker = "arm-unknown-linux-musleabi-gcc"
|
||||
rustflags = [
|
||||
"-C",
|
||||
"target-feature=+crt-static",
|
||||
"-L",
|
||||
"./musl_gcc/arm-linux-musleabi-cross/arm-linux-musleabi/lib",
|
||||
"./musl_gcc/arm-unknown-linux-musleabi/arm-unknown-linux-musleabi/lib",
|
||||
"-L",
|
||||
"./musl_gcc/arm-linux-musleabi-cross/lib/gcc/arm-linux-musleabi/11.2.1",
|
||||
"./musl_gcc/arm-unknown-linux-musleabi/lib/gcc/arm-unknown-linux-musleabi/15.1.0",
|
||||
"-l",
|
||||
"atomic",
|
||||
"-l",
|
||||
"gcc",
|
||||
]
|
||||
|
||||
4
.github/workflows/Dockerfile
vendored
@@ -18,7 +18,7 @@ RUN mkdir -p /tmp/output; \
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
RUN apk add --no-cache tzdata tini
|
||||
WORKDIR /app
|
||||
COPY --from=builder --chmod=755 /tmp/output/* /usr/local/bin
|
||||
|
||||
@@ -36,4 +36,4 @@ EXPOSE 11011/tcp
|
||||
# wss
|
||||
EXPOSE 11012/tcp
|
||||
|
||||
ENTRYPOINT ["easytier-core"]
|
||||
ENTRYPOINT ["/sbin/tini", "--", "easytier-core"]
|
||||
|
||||
198
.github/workflows/core.yml
vendored
@@ -31,34 +31,75 @@ jobs:
|
||||
skip_after_successful_duplicate: 'true'
|
||||
cancel_others: 'true'
|
||||
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml", ".github/workflows/install_rust.sh"]'
|
||||
build_web:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
pnpm -r install
|
||||
pnpm -r --filter "./easytier-web/*" build
|
||||
|
||||
- name: Archive artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: easytier-web-dashboard
|
||||
path: |
|
||||
easytier-web/frontend/dist/*
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- TARGET: aarch64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-aarch64
|
||||
- TARGET: x86_64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-x86_64
|
||||
- TARGET: mips-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-mips
|
||||
- TARGET: mipsel-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-mipsel
|
||||
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-armv7hf
|
||||
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-armv7
|
||||
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-armhf
|
||||
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: linux-arm
|
||||
|
||||
- TARGET: x86_64-apple-darwin
|
||||
@@ -71,9 +112,15 @@ jobs:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
ARTIFACT_NAME: windows-x86_64
|
||||
- TARGET: aarch64-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
ARTIFACT_NAME: windows-arm64
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
ARTIFACT_NAME: windows-i686
|
||||
|
||||
- TARGET: x86_64-unknown-freebsd
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: freebsd-13.2-x86_64
|
||||
BSD_VERSION: 13.2
|
||||
|
||||
@@ -83,7 +130,9 @@ jobs:
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
OS: ${{ matrix.OS }}
|
||||
OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||
needs: pre_job
|
||||
needs:
|
||||
- pre_job
|
||||
- build_web
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -92,7 +141,14 @@ jobs:
|
||||
run: |
|
||||
echo "GIT_DESC=$(git log -1 --format=%cd.%h --date=format:%Y-%m-%d_%H:%M:%S)" >> $GITHUB_ENV
|
||||
|
||||
- name: Download web artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: easytier-web-dashboard
|
||||
path: easytier-web/frontend/dist/
|
||||
|
||||
- name: Cargo cache
|
||||
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
@@ -110,26 +166,38 @@ jobs:
|
||||
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }}
|
||||
run: |
|
||||
bash ./.github/workflows/install_rust.sh
|
||||
|
||||
# we set the sysroot when sysroot is a dir
|
||||
# this dir is a soft link generated by install_rust.sh
|
||||
# kcp-sys need this to gen ffi bindings. without this clang may fail to find some libc headers such as bits/libc-header-start.h
|
||||
if [[ -d "./musl_gcc/sysroot" ]]; then
|
||||
export BINDGEN_EXTRA_CLANG_ARGS=--sysroot=$(readlink -f ./musl_gcc/sysroot)
|
||||
fi
|
||||
|
||||
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
||||
cargo +nightly build -r --verbose --target $TARGET -Z build-std=std,panic_abort --no-default-features --features mips
|
||||
cargo +nightly build -r --target $TARGET -Z build-std=std,panic_abort --package=easytier
|
||||
else
|
||||
cargo build --release --verbose --target $TARGET
|
||||
if [[ $OS =~ ^windows.*$ ]]; then
|
||||
SUFFIX=.exe
|
||||
fi
|
||||
cargo build --release --target $TARGET --package=easytier-web --features=embed
|
||||
mv ./target/$TARGET/release/easytier-web"$SUFFIX" ./target/$TARGET/release/easytier-web-embed"$SUFFIX"
|
||||
cargo build --release --target $TARGET
|
||||
fi
|
||||
|
||||
# Copied and slightly modified from @lmq8267 (https://github.com/lmq8267)
|
||||
- name: Build Core & Cli (X86_64 FreeBSD)
|
||||
uses: cross-platform-actions/action@v0.23.0
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
if: ${{ endsWith(matrix.TARGET, 'freebsd') }}
|
||||
env:
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
with:
|
||||
operating_system: freebsd
|
||||
environment_variables: TARGET
|
||||
architecture: x86-64
|
||||
version: ${{ matrix.BSD_VERSION }}
|
||||
shell: bash
|
||||
memory: 5G
|
||||
cpu_count: 4
|
||||
envs: TARGET
|
||||
release: ${{ matrix.BSD_VERSION }}
|
||||
arch: x86_64
|
||||
usesh: true
|
||||
mem: 6144
|
||||
cpu: 4
|
||||
run: |
|
||||
uname -a
|
||||
echo $SHELL
|
||||
@@ -138,36 +206,36 @@ jobs:
|
||||
whoami
|
||||
env | sort
|
||||
|
||||
sudo pkg install -y git protobuf
|
||||
pkg install -y git protobuf llvm-devel sudo curl
|
||||
curl --proto 'https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source $HOME/.cargo/env
|
||||
. $HOME/.cargo/env
|
||||
|
||||
rustup set auto-self-update disable
|
||||
|
||||
rustup install 1.77
|
||||
rustup default 1.77
|
||||
rustup install 1.86
|
||||
rustup default 1.86
|
||||
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export CARGO_TERM_COLOR=always
|
||||
|
||||
cargo build --release --verbose --target $TARGET --package=easytier-web --features=embed
|
||||
mv ./target/$TARGET/release/easytier-web ./target/$TARGET/release/easytier-web-embed
|
||||
cargo build --release --verbose --target $TARGET
|
||||
|
||||
- name: Install UPX
|
||||
if: ${{ matrix.OS != 'macos-latest' }}
|
||||
uses: crazy-max/ghaction-upx@v3
|
||||
with:
|
||||
version: latest
|
||||
install-only: true
|
||||
|
||||
- name: Compress
|
||||
run: |
|
||||
mkdir -p ./artifacts/objects/
|
||||
# windows is the only OS using a different convention for executable file name
|
||||
if [[ $OS =~ ^windows.*$ ]]; then
|
||||
if [[ $OS =~ ^windows.*$ && $TARGET =~ ^x86_64.*$ ]]; then
|
||||
SUFFIX=.exe
|
||||
cp easytier/third_party/Packet.dll ./artifacts/objects/
|
||||
cp easytier/third_party/wintun.dll ./artifacts/objects/
|
||||
cp easytier/third_party/*.dll ./artifacts/objects/
|
||||
elif [[ $OS =~ ^windows.*$ && $TARGET =~ ^i686.*$ ]]; then
|
||||
SUFFIX=.exe
|
||||
cp easytier/third_party/i686/*.dll ./artifacts/objects/
|
||||
elif [[ $OS =~ ^windows.*$ && $TARGET =~ ^aarch64.*$ ]]; then
|
||||
SUFFIX=.exe
|
||||
cp easytier/third_party/arm64/*.dll ./artifacts/objects/
|
||||
fi
|
||||
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
||||
TAG=$GITHUB_REF_NAME
|
||||
@@ -176,12 +244,19 @@ jobs:
|
||||
fi
|
||||
|
||||
if [[ $OS =~ ^ubuntu.*$ && ! $TARGET =~ ^.*freebsd$ ]]; then
|
||||
upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
||||
upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
||||
UPX_VERSION=4.2.4
|
||||
curl -L https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-amd64_linux.tar.xz -s | tar xJvf -
|
||||
cp upx-${UPX_VERSION}-amd64_linux/upx .
|
||||
./upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
||||
./upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
||||
fi
|
||||
|
||||
mv ./target/$TARGET/release/easytier-core"$SUFFIX" ./artifacts/objects/
|
||||
mv ./target/$TARGET/release/easytier-cli"$SUFFIX" ./artifacts/objects/
|
||||
if [[ ! $TARGET =~ ^mips.*$ ]]; then
|
||||
mv ./target/$TARGET/release/easytier-web"$SUFFIX" ./artifacts/objects/
|
||||
mv ./target/$TARGET/release/easytier-web-embed"$SUFFIX" ./artifacts/objects/
|
||||
fi
|
||||
|
||||
mv ./artifacts/objects/* ./artifacts/
|
||||
rm -rf ./artifacts/objects/
|
||||
@@ -193,25 +268,52 @@ jobs:
|
||||
path: |
|
||||
./artifacts/*
|
||||
|
||||
- name: Upload OSS
|
||||
if: ${{ env.OSS_BUCKET != '' }}
|
||||
uses: Menci/upload-to-oss@main
|
||||
with:
|
||||
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_ID }}
|
||||
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY }}
|
||||
endpoint: ${{ secrets.ALIYUN_OSS_ENDPOINT }}
|
||||
bucket: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||
local-path: ./artifacts/
|
||||
remote-path: /easytier-releases/${{env.GIT_DESC}}/easytier-${{ matrix.ARTIFACT_NAME }}
|
||||
no-delete-remote-files: true
|
||||
retry: 5
|
||||
core-result:
|
||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- pre_job
|
||||
- build_web
|
||||
- build
|
||||
steps:
|
||||
- name: Mark result as failed
|
||||
if: needs.build.result != 'success'
|
||||
run: exit 1
|
||||
|
||||
magisk_build:
|
||||
needs:
|
||||
- pre_job
|
||||
- build_web
|
||||
- build
|
||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4 # 必须先检出代码才能获取模块配置
|
||||
|
||||
# 下载二进制文件到独立目录
|
||||
- name: Download Linux aarch64 binaries
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: easytier-linux-aarch64
|
||||
path: ./downloaded-binaries/ # 独立目录避免冲突
|
||||
|
||||
# 将二进制文件复制到 Magisk 模块目录
|
||||
- name: Prepare binaries
|
||||
run: |
|
||||
mkdir -p ./easytier-contrib/easytier-magisk/
|
||||
cp ./downloaded-binaries/easytier-core ./easytier-contrib/easytier-magisk/
|
||||
cp ./downloaded-binaries/easytier-cli ./easytier-contrib/easytier-magisk/
|
||||
cp ./downloaded-binaries/easytier-web ./easytier-contrib/easytier-magisk/
|
||||
|
||||
|
||||
# 上传生成的模块
|
||||
- name: Upload Magisk Module
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Easytier-Magisk
|
||||
path: |
|
||||
./easytier-contrib/easytier-magisk
|
||||
!./easytier-contrib/easytier-magisk/build.sh
|
||||
!./easytier-contrib/easytier-magisk/magisk_update.json
|
||||
if-no-files-found: error
|
||||
|
||||
12
.github/workflows/docker.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
image_tag:
|
||||
description: 'Tag for this image build'
|
||||
type: string
|
||||
default: 'v1.2.0'
|
||||
default: 'v2.3.2'
|
||||
required: true
|
||||
mark_latest:
|
||||
description: 'Mark this image as latest'
|
||||
@@ -39,6 +39,12 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: login github container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Download artifact
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
@@ -58,4 +64,6 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: .github/workflows/Dockerfile
|
||||
tags: easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
|
||||
tags: |
|
||||
easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
|
||||
ghcr.io/easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
|
||||
|
||||
121
.github/workflows/gui.yml
vendored
@@ -36,11 +36,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- TARGET: aarch64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
GUI_TARGET: aarch64-unknown-linux-gnu
|
||||
ARTIFACT_NAME: linux-aarch64
|
||||
- TARGET: x86_64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
GUI_TARGET: x86_64-unknown-linux-gnu
|
||||
ARTIFACT_NAME: linux-x86_64
|
||||
|
||||
@@ -58,6 +58,16 @@ jobs:
|
||||
GUI_TARGET: x86_64-pc-windows-msvc
|
||||
ARTIFACT_NAME: windows-x86_64
|
||||
|
||||
- TARGET: aarch64-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
GUI_TARGET: aarch64-pc-windows-msvc
|
||||
ARTIFACT_NAME: windows-arm64
|
||||
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
GUI_TARGET: i686-pc-windows-msvc
|
||||
ARTIFACT_NAME: windows-i686
|
||||
|
||||
runs-on: ${{ matrix.OS }}
|
||||
env:
|
||||
NAME: easytier
|
||||
@@ -68,6 +78,56 @@ jobs:
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
steps:
|
||||
- name: Install GUI dependencies (x86 only)
|
||||
if: ${{ matrix.TARGET == 'x86_64-unknown-linux-musl' }}
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -qq libwebkit2gtk-4.1-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libgtk-3-dev \
|
||||
librsvg2-dev \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
patchelf
|
||||
|
||||
- name: Install GUI cross compile (aarch64 only)
|
||||
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
|
||||
run: |
|
||||
# see https://tauri.app/v1/guides/building/linux/
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy main restricted" | sudo tee /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
|
||||
sudo dpkg --add-architecture arm64
|
||||
sudo apt update
|
||||
sudo apt install aptitude
|
||||
sudo aptitude install -y libgstreamer1.0-0:arm64 gstreamer1.0-plugins-base:arm64 gstreamer1.0-plugins-good:arm64 \
|
||||
libgstreamer-gl1.0-0:arm64 libgstreamer-plugins-base1.0-0:arm64 libgstreamer-plugins-good1.0-0:arm64 libwebkit2gtk-4.1-0:arm64 \
|
||||
libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
|
||||
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set current ref as env variable
|
||||
@@ -99,8 +159,8 @@ jobs:
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
(cd easytier-gui; pnpm install)
|
||||
(cd tauri-plugin-vpnservice; pnpm install; pnpm build)
|
||||
pnpm -r install
|
||||
pnpm -r build
|
||||
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v4
|
||||
@@ -119,37 +179,16 @@ jobs:
|
||||
# GitHub repo token to use to avoid rate limiter
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install GUI cross compile (aarch64 only)
|
||||
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
|
||||
- name: copy correct DLLs
|
||||
if: ${{ matrix.OS == 'windows-latest' }}
|
||||
run: |
|
||||
# see https://tauri.app/v1/guides/building/linux/
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble main restricted" | sudo tee /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security universe" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security multiverse" | sudo tee -a /etc/apt/sources.list
|
||||
|
||||
sudo dpkg --add-architecture arm64
|
||||
sudo apt-get update && sudo apt-get upgrade -y
|
||||
sudo apt install -f -o Dpkg::Options::="--force-overwrite" libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
|
||||
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV"
|
||||
if [[ $GUI_TARGET =~ ^aarch64.*$ ]]; then
|
||||
cp ./easytier/third_party/arm64/*.dll ./easytier-gui/src-tauri/
|
||||
elif [[ $GUI_TARGET =~ ^i686.*$ ]]; then
|
||||
cp ./easytier/third_party/i686/*.dll ./easytier-gui/src-tauri/
|
||||
else
|
||||
cp ./easytier/third_party/*.dll ./easytier-gui/src-tauri/
|
||||
fi
|
||||
|
||||
- name: Build GUI
|
||||
if: ${{ matrix.GUI_TARGET != '' }}
|
||||
@@ -157,7 +196,7 @@ jobs:
|
||||
with:
|
||||
projectPath: ./easytier-gui
|
||||
# https://tauri.app/v1/guides/building/linux/#cross-compiling-tauri-applications-for-arm-based-devices
|
||||
args: --verbose --target ${{ matrix.GUI_TARGET }} ${{ matrix.OS == 'ubuntu-latest' && contains(matrix.TARGET, 'aarch64') && '--bundles deb' || '' }}
|
||||
args: --verbose --target ${{ matrix.GUI_TARGET }} ${{ matrix.OS == 'ubuntu-22.04' && contains(matrix.TARGET, 'aarch64') && '--bundles deb' || '' }}
|
||||
|
||||
- name: Compress
|
||||
run: |
|
||||
@@ -191,18 +230,6 @@ jobs:
|
||||
path: |
|
||||
./artifacts/*
|
||||
|
||||
- name: Upload OSS
|
||||
if: ${{ env.OSS_BUCKET != '' }}
|
||||
uses: Menci/upload-to-oss@main
|
||||
with:
|
||||
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_ID }}
|
||||
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY }}
|
||||
endpoint: ${{ secrets.ALIYUN_OSS_ENDPOINT }}
|
||||
bucket: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||
local-path: ./artifacts/
|
||||
remote-path: /easytier-releases/${{env.GIT_DESC}}/easytier-gui-${{ matrix.ARTIFACT_NAME }}
|
||||
no-delete-remote-files: true
|
||||
retry: 5
|
||||
gui-result:
|
||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
62
.github/workflows/install_rust.sh
vendored
@@ -8,61 +8,33 @@
|
||||
# dependencies are only needed on ubuntu as that's the only place where
|
||||
# we make cross-compilation
|
||||
if [[ $OS =~ ^ubuntu.*$ ]]; then
|
||||
sudo apt-get update && sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools libappindicator3-dev
|
||||
# for easytier-gui
|
||||
if [[ $GUI_TARGET != '' && $GUI_TARGET =~ ^x86_64.*$ ]]; then
|
||||
sudo apt install -qq libwebkit2gtk-4.1-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libgtk-3-dev \
|
||||
librsvg2-dev \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
patchelf
|
||||
sudo apt-get update && sudo apt-get install -qq musl-tools libappindicator3-dev llvm clang
|
||||
# https://github.com/cross-tools/musl-cross/releases
|
||||
# if "musl" is a substring of TARGET, we assume that we are using musl
|
||||
MUSL_TARGET=$TARGET
|
||||
# if target is mips or mipsel, we should use soft-float version of musl
|
||||
if [[ $TARGET =~ ^mips.*$ || $TARGET =~ ^mipsel.*$ ]]; then
|
||||
MUSL_TARGET=${TARGET}sf
|
||||
fi
|
||||
# curl -s musl.cc | grep mipsel
|
||||
case $TARGET in
|
||||
mipsel-unknown-linux-musl)
|
||||
MUSL_URI=mipsel-linux-muslsf
|
||||
;;
|
||||
mips-unknown-linux-musl)
|
||||
MUSL_URI=mips-linux-muslsf
|
||||
;;
|
||||
aarch64-unknown-linux-musl)
|
||||
MUSL_URI=aarch64-linux-musl
|
||||
;;
|
||||
armv7-unknown-linux-musleabihf)
|
||||
MUSL_URI=armv7l-linux-musleabihf
|
||||
;;
|
||||
armv7-unknown-linux-musleabi)
|
||||
MUSL_URI=armv7m-linux-musleabi
|
||||
;;
|
||||
arm-unknown-linux-musleabihf)
|
||||
MUSL_URI=arm-linux-musleabihf
|
||||
;;
|
||||
arm-unknown-linux-musleabi)
|
||||
MUSL_URI=arm-linux-musleabi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$MUSL_URI" ]; then
|
||||
if [[ $MUSL_TARGET =~ musl ]]; then
|
||||
mkdir -p ./musl_gcc
|
||||
wget -c https://musl.cc/${MUSL_URI}-cross.tgz -P ./musl_gcc/
|
||||
tar zxf ./musl_gcc/${MUSL_URI}-cross.tgz -C ./musl_gcc/
|
||||
sudo ln -s $(pwd)/musl_gcc/${MUSL_URI}-cross/bin/*gcc /usr/bin/
|
||||
wget --inet4-only -c https://github.com/cross-tools/musl-cross/releases/download/20250520/${MUSL_TARGET}.tar.xz -P ./musl_gcc/
|
||||
tar xf ./musl_gcc/${MUSL_TARGET}.tar.xz -C ./musl_gcc/
|
||||
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/bin/*gcc /usr/bin/
|
||||
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/include/ /usr/include/musl-cross
|
||||
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/${MUSL_TARGET}/sysroot/ ./musl_gcc/sysroot
|
||||
sudo chmod -R a+rwx ./musl_gcc
|
||||
fi
|
||||
fi
|
||||
|
||||
# see https://github.com/rust-lang/rustup/issues/3709
|
||||
rustup set auto-self-update disable
|
||||
rustup install 1.77
|
||||
rustup default 1.77
|
||||
rustup install 1.86
|
||||
rustup default 1.86
|
||||
|
||||
# mips/mipsel cannot add target from rustup, need compile by ourselves
|
||||
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
||||
cd "$PWD/musl_gcc/${MUSL_URI}-cross/lib/gcc/${MUSL_URI}/11.2.1" || exit 255
|
||||
cd "$PWD/musl_gcc/${MUSL_TARGET}/lib/gcc/${MUSL_TARGET}/15.1.0" || exit 255
|
||||
# for panic-abort
|
||||
cp libgcc_eh.a libunwind.a
|
||||
|
||||
|
||||
18
.github/workflows/mobile.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- TARGET: android
|
||||
OS: ubuntu-latest
|
||||
OS: ubuntu-22.04
|
||||
ARTIFACT_NAME: android
|
||||
runs-on: ${{ matrix.OS }}
|
||||
env:
|
||||
@@ -95,8 +95,8 @@ jobs:
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
(cd easytier-gui; pnpm install)
|
||||
(cd tauri-plugin-vpnservice; pnpm install; pnpm build)
|
||||
pnpm -r install
|
||||
pnpm -r build
|
||||
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v4
|
||||
@@ -146,18 +146,6 @@ jobs:
|
||||
path: |
|
||||
./artifacts/*
|
||||
|
||||
- name: Upload OSS
|
||||
if: ${{ env.OSS_BUCKET != '' }}
|
||||
uses: Menci/upload-to-oss@main
|
||||
with:
|
||||
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_ID }}
|
||||
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY }}
|
||||
endpoint: ${{ secrets.ALIYUN_OSS_ENDPOINT }}
|
||||
bucket: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||
local-path: ./artifacts/
|
||||
remote-path: /easytier-releases/${{env.GIT_DESC}}/easytier-gui-${{ matrix.ARTIFACT_NAME }}
|
||||
no-delete-remote-files: true
|
||||
retry: 5
|
||||
mobile-result:
|
||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
13
.github/workflows/release.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
version:
|
||||
description: 'Version for this release'
|
||||
type: string
|
||||
default: 'v2.0.2'
|
||||
default: 'v2.3.2'
|
||||
required: true
|
||||
make_latest:
|
||||
description: 'Mark this release as latest'
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
repo: EasyTier/EasyTier
|
||||
path: release_assets_nozip
|
||||
|
||||
- name: Download GUI Artifact
|
||||
- name: Download Mobile Artifact
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -78,7 +78,14 @@ jobs:
|
||||
ls -l -R ./
|
||||
chmod -R 755 .
|
||||
for x in `ls`; do
|
||||
zip ../zipped_assets/$x-${VERSION}.zip $x/*;
|
||||
if [ "$x" = "Easytier-Magisk" ]; then
|
||||
# for Easytier-Magisk, make sure files are in the root of the zip
|
||||
cd $x;
|
||||
zip -r ../../zipped_assets/$x-${VERSION}.zip .;
|
||||
cd ..;
|
||||
else
|
||||
zip -r ../zipped_assets/$x-${VERSION}.zip $x;
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Release
|
||||
|
||||
34
.github/workflows/test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
skip_after_successful_duplicate: 'true'
|
||||
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/test.yml"]'
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
steps:
|
||||
@@ -47,11 +47,40 @@ jobs:
|
||||
|
||||
- name: Setup system for test
|
||||
run: |
|
||||
sudo modprobe br_netfilter
|
||||
sudo sysctl net.bridge.bridge-nf-call-iptables=0
|
||||
sudo sysctl net.bridge.bridge-nf-call-ip6tables=0
|
||||
sudo sysctl net.ipv6.conf.lo.disable_ipv6=0
|
||||
sudo ip addr add 2001:db8::2/64 dev lo
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
pnpm -r install
|
||||
pnpm -r --filter "./easytier-web/*" build
|
||||
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -62,6 +91,7 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
sudo -E env "PATH=$PATH" cargo test --no-default-features --features=full --verbose
|
||||
sudo prlimit --pid $$ --nofile=1048576:1048576
|
||||
sudo -E env "PATH=$PATH" cargo test --no-default-features --features=full --verbose -- --test-threads=1
|
||||
sudo chown -R $USER:$USER ./target
|
||||
sudo chown -R $USER:$USER ~/.cargo
|
||||
|
||||
8
.gitignore
vendored
@@ -11,6 +11,7 @@ target-*/
|
||||
*.pdb
|
||||
|
||||
.vscode
|
||||
/.idea
|
||||
|
||||
# perf & flamegraph
|
||||
perf.data
|
||||
@@ -29,3 +30,10 @@ musl_gcc
|
||||
|
||||
# log
|
||||
easytier-panic.log
|
||||
|
||||
# web
|
||||
node_modules
|
||||
|
||||
.vite
|
||||
|
||||
easytier-gui/src-tauri/*.dll
|
||||
|
||||
4385
Cargo.lock
generated
12
Cargo.toml
@@ -1,7 +1,13 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["easytier", "easytier-gui/src-tauri"]
|
||||
default-members = ["easytier"]
|
||||
members = [
|
||||
"easytier",
|
||||
"easytier-gui/src-tauri",
|
||||
"easytier-rpc-build",
|
||||
"easytier-web",
|
||||
"easytier-contrib/easytier-ffi",
|
||||
]
|
||||
default-members = ["easytier", "easytier-web"]
|
||||
|
||||
[profile.dev]
|
||||
panic = "unwind"
|
||||
@@ -10,3 +16,5 @@ panic = "unwind"
|
||||
panic = "abort"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
strip = true
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
{
|
||||
"name": "vpnservice",
|
||||
"path": "tauri-plugin-vpnservice"
|
||||
},
|
||||
{
|
||||
"name": "rpc-build",
|
||||
"path": "easytier-rpc-build"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
|
||||
174
LICENSE
@@ -1,73 +1,165 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
0. Additional Definitions.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
4. Combined Works.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
d) Do one of the following:
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
Copyright 2023 sunsijie
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
5. Combined Libraries.
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser 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
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
||||
53
README.md
@@ -1,14 +1,17 @@
|
||||
# EasyTier
|
||||
|
||||
[](https://github.com/EasyTier/EasyTier/releases)
|
||||
[](https://github.com/EasyTier/EasyTier/blob/main/LICENSE)
|
||||
[](https://github.com/EasyTier/EasyTier/commits/main)
|
||||
[](https://github.com/EasyTier/EasyTier/issues)
|
||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/core.yml)
|
||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/gui.yml)
|
||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/test.yml)
|
||||
[](https://deepwiki.com/EasyTier/EasyTier)
|
||||
|
||||
[简体中文](/README_CN.md) | [English](/README.md)
|
||||
|
||||
**Please visit the [EasyTier Official Website](https://www.easytier.top/en/) to view the full documentation.**
|
||||
**Please visit the [EasyTier Official Website](https://easytier.cn/en/) to view the full documentation.**
|
||||
|
||||
EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.
|
||||
|
||||
@@ -31,6 +34,7 @@ EasyTier is a simple, safe and decentralized VPN networking solution implemented
|
||||
- **High Availability**: Supports multi-path and switches to healthy paths when high packet loss or network errors are detected.
|
||||
- **IPv6 Support**: Supports networking using IPv6.
|
||||
- **Multiple Protocol Types**: Supports communication between nodes using protocols such as WebSocket and QUIC.
|
||||
- **Web Management Interface**: Provides a [web-based management](https://easytier.cn/web) interface for easy configuration and monitoring.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -52,7 +56,7 @@ EasyTier is a simple, safe and decentralized VPN networking solution implemented
|
||||
|
||||
4. **Install by Docker Compose**
|
||||
|
||||
Please visit the [EasyTier Official Website](https://www.easytier.top/en/) to view the full documentation.
|
||||
Please visit the [EasyTier Official Website](https://easytier.cn/en/) to view the full documentation.
|
||||
|
||||
5. **Install by script (For Linux Only)**
|
||||
|
||||
@@ -60,13 +64,42 @@ EasyTier is a simple, safe and decentralized VPN networking solution implemented
|
||||
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh" && bash /tmp/easytier.sh install
|
||||
```
|
||||
|
||||
You can also uninstall/update Easytier by the command "uninstall" or "update" of this script
|
||||
The script supports the following commands and options:
|
||||
|
||||
Commands:
|
||||
- `install`: Install EasyTier
|
||||
- `uninstall`: Uninstall EasyTier
|
||||
- `update`: Update EasyTier to the latest version
|
||||
- `help`: Show help message
|
||||
|
||||
Options:
|
||||
- `--skip-folder-verify`: Skip folder verification during installation
|
||||
- `--skip-folder-fix`: Skip automatic folder path fixing
|
||||
- `--no-gh-proxy`: Disable GitHub proxy
|
||||
- `--gh-proxy`: Set custom GitHub proxy URL (default: https://ghfast.top/)
|
||||
|
||||
Examples:
|
||||
```sh
|
||||
# Show help
|
||||
bash /tmp/easytier.sh help
|
||||
|
||||
# Install with options
|
||||
bash /tmp/easytier.sh install --skip-folder-verify
|
||||
bash /tmp/easytier.sh install --no-gh-proxy
|
||||
bash /tmp/easytier.sh install --gh-proxy https://your-proxy.com/
|
||||
|
||||
# Update EasyTier
|
||||
bash /tmp/easytier.sh update
|
||||
|
||||
# Uninstall EasyTier
|
||||
bash /tmp/easytier.sh uninstall
|
||||
```
|
||||
|
||||
6. **Install by Homebrew (For MacOS Only)**
|
||||
|
||||
```sh
|
||||
brew tap brewforge/chinese
|
||||
brew install --cask easytier
|
||||
brew install --cask easytier-gui
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
@@ -200,20 +233,20 @@ Subnet proxy information will automatically sync to each node in the virtual net
|
||||
|
||||
### Networking without Public IP
|
||||
|
||||
EasyTier supports networking using shared public nodes. The currently deployed shared public node is ``tcp://public.easytier.top:11010``.
|
||||
EasyTier supports networking using shared public nodes. The currently deployed shared public node is ``tcp://public.easytier.cn:11010``.
|
||||
|
||||
When using shared nodes, each node entering the network needs to provide the same ``--network-name`` and ``--network-secret`` parameters as the unique identifier of the network.
|
||||
|
||||
Taking two nodes as an example, Node A executes:
|
||||
|
||||
```sh
|
||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -e tcp://public.easytier.top:11010
|
||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
Node B executes
|
||||
|
||||
```sh
|
||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -e tcp://public.easytier.top:11010
|
||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
After the command is successfully executed, Node A can access Node B through the virtual IP 10.144.144.2.
|
||||
@@ -286,7 +319,7 @@ Run you own public server cluster is exactly same as running an virtual network,
|
||||
You can also join the official public server cluster with following command:
|
||||
|
||||
```
|
||||
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.top:11010
|
||||
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
|
||||
@@ -296,10 +329,8 @@ You can use ``easytier-core --help`` to view all configuration items
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Improve documentation and user guides.
|
||||
- [ ] Support features such as encryption, TCP hole punching, etc.
|
||||
- [ ] Support features such TCP hole punching, KCP, FEC etc.
|
||||
- [ ] Support iOS.
|
||||
- [ ] Support Web configuration management.
|
||||
|
||||
## Community and Contribution
|
||||
|
||||
|
||||
49
README_CN.md
@@ -8,7 +8,7 @@
|
||||
|
||||
[简体中文](/README_CN.md) | [English](/README.md)
|
||||
|
||||
**请访问 [EasyTier 官网](https://www.easytier.top/) 以查看完整的文档。**
|
||||
**请访问 [EasyTier 官网](https://easytier.cn/) 以查看完整的文档。**
|
||||
|
||||
一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
- **高可用性**:支持多路径和在检测到高丢包率或网络错误时切换到健康路径。
|
||||
- **IPV6 支持**:支持利用 IPV6 组网。
|
||||
- **多协议类型**: 支持使用 WebSocket、QUIC 等协议进行节点间通信。
|
||||
- **Web 管理界面**:支持通过 [Web 界面](https://easytier.cn)管理节点。
|
||||
|
||||
## 安装
|
||||
|
||||
@@ -52,7 +53,7 @@
|
||||
|
||||
4. **通过Docker Compose安装**
|
||||
|
||||
请访问 [EasyTier 官网](https://www.easytier.top/) 以查看完整的文档。
|
||||
请访问 [EasyTier 官网](https://easytier.cn/) 以查看完整的文档。
|
||||
|
||||
5. **使用一键脚本安装 (仅适用于 Linux)**
|
||||
|
||||
@@ -60,13 +61,42 @@
|
||||
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh" && bash /tmp/easytier.sh install
|
||||
```
|
||||
|
||||
使用本脚本安装的 Easytier 可以使用脚本的 uninstall/update 对其卸载/升级
|
||||
脚本支持以下命令和选项:
|
||||
|
||||
命令:
|
||||
- `install`: 安装 EasyTier
|
||||
- `uninstall`: 卸载 EasyTier
|
||||
- `update`: 更新 EasyTier 到最新版本
|
||||
- `help`: 显示帮助信息
|
||||
|
||||
选项:
|
||||
- `--skip-folder-verify`: 跳过安装过程中的文件夹验证
|
||||
- `--skip-folder-fix`: 跳过自动修复文件夹路径
|
||||
- `--no-gh-proxy`: 禁用 GitHub 代理
|
||||
- `--gh-proxy`: 设置自定义 GitHub 代理 URL (默认值: https://ghfast.top/)
|
||||
|
||||
示例:
|
||||
```sh
|
||||
# 查看帮助
|
||||
bash /tmp/easytier.sh help
|
||||
|
||||
# 安装(带选项)
|
||||
bash /tmp/easytier.sh install --skip-folder-verify
|
||||
bash /tmp/easytier.sh install --no-gh-proxy
|
||||
bash /tmp/easytier.sh install --gh-proxy https://your-proxy.com/
|
||||
|
||||
# 更新 EasyTier
|
||||
bash /tmp/easytier.sh update
|
||||
|
||||
# 卸载 EasyTier
|
||||
bash /tmp/easytier.sh uninstall
|
||||
```
|
||||
|
||||
6. **使用 Homebrew 安装 (仅适用于 MacOS)**
|
||||
|
||||
```sh
|
||||
brew tap brewforge/chinese
|
||||
brew install --cask easytier
|
||||
brew install --cask easytier-gui
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
@@ -199,20 +229,20 @@ sudo easytier-core --ipv4 10.144.144.2 -n 10.1.1.0/24
|
||||
|
||||
### 无公网IP组网
|
||||
|
||||
EasyTier 支持共享公网节点进行组网。目前已部署共享的公网节点 ``tcp://public.easytier.top:11010``。
|
||||
EasyTier 支持共享公网节点进行组网。目前已部署共享的公网节点 ``tcp://public.easytier.cn:11010``。
|
||||
|
||||
使用共享节点时,需要每个入网节点提供相同的 ``--network-name`` 和 ``--network-secret`` 参数,作为网络的唯一标识。
|
||||
|
||||
以双节点为例,节点 A 执行:
|
||||
|
||||
```sh
|
||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -e tcp://public.easytier.top:11010
|
||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
节点 B 执行
|
||||
|
||||
```sh
|
||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -e tcp://public.easytier.top:11010
|
||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
命令执行成功后,节点 A 即可通过虚拟 IP 10.144.144.2 访问节点 B。
|
||||
@@ -289,7 +319,7 @@ connected_clients:
|
||||
也可以使用以下命令加入官方公共服务器集群,后续将实现公共服务器集群的节点间负载均衡:
|
||||
|
||||
```
|
||||
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.top:11010
|
||||
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.cn:11010
|
||||
```
|
||||
|
||||
### 其他配置
|
||||
@@ -299,9 +329,8 @@ sudo easytier-core --network-name easytier --network-secret easytier -p tcp://pu
|
||||
## 路线图
|
||||
|
||||
- [ ] 完善文档和用户指南。
|
||||
- [ ] 支持 TCP 打洞等特性。
|
||||
- [ ] 支持 TCP 打洞、KCP、FEC 等特性。
|
||||
- [ ] 支持 iOS。
|
||||
- [ ] 支持 Web 配置管理。
|
||||
|
||||
## 社区和贡献
|
||||
|
||||
|
||||
17
easytier-contrib/easytier-ffi/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "easytier-ffi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
easytier = { path = "../../easytier" }
|
||||
|
||||
once_cell = "1.18.0"
|
||||
dashmap = "6.0"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
uuid = "1.17.0"
|
||||
159
easytier-contrib/easytier-ffi/examples/csharp.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
public class EasyTierFFI
|
||||
{
|
||||
// 导入 DLL 函数
|
||||
private const string DllName = "easytier_ffi.dll";
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int parse_config([MarshalAs(UnmanagedType.LPStr)] string cfgStr);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int run_network_instance([MarshalAs(UnmanagedType.LPStr)] string cfgStr);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int retain_network_instance(IntPtr instNames, int length);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int collect_network_infos(IntPtr infos, int maxLength);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void get_error_msg(out IntPtr errorMsg);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void free_string(IntPtr str);
|
||||
|
||||
// 定义 KeyValuePair 结构体
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KeyValuePair
|
||||
{
|
||||
public IntPtr Key;
|
||||
public IntPtr Value;
|
||||
}
|
||||
|
||||
// 解析配置
|
||||
public static void ParseConfig(string config)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config))
|
||||
{
|
||||
throw new ArgumentException("Configuration string cannot be null or empty.");
|
||||
}
|
||||
|
||||
int result = parse_config(config);
|
||||
if (result < 0)
|
||||
{
|
||||
throw new Exception(GetErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 启动网络实例
|
||||
public static void RunNetworkInstance(string config)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config))
|
||||
{
|
||||
throw new ArgumentException("Configuration string cannot be null or empty.");
|
||||
}
|
||||
|
||||
int result = run_network_instance(config);
|
||||
if (result < 0)
|
||||
{
|
||||
throw new Exception(GetErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 保留网络实例
|
||||
public static void RetainNetworkInstances(string[] instanceNames)
|
||||
{
|
||||
IntPtr[] namePointers = null;
|
||||
IntPtr namesPtr = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
if (instanceNames != null && instanceNames.Length > 0)
|
||||
{
|
||||
namePointers = new IntPtr[instanceNames.Length];
|
||||
for (int i = 0; i < instanceNames.Length; i++)
|
||||
{
|
||||
if (string.IsNullOrEmpty(instanceNames[i]))
|
||||
{
|
||||
throw new ArgumentException("Instance name cannot be null or empty.");
|
||||
}
|
||||
namePointers[i] = Marshal.StringToHGlobalAnsi(instanceNames[i]);
|
||||
}
|
||||
|
||||
namesPtr = Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>() * namePointers.Length);
|
||||
Marshal.Copy(namePointers, 0, namesPtr, namePointers.Length);
|
||||
}
|
||||
|
||||
int result = retain_network_instance(namesPtr, instanceNames?.Length ?? 0);
|
||||
if (result < 0)
|
||||
{
|
||||
throw new Exception(GetErrorMessage());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (namePointers != null)
|
||||
{
|
||||
foreach (var ptr in namePointers)
|
||||
{
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (namesPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(namesPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收集网络信息
|
||||
public static KeyValuePair<string, string>[] CollectNetworkInfos(int maxLength)
|
||||
{
|
||||
IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf<KeyValuePair>() * maxLength);
|
||||
try
|
||||
{
|
||||
int count = collect_network_infos(buffer, maxLength);
|
||||
if (count < 0)
|
||||
{
|
||||
throw new Exception(GetErrorMessage());
|
||||
}
|
||||
|
||||
var result = new KeyValuePair<string, string>[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var kv = Marshal.PtrToStructure<KeyValuePair>(buffer + i * Marshal.SizeOf<KeyValuePair>());
|
||||
string key = Marshal.PtrToStringAnsi(kv.Key);
|
||||
string value = Marshal.PtrToStringAnsi(kv.Value);
|
||||
|
||||
// 释放由 FFI 分配的字符串内存
|
||||
free_string(kv.Key);
|
||||
free_string(kv.Value);
|
||||
|
||||
result[i] = new KeyValuePair<string, string>(key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取错误信息
|
||||
private static string GetErrorMessage()
|
||||
{
|
||||
get_error_msg(out IntPtr errorMsgPtr);
|
||||
if (errorMsgPtr == IntPtr.Zero)
|
||||
{
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
string errorMsg = Marshal.PtrToStringAnsi(errorMsgPtr);
|
||||
free_string(errorMsgPtr); // 释放错误信息字符串
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
224
easytier-contrib/easytier-ffi/src/lib.rs
Normal file
@@ -0,0 +1,224 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use easytier::{
|
||||
common::config::{ConfigLoader as _, TomlConfigLoader},
|
||||
instance_manager::NetworkInstanceManager,
|
||||
launcher::ConfigSource,
|
||||
};
|
||||
|
||||
static INSTANCE_NAME_ID_MAP: once_cell::sync::Lazy<DashMap<String, uuid::Uuid>> =
|
||||
once_cell::sync::Lazy::new(DashMap::new);
|
||||
static INSTANCE_MANAGER: once_cell::sync::Lazy<NetworkInstanceManager> =
|
||||
once_cell::sync::Lazy::new(NetworkInstanceManager::new);
|
||||
|
||||
static ERROR_MSG: once_cell::sync::Lazy<Mutex<Vec<u8>>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(Vec::new()));
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KeyValuePair {
|
||||
pub key: *const std::ffi::c_char,
|
||||
pub value: *const std::ffi::c_char,
|
||||
}
|
||||
|
||||
fn set_error_msg(msg: &str) {
|
||||
let bytes = msg.as_bytes();
|
||||
let mut msg_buf = ERROR_MSG.lock().unwrap();
|
||||
let len = bytes.len();
|
||||
msg_buf.resize(len, 0);
|
||||
msg_buf[..len].copy_from_slice(bytes);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn get_error_msg(out: *mut *const std::ffi::c_char) {
|
||||
let msg_buf = ERROR_MSG.lock().unwrap();
|
||||
if msg_buf.is_empty() {
|
||||
unsafe {
|
||||
*out = std::ptr::null();
|
||||
}
|
||||
return;
|
||||
}
|
||||
let cstr = std::ffi::CString::new(&msg_buf[..]).unwrap();
|
||||
unsafe {
|
||||
*out = cstr.into_raw();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn free_string(s: *const std::ffi::c_char) {
|
||||
if s.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let _ = std::ffi::CString::from_raw(s as *mut std::ffi::c_char);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn parse_config(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
|
||||
let cfg_str = unsafe {
|
||||
assert!(!cfg_str.is_null());
|
||||
std::ffi::CStr::from_ptr(cfg_str)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
};
|
||||
|
||||
if let Err(e) = TomlConfigLoader::new_from_str(&cfg_str) {
|
||||
set_error_msg(&format!("failed to parse config: {:?}", e));
|
||||
return -1;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn run_network_instance(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
|
||||
let cfg_str = unsafe {
|
||||
assert!(!cfg_str.is_null());
|
||||
std::ffi::CStr::from_ptr(cfg_str)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
};
|
||||
let cfg = match TomlConfigLoader::new_from_str(&cfg_str) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => {
|
||||
set_error_msg(&format!("failed to parse config: {}", e));
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
let inst_name = cfg.get_inst_name();
|
||||
|
||||
if INSTANCE_NAME_ID_MAP.contains_key(&inst_name) {
|
||||
set_error_msg("instance already exists");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let instance_id = match INSTANCE_MANAGER.run_network_instance(cfg, ConfigSource::FFI) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
set_error_msg(&format!("failed to start instance: {}", e));
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
INSTANCE_NAME_ID_MAP.insert(inst_name, instance_id);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn retain_network_instance(
|
||||
inst_names: *const *const std::ffi::c_char,
|
||||
length: usize,
|
||||
) -> std::ffi::c_int {
|
||||
if length == 0 {
|
||||
if let Err(e) = INSTANCE_MANAGER.retain_network_instance(Vec::new()) {
|
||||
set_error_msg(&format!("failed to retain instances: {}", e));
|
||||
return -1;
|
||||
}
|
||||
INSTANCE_NAME_ID_MAP.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
let inst_names = unsafe {
|
||||
assert!(!inst_names.is_null());
|
||||
std::slice::from_raw_parts(inst_names, length)
|
||||
.iter()
|
||||
.map(|&name| {
|
||||
assert!(!name.is_null());
|
||||
std::ffi::CStr::from_ptr(name)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let inst_ids: Vec<uuid::Uuid> = inst_names
|
||||
.iter()
|
||||
.filter_map(|name| INSTANCE_NAME_ID_MAP.get(name).map(|id| *id))
|
||||
.collect();
|
||||
|
||||
if let Err(e) = INSTANCE_MANAGER.retain_network_instance(inst_ids) {
|
||||
set_error_msg(&format!("failed to retain instances: {}", e));
|
||||
return -1;
|
||||
}
|
||||
|
||||
let _ = INSTANCE_NAME_ID_MAP.retain(|k, _| inst_names.contains(k));
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn collect_network_infos(
|
||||
infos: *mut KeyValuePair,
|
||||
max_length: usize,
|
||||
) -> std::ffi::c_int {
|
||||
if max_length == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let infos = unsafe {
|
||||
assert!(!infos.is_null());
|
||||
std::slice::from_raw_parts_mut(infos, max_length)
|
||||
};
|
||||
|
||||
let collected_infos = match INSTANCE_MANAGER.collect_network_infos() {
|
||||
Ok(infos) => infos,
|
||||
Err(e) => {
|
||||
set_error_msg(&format!("failed to collect network infos: {}", e));
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
let mut index = 0;
|
||||
for (instance_id, value) in collected_infos.iter() {
|
||||
if index >= max_length {
|
||||
break;
|
||||
}
|
||||
let Some(key) = INSTANCE_MANAGER.get_network_instance_name(instance_id) else {
|
||||
continue;
|
||||
};
|
||||
// convert value to json string
|
||||
let value = match serde_json::to_string(&value) {
|
||||
Ok(value) => value,
|
||||
Err(e) => {
|
||||
set_error_msg(&format!("failed to serialize instance info: {}", e));
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
infos[index] = KeyValuePair {
|
||||
key: std::ffi::CString::new(key.clone()).unwrap().into_raw(),
|
||||
value: std::ffi::CString::new(value).unwrap().into_raw(),
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
|
||||
index as std::ffi::c_int
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_config() {
|
||||
let cfg_str = r#"
|
||||
inst_name = "test"
|
||||
network = "test_network"
|
||||
"#;
|
||||
let cstr = std::ffi::CString::new(cfg_str).unwrap();
|
||||
assert_eq!(parse_config(cstr.as_ptr()), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_network_instance() {
|
||||
let cfg_str = r#"
|
||||
inst_name = "test"
|
||||
network = "test_network"
|
||||
"#;
|
||||
let cstr = std::ffi::CString::new(cfg_str).unwrap();
|
||||
assert_eq!(run_network_instance(cstr.as_ptr()), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/sbin/sh
|
||||
|
||||
#################
|
||||
# Initialization
|
||||
#################
|
||||
|
||||
umask 022
|
||||
|
||||
# echo before loading util_functions
|
||||
ui_print() { echo "$1"; }
|
||||
|
||||
require_new_magisk() {
|
||||
ui_print "********************************"
|
||||
ui_print " Please install Magisk v20.4+! "
|
||||
ui_print "********************************"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#########################
|
||||
# Load util_functions.sh
|
||||
#########################
|
||||
|
||||
OUTFD=$2
|
||||
ZIPFILE=$3
|
||||
|
||||
mount /data 2>/dev/null
|
||||
|
||||
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||
. /data/adb/magisk/util_functions.sh
|
||||
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
||||
|
||||
install_module
|
||||
exit 0
|
||||
@@ -0,0 +1 @@
|
||||
#MAGISK
|
||||
6
easytier-contrib/easytier-magisk/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# easytier_magisk版模块
|
||||
magisk安装后重启
|
||||
|
||||
目录位置:/data/adb/modules/easytier_magisk
|
||||
配置文件位置://data/adb/modules/easytier_magisk/config/config.toml
|
||||
修改config.conf即可,修改后配置文件后去magisk app重新开关模块即可生效
|
||||
14
easytier-contrib/easytier-magisk/action.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/data/adb/magisk/busybox sh
|
||||
MODDIR=${0%/*}
|
||||
|
||||
# 查找 easytier-core 进程的 PID
|
||||
PID=$(pgrep easytier-core)
|
||||
|
||||
# 检查是否找到了进程
|
||||
if [ -z "$PID" ]; then
|
||||
echo "easytier-core 进程未找到"
|
||||
else
|
||||
# 结束进程
|
||||
kill $PID
|
||||
echo "已结束 easytier-core 进程 (PID: $PID)"
|
||||
fi
|
||||
25
easytier-contrib/easytier-magisk/build.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
version=$(cat module.prop | grep 'version=' | awk -F '=' '{print $2}' | sed 's/ (.*//')
|
||||
|
||||
version='v'$(grep '^version =' ../../easytier/Cargo.toml | cut -d '"' -f 2)
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
echo "Error: 版本号不存在."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
filename="easytier_magisk_${version}.zip"
|
||||
echo $version
|
||||
|
||||
|
||||
if [ -f "./easytier-core" ] && [ -f "./easytier-cli" ] && [ -f "./easytier-web" ]; then
|
||||
zip -r -o -X "$filename" ./ -x '.git/*' -x '.github/*' -x 'folder/*' -x 'build.sh' -x 'magisk_update.json'
|
||||
else
|
||||
wget -O "easytier_last.zip" https://github.com/EasyTier/EasyTier/releases/download/"$version"/easytier-linux-aarch64-"$version".zip
|
||||
unzip -o easytier_last.zip -d ./
|
||||
mv ./easytier-linux-aarch64/* ./
|
||||
rm -rf ./easytier_last.zip
|
||||
rm -rf ./easytier-linux-aarch64
|
||||
zip -r -o -X "$filename" ./ -x '.git/*' -x '.github/*' -x 'folder/*' -x 'build.sh' -x 'magisk_update.json'
|
||||
fi
|
||||
37
easytier-contrib/easytier-magisk/config/config.toml
Normal file
@@ -0,0 +1,37 @@
|
||||
instance_name = "default"
|
||||
dhcp = false
|
||||
#ipv4="本机ip"
|
||||
listeners = [
|
||||
"tcp://0.0.0.0:11010",
|
||||
"udp://0.0.0.0:11010",
|
||||
"wg://0.0.0.0:11011",
|
||||
"ws://0.0.0.0:11011/",
|
||||
"wss://0.0.0.0:11012/",
|
||||
]
|
||||
mapped_listeners = []
|
||||
exit_nodes = []
|
||||
rpc_portal = "0.0.0.0:15888"
|
||||
|
||||
[network_identity]
|
||||
network_name = "default"
|
||||
network_secret = ""
|
||||
|
||||
[[peer]]
|
||||
#uri = "协议://中转ip:端口"
|
||||
|
||||
[flags]
|
||||
default_protocol = "tcp"
|
||||
dev_name = ""
|
||||
enable_encryption = true
|
||||
enable_ipv6 = true
|
||||
mtu = 1380
|
||||
latency_first = false
|
||||
enable_exit_node = false
|
||||
no_tun = false
|
||||
use_smoltcp = false
|
||||
foreign_network_whitelist = "*"
|
||||
disable_p2p = false
|
||||
relay_all_peer_rpc = false
|
||||
disable_udp_hole_punching = false
|
||||
|
||||
|
||||
7
easytier-contrib/easytier-magisk/customize.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
ui_print '安装完成'
|
||||
ui_print '当前架构为' + $ARCH
|
||||
ui_print '当前系统版本为' + $API
|
||||
ui_print '安装目录为: /data/adb/modules/easytier_magisk'
|
||||
ui_print '配置文件位置: /data/adb/modules/easytier_magisk/config/config.toml'
|
||||
ui_print '修改后配置文件后在magisk app点击操作按钮即可生效'
|
||||
ui_print '记得重启'
|
||||
48
easytier-contrib/easytier-magisk/easytier_core.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
MODDIR=${0%/*}
|
||||
CONFIG_FILE="${MODDIR}/config/config.toml"
|
||||
LOG_FILE="${MODDIR}/log.log"
|
||||
MODULE_PROP="${MODDIR}/module.prop"
|
||||
EASYTIER="${MODDIR}/easytier-core"
|
||||
|
||||
# 更新module.prop文件中的description
|
||||
update_module_description() {
|
||||
local status_message=$1
|
||||
sed -i "/^description=/c\description=[状态]${status_message}" ${MODULE_PROP}
|
||||
}
|
||||
|
||||
if [ ! -e /dev/net/tun ]; then
|
||||
if [ ! -d /dev/net ]; then
|
||||
mkdir -p /dev/net
|
||||
fi
|
||||
|
||||
ln -s /dev/tun /dev/net/tun
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if ls $MODDIR | grep -q "disable"; then
|
||||
update_module_description "关闭中"
|
||||
if pgrep -f 'easytier-core' >/dev/null; then
|
||||
echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在,正在关闭 ..."
|
||||
pkill easytier-core # 关闭进程
|
||||
fi
|
||||
else
|
||||
if ! pgrep -f 'easytier-core' >/dev/null; then
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
update_module_description "config.toml不存在"
|
||||
sleep 3s
|
||||
continue
|
||||
fi
|
||||
|
||||
TZ=Asia/Shanghai ${EASYTIER} -c ${CONFIG_FILE} > ${LOG_FILE} &
|
||||
sleep 5s # 等待easytier-core启动完成
|
||||
update_module_description "已开启(不一定运行成功)"
|
||||
ip rule add from all lookup main
|
||||
else
|
||||
echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在"
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 3s # 暂停3秒后再次执行循环
|
||||
done
|
||||
6
easytier-contrib/easytier-magisk/magisk_update.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "v1.0",
|
||||
"versionCode": 1,
|
||||
"zipUrl": "",
|
||||
"changelog": ""
|
||||
}
|
||||
7
easytier-contrib/easytier-magisk/module.prop
Normal file
@@ -0,0 +1,7 @@
|
||||
id=easytier_magisk
|
||||
name=EasyTier_Magisk
|
||||
version=v2.3.2
|
||||
versionCode=1
|
||||
author=EasyTier
|
||||
description=easytier magisk module @EasyTier(https://github.com/EasyTier/EasyTier)
|
||||
updateJson=https://raw.githubusercontent.com/EasyTier/EasyTier/refs/heads/main/easytier-contrib/easytier-magisk/magisk_update.json
|
||||
27
easytier-contrib/easytier-magisk/service.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/data/adb/magisk/busybox sh
|
||||
MODDIR=${0%/*}
|
||||
# MODDIR="$(dirname $(readlink -f "$0"))"
|
||||
chmod 755 ${MODDIR}/*
|
||||
|
||||
# 等待系统启动成功
|
||||
while [ "$(getprop sys.boot_completed)" != "1" ]; do
|
||||
sleep 5s
|
||||
done
|
||||
|
||||
# 防止系统挂起
|
||||
echo "PowerManagerService.noSuspend" > /sys/power/wake_lock
|
||||
|
||||
# 修改模块描述
|
||||
sed -i 's/$(description=)$[^"]*/\1[状态]关闭中/' "$MODDIR/module.prop"
|
||||
|
||||
# 等待 3 秒
|
||||
sleep 3s
|
||||
|
||||
"${MODDIR}/easytier_core.sh" &
|
||||
|
||||
# 检查是否启用模块
|
||||
while [ ! -f ${MODDIR}/disable ]; do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
pkill easytier-core
|
||||
2
easytier-contrib/easytier-magisk/system/etc/resolv.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
nameserver 114.114.114.114
|
||||
nameserver 223.5.5.5
|
||||
3
easytier-contrib/easytier-magisk/uninstall.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
MODDIR=${0%/*}
|
||||
pkill easytier-core # 结束 easytier-core 进程
|
||||
rm -rf $MODDIR/*
|
||||
@@ -1,2 +0,0 @@
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
@@ -18,7 +18,11 @@ cd ../tauri-plugin-vpnservice
|
||||
pnpm install
|
||||
pnpm build
|
||||
|
||||
cd ../easytier-gui
|
||||
cd ../easytier-web/frontend-lib
|
||||
pnpm install
|
||||
pnpm build
|
||||
|
||||
cd ../../easytier-gui
|
||||
pnpm install
|
||||
pnpm tauri build
|
||||
```
|
||||
|
||||
@@ -50,7 +50,11 @@ dev_name_placeholder: 注意:当多个网络同时使用相同的TUN接口名
|
||||
off_text: 点击关闭
|
||||
on_text: 点击开启
|
||||
show_config: 显示配置
|
||||
edit_config: 编辑配置文件
|
||||
close: 关闭
|
||||
save: 保存
|
||||
config_saved: 配置已保存
|
||||
|
||||
|
||||
use_latency_first: 延迟优先模式
|
||||
my_node_info: 当前节点信息
|
||||
@@ -113,3 +117,4 @@ event:
|
||||
VpnPortalClientDisconnected: VPN门户客户端已断开连接
|
||||
DhcpIpv4Changed: DHCP IPv4地址更改
|
||||
DhcpIpv4Conflicted: DHCP IPv4地址冲突
|
||||
PortForwardAdded: 端口转发添加
|
||||
|
||||
@@ -51,7 +51,10 @@ dev_name_placeholder: 'Note: When multiple networks use the same TUN interface n
|
||||
off_text: Press to disable
|
||||
on_text: Press to enable
|
||||
show_config: Show Config
|
||||
edit_config: Edit Config File
|
||||
close: Close
|
||||
save: Save
|
||||
config_saved: Configuration saved
|
||||
my_node_info: My Node Info
|
||||
peer_count: Connected
|
||||
upload: Upload
|
||||
@@ -112,3 +115,4 @@ event:
|
||||
VpnPortalClientDisconnected: VpnPortalClientDisconnected
|
||||
DhcpIpv4Changed: DhcpIpv4Changed
|
||||
DhcpIpv4Conflicted: DhcpIpv4Conflicted
|
||||
PortForwardAdded: PortForwardAdded
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"name": "easytier-gui",
|
||||
"type": "module",
|
||||
"version": "2.0.2",
|
||||
"version": "2.3.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
@@ -12,44 +13,44 @@
|
||||
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@primevue/themes": "^4.1.0",
|
||||
"@tauri-apps/plugin-autostart": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-os": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-process": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "2.0.0-rc.1",
|
||||
"@vueuse/core": "^11.1.0",
|
||||
"@primevue/themes": "4.3.3",
|
||||
"@tauri-apps/plugin-autostart": "2.0.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.0.0",
|
||||
"@tauri-apps/plugin-os": "2.0.0",
|
||||
"@tauri-apps/plugin-process": "2.0.0",
|
||||
"@tauri-apps/plugin-shell": "2.0.1",
|
||||
"@vueuse/core": "^11.2.0",
|
||||
"aura": "link:@primevue\\themes\\aura",
|
||||
"easytier-frontend-lib": "workspace:*",
|
||||
"ip-num": "1.5.1",
|
||||
"pinia": "^2.2.4",
|
||||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.1.0",
|
||||
"tauri-plugin-vpnservice-api": "link:..\\tauri-plugin-vpnservice",
|
||||
"vue": "^3.5.11",
|
||||
"vue-i18n": "^10.0.4",
|
||||
"primevue": "4.3.3",
|
||||
"tauri-plugin-vpnservice-api": "workspace:*",
|
||||
"vue": "^3.5.12",
|
||||
"vue-router": "^4.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^3.7.3",
|
||||
"@intlify/unplugin-vue-i18n": "^5.2.0",
|
||||
"@primevue/auto-import-resolver": "^4.1.0",
|
||||
"@tauri-apps/api": "2.0.0-rc.0",
|
||||
"@tauri-apps/cli": "2.0.0-rc.3",
|
||||
"@primevue/auto-import-resolver": "4.3.3",
|
||||
"@tauri-apps/api": "2.1.0",
|
||||
"@tauri-apps/cli": "2.1.0",
|
||||
"@types/default-gateway": "^7.2.2",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vue-macros/volar": "0.30.3",
|
||||
"@vue-macros/volar": "0.30.5",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cidr-tools": "^11.0.2",
|
||||
"default-gateway": "^7.2.2",
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-plugin-format": "^0.1.2",
|
||||
"internal-ip": "^8.0.0",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"tailwindcss": "=3.4.17",
|
||||
"typescript": "^5.6.2",
|
||||
"unplugin-auto-import": "^0.18.3",
|
||||
"unplugin-vue-components": "^0.27.4",
|
||||
"unplugin-vue-macros": "^2.12.3",
|
||||
"unplugin-vue-macros": "^2.13.3",
|
||||
"unplugin-vue-markdown": "^0.26.2",
|
||||
"unplugin-vue-router": "^0.10.8",
|
||||
"uuid": "^10.0.0",
|
||||
@@ -57,7 +58,6 @@
|
||||
"vite-plugin-vue-devtools": "^7.4.6",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vue-i18n": "^10.0.0",
|
||||
"vue-tsc": "^2.1.6"
|
||||
},
|
||||
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4"
|
||||
}
|
||||
"vue-tsc": "^2.1.10"
|
||||
}
|
||||
}
|
||||
446
easytier-gui/pnpm-lock.yaml
generated
@@ -28,7 +28,7 @@ importers:
|
||||
version: 2.0.0-rc.1
|
||||
'@vueuse/core':
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
version: 11.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
aura:
|
||||
specifier: link:@primevue\themes\aura
|
||||
version: link:@primevue/themes/aura
|
||||
@@ -37,7 +37,7 @@ importers:
|
||||
version: 1.5.1
|
||||
pinia:
|
||||
specifier: ^2.2.4
|
||||
version: 2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3))
|
||||
version: 2.2.4(typescript@5.6.3)(vue@3.4.38(typescript@5.6.3))
|
||||
primeflex:
|
||||
specifier: ^3.3.1
|
||||
version: 3.3.1
|
||||
@@ -46,26 +46,26 @@ importers:
|
||||
version: 7.0.0
|
||||
primevue:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
version: 4.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
tauri-plugin-vpnservice-api:
|
||||
specifier: link:..\tauri-plugin-vpnservice
|
||||
version: link:../tauri-plugin-vpnservice
|
||||
vue:
|
||||
specifier: ^3.5.11
|
||||
version: 3.5.11(typescript@5.6.3)
|
||||
specifier: '=3.4.38'
|
||||
version: 3.4.38(typescript@5.6.3)
|
||||
vue-i18n:
|
||||
specifier: ^10.0.4
|
||||
version: 10.0.4(vue@3.5.11(typescript@5.6.3))
|
||||
version: 10.0.4(vue@3.4.38(typescript@5.6.3))
|
||||
vue-router:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.5(vue@3.5.11(typescript@5.6.3))
|
||||
version: 4.4.5(vue@3.4.38(typescript@5.6.3))
|
||||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^3.7.3
|
||||
version: 3.7.3(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(@vue/compiler-sfc@3.5.11)(eslint-plugin-format@0.1.2(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))
|
||||
'@primevue/auto-import-resolver':
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
@@ -83,10 +83,10 @@ importers:
|
||||
version: 10.0.0
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/volar':
|
||||
specifier: 0.30.3
|
||||
version: 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3))
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.20(postcss@8.4.47)
|
||||
@@ -110,19 +110,19 @@ importers:
|
||||
version: 5.6.3
|
||||
unplugin-auto-import:
|
||||
specifier: ^0.18.3
|
||||
version: 0.18.3(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0)
|
||||
version: 0.18.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)
|
||||
unplugin-vue-components:
|
||||
specifier: ^0.27.4
|
||||
version: 0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
version: 0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin-vue-macros:
|
||||
specifier: ^2.12.3
|
||||
version: 2.12.3(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 2.12.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin-vue-markdown:
|
||||
specifier: ^0.26.2
|
||||
version: 0.26.2(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))
|
||||
unplugin-vue-router:
|
||||
specifier: ^0.10.8
|
||||
version: 0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))
|
||||
uuid:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
@@ -131,10 +131,10 @@ importers:
|
||||
version: 5.4.8(@types/node@22.7.5)
|
||||
vite-plugin-vue-devtools:
|
||||
specifier: ^7.4.6
|
||||
version: 7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))
|
||||
vite-plugin-vue-layouts:
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))
|
||||
version: 0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))
|
||||
vue-tsc:
|
||||
specifier: ^2.1.6
|
||||
version: 2.1.6(typescript@5.6.3)
|
||||
@@ -1380,15 +1380,27 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@vue/compiler-core@3.4.38':
|
||||
resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==}
|
||||
|
||||
'@vue/compiler-core@3.5.11':
|
||||
resolution: {integrity: sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==}
|
||||
|
||||
'@vue/compiler-dom@3.4.38':
|
||||
resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==}
|
||||
|
||||
'@vue/compiler-dom@3.5.11':
|
||||
resolution: {integrity: sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==}
|
||||
|
||||
'@vue/compiler-sfc@3.4.38':
|
||||
resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.11':
|
||||
resolution: {integrity: sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==}
|
||||
|
||||
'@vue/compiler-ssr@3.4.38':
|
||||
resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.11':
|
||||
resolution: {integrity: sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==}
|
||||
|
||||
@@ -1417,20 +1429,37 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity@3.4.38':
|
||||
resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==}
|
||||
|
||||
'@vue/reactivity@3.5.11':
|
||||
resolution: {integrity: sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==}
|
||||
|
||||
'@vue/runtime-core@3.4.38':
|
||||
resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==}
|
||||
|
||||
'@vue/runtime-core@3.5.11':
|
||||
resolution: {integrity: sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==}
|
||||
|
||||
'@vue/runtime-dom@3.4.38':
|
||||
resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==}
|
||||
|
||||
'@vue/runtime-dom@3.5.11':
|
||||
resolution: {integrity: sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==}
|
||||
|
||||
'@vue/server-renderer@3.4.38':
|
||||
resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==}
|
||||
peerDependencies:
|
||||
vue: 3.4.38
|
||||
|
||||
'@vue/server-renderer@3.5.11':
|
||||
resolution: {integrity: sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==}
|
||||
peerDependencies:
|
||||
vue: 3.5.11
|
||||
|
||||
'@vue/shared@3.4.38':
|
||||
resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==}
|
||||
|
||||
'@vue/shared@3.5.11':
|
||||
resolution: {integrity: sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==}
|
||||
|
||||
@@ -3369,6 +3398,14 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue@3.4.38:
|
||||
resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
vue@3.5.11:
|
||||
resolution: {integrity: sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==}
|
||||
peerDependencies:
|
||||
@@ -3932,7 +3969,7 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.3.1': {}
|
||||
|
||||
'@intlify/bundle-utils@9.0.0-beta.0(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))':
|
||||
'@intlify/bundle-utils@9.0.0-beta.0(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 10.0.0
|
||||
'@intlify/shared': 10.0.0
|
||||
@@ -3944,7 +3981,7 @@ snapshots:
|
||||
source-map-js: 1.2.1
|
||||
yaml-eslint-parser: 1.2.3
|
||||
optionalDependencies:
|
||||
vue-i18n: 10.0.4(vue@3.5.11(typescript@5.6.3))
|
||||
vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3))
|
||||
|
||||
'@intlify/core-base@10.0.4':
|
||||
dependencies:
|
||||
@@ -3965,12 +4002,12 @@ snapshots:
|
||||
|
||||
'@intlify/shared@10.0.4': {}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))':
|
||||
'@intlify/unplugin-vue-i18n@5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6))
|
||||
'@intlify/bundle-utils': 9.0.0-beta.0(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))
|
||||
'@intlify/bundle-utils': 9.0.0-beta.0(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))
|
||||
'@intlify/shared': 10.0.0
|
||||
'@intlify/vue-i18n-extensions': 7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))
|
||||
'@intlify/vue-i18n-extensions': 7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.24.0)
|
||||
'@typescript-eslint/scope-manager': 7.18.0
|
||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3)
|
||||
@@ -3982,9 +4019,9 @@ snapshots:
|
||||
picocolors: 1.1.0
|
||||
source-map-js: 1.2.1
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
optionalDependencies:
|
||||
vue-i18n: 10.0.4(vue@3.5.11(typescript@5.6.3))
|
||||
vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/compiler-dom'
|
||||
- eslint
|
||||
@@ -3993,14 +4030,14 @@ snapshots:
|
||||
- typescript
|
||||
- webpack-sources
|
||||
|
||||
'@intlify/vue-i18n-extensions@7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3))':
|
||||
'@intlify/vue-i18n-extensions@7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
optionalDependencies:
|
||||
'@intlify/shared': 10.0.0
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue-i18n: 10.0.4(vue@3.5.11(typescript@5.6.3))
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3))
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
@@ -4071,16 +4108,16 @@ snapshots:
|
||||
dependencies:
|
||||
'@primevue/metadata': 4.1.0
|
||||
|
||||
'@primevue/core@4.1.0(vue@3.5.11(typescript@5.6.3))':
|
||||
'@primevue/core@4.1.0(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.2.0
|
||||
'@primeuix/utils': 0.2.0
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
'@primevue/icons@4.1.0(vue@3.5.11(typescript@5.6.3))':
|
||||
'@primevue/icons@4.1.0(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.2.0
|
||||
'@primevue/core': 4.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
'@primevue/core': 4.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
@@ -4368,10 +4405,10 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.8.1
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
vite: 5.4.8(@types/node@22.7.5)
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
'@vitest/eslint-plugin@1.1.7(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)':
|
||||
dependencies:
|
||||
@@ -4392,42 +4429,55 @@ snapshots:
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.0.8
|
||||
|
||||
'@vue-macros/api@0.11.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/api@0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@babel/types': 7.25.8
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
resolve.exports: 2.0.2
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
'@vue-macros/better-define@1.9.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/better-define@1.9.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/boolean-prop@0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/boolean-prop@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-core': 3.5.11
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
'@vue-macros/chain-call@0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/chain-call@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/common@1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@babel/types': 7.25.8
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.24.0)
|
||||
'@vue/compiler-sfc': 3.5.11
|
||||
ast-kit: 1.2.1
|
||||
local-pkg: 0.5.0
|
||||
magic-string-ast: 0.6.2
|
||||
optionalDependencies:
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
'@vue-macros/common@1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@babel/types': 7.25.8
|
||||
@@ -4441,9 +4491,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
'@vue-macros/config@0.4.2(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/config@0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
make-synchronized: 0.2.9
|
||||
unconfig: 0.5.5
|
||||
transitivePeerDependencies:
|
||||
@@ -4451,71 +4501,71 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@vue-macros/define-emit@0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-emit@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-models@1.3.1(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-models@1.3.1(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
ast-walker-scope: 0.6.2
|
||||
unplugin: 1.14.1
|
||||
optionalDependencies:
|
||||
'@vueuse/core': 11.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
'@vueuse/core': 11.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-prop@0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-prop@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-props-refs@1.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-props-refs@1.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-props@4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-props@4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-render@1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-render@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/define-slots@1.2.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/define-slots@1.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
@@ -4529,37 +4579,37 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@vue-macros/export-expose@0.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/export-expose@0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-sfc': 3.5.11
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/export-props@0.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/export-props@0.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/export-render@0.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/export-render@0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/hoist-static@1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/hoist-static@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
@@ -4576,9 +4626,9 @@ snapshots:
|
||||
- typescript
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/named-template@0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/named-template@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
@@ -4586,31 +4636,31 @@ snapshots:
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-core': 3.5.11
|
||||
'@vue/shared': 3.5.11
|
||||
magic-string: 0.30.12
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/script-lang@0.2.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/script-lang@0.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/setup-block@0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/setup-block@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
@@ -4618,56 +4668,56 @@ snapshots:
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/setup-component@0.18.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/setup-component@0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/setup-sfc@0.18.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/setup-sfc@0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/short-bind@1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/short-bind@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-core': 3.5.11
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
'@vue-macros/short-emits@1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/short-emits@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
'@vue-macros/short-vmodel@1.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/short-vmodel@1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/compiler-core': 3.5.11
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
'@vue-macros/volar@0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue-macros/volar@0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/language-core': 2.1.6(typescript@5.6.3)
|
||||
muggle-string: 0.4.1
|
||||
optionalDependencies:
|
||||
@@ -4708,6 +4758,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vue/compiler-core@3.4.38':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
'@vue/shared': 3.4.38
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-core@3.5.11':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
@@ -4716,11 +4774,28 @@ snapshots:
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.4.38':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
|
||||
'@vue/compiler-dom@3.5.11':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.11
|
||||
'@vue/shared': 3.5.11
|
||||
|
||||
'@vue/compiler-sfc@3.4.38':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
'@vue/compiler-core': 3.4.38
|
||||
'@vue/compiler-dom': 3.4.38
|
||||
'@vue/compiler-ssr': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.12
|
||||
postcss: 8.4.47
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-sfc@3.5.11':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
@@ -4733,6 +4808,11 @@ snapshots:
|
||||
postcss: 8.4.47
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.4.38':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
|
||||
'@vue/compiler-ssr@3.5.11':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
@@ -4745,7 +4825,7 @@ snapshots:
|
||||
|
||||
'@vue/devtools-api@6.6.4': {}
|
||||
|
||||
'@vue/devtools-core@7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vue/devtools-core@7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue/devtools-kit': 7.4.6
|
||||
'@vue/devtools-shared': 7.4.6
|
||||
@@ -4753,7 +4833,7 @@ snapshots:
|
||||
nanoid: 3.3.7
|
||||
pathe: 1.1.2
|
||||
vite-hot-client: 0.2.3(vite@5.4.8(@types/node@22.7.5))
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- vite
|
||||
|
||||
@@ -4784,15 +4864,31 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
'@vue/reactivity@3.4.38':
|
||||
dependencies:
|
||||
'@vue/shared': 3.4.38
|
||||
|
||||
'@vue/reactivity@3.5.11':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.11
|
||||
|
||||
'@vue/runtime-core@3.4.38':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
|
||||
'@vue/runtime-core@3.5.11':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.11
|
||||
'@vue/shared': 3.5.11
|
||||
|
||||
'@vue/runtime-dom@3.4.38':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.4.38
|
||||
'@vue/runtime-core': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/runtime-dom@3.5.11':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.11
|
||||
@@ -4800,29 +4896,37 @@ snapshots:
|
||||
'@vue/shared': 3.5.11
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.4.38
|
||||
'@vue/shared': 3.4.38
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
'@vue/server-renderer@3.5.11(vue@3.5.11(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.11
|
||||
'@vue/shared': 3.5.11
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
|
||||
'@vue/shared@3.4.38': {}
|
||||
|
||||
'@vue/shared@3.5.11': {}
|
||||
|
||||
'@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.20
|
||||
'@vueuse/metadata': 11.1.0
|
||||
'@vueuse/shared': 11.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.3))
|
||||
'@vueuse/shared': 11.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/metadata@11.1.0': {}
|
||||
|
||||
'@vueuse/shared@11.1.0(vue@3.5.11(typescript@5.6.3))':
|
||||
'@vueuse/shared@11.1.0(vue@3.4.38(typescript@5.6.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.3))
|
||||
vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
@@ -6322,11 +6426,11 @@ snapshots:
|
||||
|
||||
pify@2.3.0: {}
|
||||
|
||||
pinia@2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3)):
|
||||
pinia@2.2.4(typescript@5.6.3)(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.3))
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3))
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
@@ -6389,12 +6493,12 @@ snapshots:
|
||||
|
||||
primeicons@7.0.0: {}
|
||||
|
||||
primevue@4.1.0(vue@3.5.11(typescript@5.6.3)):
|
||||
primevue@4.1.0(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.2.0
|
||||
'@primeuix/utils': 0.2.0
|
||||
'@primevue/core': 4.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
'@primevue/icons': 4.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
'@primevue/core': 4.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
'@primevue/icons': 4.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
@@ -6772,7 +6876,7 @@ snapshots:
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unplugin-auto-import@0.18.3(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0):
|
||||
unplugin-auto-import@0.18.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0):
|
||||
dependencies:
|
||||
'@antfu/utils': 0.7.10
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.24.0)
|
||||
@@ -6783,7 +6887,7 @@ snapshots:
|
||||
unimport: 3.13.1(rollup@4.24.0)
|
||||
unplugin: 1.14.1
|
||||
optionalDependencies:
|
||||
'@vueuse/core': 11.1.0(vue@3.5.11(typescript@5.6.3))
|
||||
'@vueuse/core': 11.1.0(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
@@ -6799,7 +6903,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- webpack-sources
|
||||
|
||||
unplugin-vue-components@0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3)):
|
||||
unplugin-vue-components@0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@antfu/utils': 0.7.10
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.24.0)
|
||||
@@ -6811,7 +6915,7 @@ snapshots:
|
||||
minimatch: 9.0.5
|
||||
mlly: 1.7.2
|
||||
unplugin: 1.14.1
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
optionalDependencies:
|
||||
'@babel/parser': 7.25.8
|
||||
transitivePeerDependencies:
|
||||
@@ -6819,9 +6923,9 @@ snapshots:
|
||||
- supports-color
|
||||
- webpack-sources
|
||||
|
||||
unplugin-vue-define-options@1.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3)):
|
||||
unplugin-vue-define-options@1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
ast-walker-scope: 0.6.2
|
||||
unplugin: 1.14.1
|
||||
transitivePeerDependencies:
|
||||
@@ -6829,40 +6933,40 @@ snapshots:
|
||||
- vue
|
||||
- webpack-sources
|
||||
|
||||
unplugin-vue-macros@2.12.3(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.5.11(typescript@5.6.3)):
|
||||
unplugin-vue-macros@2.12.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@vue-macros/better-define': 1.9.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/chain-call': 0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-emit': 0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-models': 1.3.1(@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-prop': 0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-props': 4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3)))(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-props-refs': 1.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-render': 1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/define-slots': 1.2.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/better-define': 1.9.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/chain-call': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-emit': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-models': 1.3.1(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-props': 4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-props-refs': 1.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-render': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/define-slots': 1.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/devtools': 0.4.0(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))
|
||||
'@vue-macros/export-expose': 0.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/export-props': 0.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/export-render': 0.3.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/hoist-static': 1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/export-expose': 0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/export-props': 0.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/export-render': 0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/hoist-static': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/jsx-directive': 0.9.1(rollup@4.24.0)(typescript@5.6.3)
|
||||
'@vue-macros/named-template': 0.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/script-lang': 0.2.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/setup-block': 0.4.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/setup-component': 0.18.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/setup-sfc': 0.18.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/short-emits': 1.6.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/volar': 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/named-template': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/script-lang': 0.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/setup-block': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/setup-component': 0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/setup-sfc': 0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/short-emits': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue-macros/volar': 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3))
|
||||
unplugin: 1.14.1
|
||||
unplugin-combine: 1.0.3(esbuild@0.23.1)(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))
|
||||
unplugin-vue-define-options: 1.5.1(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
unplugin-vue-define-options: 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
transitivePeerDependencies:
|
||||
- '@rspack/core'
|
||||
- '@vueuse/core'
|
||||
@@ -6890,11 +6994,11 @@ snapshots:
|
||||
- rollup
|
||||
- webpack-sources
|
||||
|
||||
unplugin-vue-router@0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3)):
|
||||
unplugin-vue-router@0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@babel/types': 7.25.8
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.24.0)
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))
|
||||
ast-walker-scope: 0.6.2
|
||||
chokidar: 3.6.0
|
||||
fast-glob: 3.3.2
|
||||
@@ -6907,7 +7011,7 @@ snapshots:
|
||||
unplugin: 1.14.1
|
||||
yaml: 2.5.1
|
||||
optionalDependencies:
|
||||
vue-router: 4.4.5(vue@3.5.11(typescript@5.6.3))
|
||||
vue-router: 4.4.5(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
@@ -6957,9 +7061,9 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-devtools@7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3)):
|
||||
vite-plugin-vue-devtools@7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-core': 7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.5.11(typescript@5.6.3))
|
||||
'@vue/devtools-core': 7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/devtools-kit': 7.4.6
|
||||
'@vue/devtools-shared': 7.4.6
|
||||
execa: 8.0.1
|
||||
@@ -6988,13 +7092,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-layouts@0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.5.11(typescript@5.6.3)))(vue@3.5.11(typescript@5.6.3)):
|
||||
vite-plugin-vue-layouts@0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
fast-glob: 3.3.2
|
||||
vite: 5.4.8(@types/node@22.7.5)
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue-router: 4.4.5(vue@3.5.11(typescript@5.6.3))
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
vue-router: 4.4.5(vue@3.4.38(typescript@5.6.3))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -7009,9 +7113,9 @@ snapshots:
|
||||
|
||||
vscode-uri@3.0.8: {}
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.11(typescript@5.6.3)):
|
||||
vue-demi@0.14.10(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
@@ -7026,17 +7130,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-i18n@10.0.4(vue@3.5.11(typescript@5.6.3)):
|
||||
vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 10.0.4
|
||||
'@intlify/shared': 10.0.4
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
vue-router@4.4.5(vue@3.5.11(typescript@5.6.3)):
|
||||
vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.11(typescript@5.6.3)
|
||||
vue: 3.4.38(typescript@5.6.3)
|
||||
|
||||
vue-tsc@2.1.6(typescript@5.6.3):
|
||||
dependencies:
|
||||
@@ -7045,6 +7149,16 @@ snapshots:
|
||||
semver: 7.6.3
|
||||
typescript: 5.6.3
|
||||
|
||||
vue@3.4.38(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.38
|
||||
'@vue/compiler-sfc': 3.4.38
|
||||
'@vue/runtime-dom': 3.4.38
|
||||
'@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.6.3))
|
||||
'@vue/shared': 3.4.38
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
vue@3.5.11(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.11
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "easytier-gui"
|
||||
version = "2.0.2"
|
||||
version = "2.3.2"
|
||||
description = "EasyTier GUI"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
@@ -14,11 +14,20 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0-rc", features = [] }
|
||||
|
||||
# enable thunk-rs when compiling for x86_64 or i686 windows
|
||||
[target.x86_64-pc-windows-msvc.build-dependencies]
|
||||
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||
|
||||
[target.i686-pc-windows-msvc.build-dependencies]
|
||||
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2.0.0-rc", features = [
|
||||
# wry 0.47 may crash on android, see https://github.com/EasyTier/EasyTier/issues/527
|
||||
tauri = { version = "=2.0.6", features = [
|
||||
"tray-icon",
|
||||
"image-png",
|
||||
"image-ico",
|
||||
"devtools",
|
||||
] }
|
||||
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -37,13 +46,14 @@ gethostname = "0.5"
|
||||
|
||||
dunce = "1.0.4"
|
||||
|
||||
tauri-plugin-shell = "2.0.0-rc"
|
||||
tauri-plugin-process = "2.0.0-rc"
|
||||
tauri-plugin-clipboard-manager = "2.0.0-rc"
|
||||
tauri-plugin-positioner = { version = "2.0.0-rc", features = ["tray-icon"] }
|
||||
tauri-plugin-shell = "2.0"
|
||||
tauri-plugin-process = "2.0"
|
||||
tauri-plugin-clipboard-manager = "2.0"
|
||||
tauri-plugin-positioner = { version = "2.0", features = ["tray-icon"] }
|
||||
tauri-plugin-vpnservice = { path = "../../tauri-plugin-vpnservice" }
|
||||
tauri-plugin-os = "2.0.0-rc"
|
||||
tauri-plugin-autostart = "2.0.0-rc"
|
||||
tauri-plugin-os = "2.0"
|
||||
tauri-plugin-autostart = "2.0"
|
||||
uuid = "1.17.0"
|
||||
|
||||
|
||||
[features]
|
||||
@@ -51,4 +61,4 @@ tauri-plugin-autostart = "2.0.0-rc"
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-single-instance = "2.0.0-rc.0"
|
||||
tauri-plugin-single-instance = "2.2.3"
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
fn main() {
|
||||
// enable thunk-rs when target os is windows and arch is x86_64 or i686
|
||||
#[cfg(target_os = "windows")]
|
||||
if !std::env::var("TARGET")
|
||||
.unwrap_or_default()
|
||||
.contains("aarch64")
|
||||
{
|
||||
thunk::thunk();
|
||||
}
|
||||
|
||||
tauri_build::build();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"vpnservice:allow-prepare-vpn",
|
||||
"vpnservice:allow-start-vpn",
|
||||
"vpnservice:allow-stop-vpn",
|
||||
"vpnservice:allow-register-listener",
|
||||
"vpnservice:allow-registerListener",
|
||||
"os:default",
|
||||
"os:allow-os-type",
|
||||
"os:allow-arch",
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
@@ -3,183 +3,22 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use dashmap::DashMap;
|
||||
use easytier::{
|
||||
common::config::{
|
||||
ConfigLoader, FileLoggerConfig, Flags, NetworkIdentity, PeerConfig, TomlConfigLoader,
|
||||
VpnPortalConfig,
|
||||
},
|
||||
launcher::{NetworkInstance, NetworkInstanceRunningInfo},
|
||||
common::config::{ConfigLoader, FileLoggerConfig, LoggingConfigBuilder, TomlConfigLoader},
|
||||
instance_manager::NetworkInstanceManager,
|
||||
launcher::{ConfigSource, NetworkConfig, NetworkInstanceRunningInfo},
|
||||
utils::{self, NewFilterSender},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use tauri::Manager as _;
|
||||
|
||||
pub const AUTOSTART_ARG: &str = "--autostart";
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
enum NetworkingMethod {
|
||||
PublicServer,
|
||||
Manual,
|
||||
Standalone,
|
||||
}
|
||||
|
||||
impl Default for NetworkingMethod {
|
||||
fn default() -> Self {
|
||||
NetworkingMethod::PublicServer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
struct NetworkConfig {
|
||||
instance_id: String,
|
||||
|
||||
dhcp: bool,
|
||||
virtual_ipv4: String,
|
||||
network_length: i32,
|
||||
hostname: Option<String>,
|
||||
network_name: String,
|
||||
network_secret: String,
|
||||
networking_method: NetworkingMethod,
|
||||
|
||||
public_server_url: String,
|
||||
peer_urls: Vec<String>,
|
||||
|
||||
proxy_cidrs: Vec<String>,
|
||||
|
||||
enable_vpn_portal: bool,
|
||||
vpn_portal_listen_port: i32,
|
||||
vpn_portal_client_network_addr: String,
|
||||
vpn_portal_client_network_len: i32,
|
||||
|
||||
advanced_settings: bool,
|
||||
|
||||
listener_urls: Vec<String>,
|
||||
rpc_port: i32,
|
||||
latency_first: bool,
|
||||
|
||||
dev_name: String,
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
fn gen_config(&self) -> Result<TomlConfigLoader, anyhow::Error> {
|
||||
let cfg = TomlConfigLoader::default();
|
||||
cfg.set_id(
|
||||
self.instance_id
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse instance id: {}", self.instance_id))?,
|
||||
);
|
||||
cfg.set_hostname(self.hostname.clone());
|
||||
cfg.set_dhcp(self.dhcp);
|
||||
cfg.set_inst_name(self.network_name.clone());
|
||||
cfg.set_network_identity(NetworkIdentity::new(
|
||||
self.network_name.clone(),
|
||||
self.network_secret.clone(),
|
||||
));
|
||||
|
||||
if !self.dhcp {
|
||||
if self.virtual_ipv4.len() > 0 {
|
||||
let ip = format!("{}/{}", self.virtual_ipv4, self.network_length)
|
||||
.parse()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to parse ipv4 inet address: {}, {}",
|
||||
self.virtual_ipv4, self.network_length
|
||||
)
|
||||
})?;
|
||||
cfg.set_ipv4(Some(ip));
|
||||
}
|
||||
}
|
||||
|
||||
match self.networking_method {
|
||||
NetworkingMethod::PublicServer => {
|
||||
cfg.set_peers(vec![PeerConfig {
|
||||
uri: self.public_server_url.parse().with_context(|| {
|
||||
format!(
|
||||
"failed to parse public server uri: {}",
|
||||
self.public_server_url
|
||||
)
|
||||
})?,
|
||||
}]);
|
||||
}
|
||||
NetworkingMethod::Manual => {
|
||||
let mut peers = vec![];
|
||||
for peer_url in self.peer_urls.iter() {
|
||||
if peer_url.is_empty() {
|
||||
continue;
|
||||
}
|
||||
peers.push(PeerConfig {
|
||||
uri: peer_url
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse peer uri: {}", peer_url))?,
|
||||
});
|
||||
}
|
||||
|
||||
cfg.set_peers(peers);
|
||||
}
|
||||
NetworkingMethod::Standalone => {}
|
||||
}
|
||||
|
||||
let mut listener_urls = vec![];
|
||||
for listener_url in self.listener_urls.iter() {
|
||||
if listener_url.is_empty() {
|
||||
continue;
|
||||
}
|
||||
listener_urls.push(
|
||||
listener_url
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse listener uri: {}", listener_url))?,
|
||||
);
|
||||
}
|
||||
cfg.set_listeners(listener_urls);
|
||||
|
||||
for n in self.proxy_cidrs.iter() {
|
||||
cfg.add_proxy_cidr(
|
||||
n.parse()
|
||||
.with_context(|| format!("failed to parse proxy network: {}", n))?,
|
||||
);
|
||||
}
|
||||
|
||||
cfg.set_rpc_portal(
|
||||
format!("0.0.0.0:{}", self.rpc_port)
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse rpc portal port: {}", self.rpc_port))?,
|
||||
);
|
||||
|
||||
if self.enable_vpn_portal {
|
||||
let cidr = format!(
|
||||
"{}/{}",
|
||||
self.vpn_portal_client_network_addr, self.vpn_portal_client_network_len
|
||||
);
|
||||
cfg.set_vpn_portal_config(VpnPortalConfig {
|
||||
client_cidr: cidr
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse vpn portal client cidr: {}", cidr))?,
|
||||
wireguard_listen: format!("0.0.0.0:{}", self.vpn_portal_listen_port)
|
||||
.parse()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to parse vpn portal wireguard listen port. {}",
|
||||
self.vpn_portal_listen_port
|
||||
)
|
||||
})?,
|
||||
});
|
||||
}
|
||||
let mut flags = Flags::default();
|
||||
flags.latency_first = self.latency_first;
|
||||
flags.dev_name = self.dev_name.clone();
|
||||
cfg.set_flags(flags);
|
||||
Ok(cfg)
|
||||
}
|
||||
}
|
||||
|
||||
static INSTANCE_MAP: once_cell::sync::Lazy<DashMap<String, NetworkInstance>> =
|
||||
once_cell::sync::Lazy::new(DashMap::new);
|
||||
static INSTANCE_MANAGER: once_cell::sync::Lazy<NetworkInstanceManager> =
|
||||
once_cell::sync::Lazy::new(NetworkInstanceManager::new);
|
||||
|
||||
static mut LOGGER_LEVEL_SENDER: once_cell::sync::Lazy<Option<NewFilterSender>> =
|
||||
once_cell::sync::Lazy::new(Default::default);
|
||||
@@ -203,43 +42,48 @@ fn parse_network_config(cfg: NetworkConfig) -> Result<String, String> {
|
||||
Ok(toml.dump())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn generate_network_config(toml_config: String) -> Result<NetworkConfig, String> {
|
||||
let config = TomlConfigLoader::new_from_str(&toml_config).map_err(|e| e.to_string())?;
|
||||
let cfg = NetworkConfig::new_from_config(&config).map_err(|e| e.to_string())?;
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn run_network_instance(cfg: NetworkConfig) -> Result<(), String> {
|
||||
if INSTANCE_MAP.contains_key(&cfg.instance_id) {
|
||||
return Err("instance already exists".to_string());
|
||||
}
|
||||
let instance_id = cfg.instance_id.clone();
|
||||
|
||||
let instance_id = cfg.instance_id().to_string();
|
||||
let cfg = cfg.gen_config().map_err(|e| e.to_string())?;
|
||||
let mut instance = NetworkInstance::new(cfg);
|
||||
instance.start().map_err(|e| e.to_string())?;
|
||||
|
||||
INSTANCE_MANAGER
|
||||
.run_network_instance(cfg, ConfigSource::GUI)
|
||||
.map_err(|e| e.to_string())?;
|
||||
println!("instance {} started", instance_id);
|
||||
INSTANCE_MAP.insert(instance_id, instance);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn retain_network_instance(instance_ids: Vec<String>) -> Result<(), String> {
|
||||
let _ = INSTANCE_MAP.retain(|k, _| instance_ids.contains(k));
|
||||
println!(
|
||||
"instance {:?} retained",
|
||||
INSTANCE_MAP
|
||||
.iter()
|
||||
.map(|item| item.key().clone())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
let instance_ids = instance_ids
|
||||
.into_iter()
|
||||
.filter_map(|id| uuid::Uuid::parse_str(&id).ok())
|
||||
.collect();
|
||||
let retained = INSTANCE_MANAGER
|
||||
.retain_network_instance(instance_ids)
|
||||
.map_err(|e| e.to_string())?;
|
||||
println!("instance {:?} retained", retained);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn collect_network_infos() -> Result<BTreeMap<String, NetworkInstanceRunningInfo>, String> {
|
||||
let infos = INSTANCE_MANAGER
|
||||
.collect_network_infos()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut ret = BTreeMap::new();
|
||||
for instance in INSTANCE_MAP.iter() {
|
||||
if let Some(info) = instance.get_running_info() {
|
||||
ret.insert(instance.key().clone(), info);
|
||||
}
|
||||
for (uuid, info) in infos {
|
||||
ret.insert(uuid.to_string(), info);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
@@ -250,6 +94,7 @@ fn get_os_hostname() -> Result<String, String> {
|
||||
|
||||
#[tauri::command]
|
||||
fn set_logging_level(level: String) -> Result<(), String> {
|
||||
#[allow(static_mut_refs)]
|
||||
let sender = unsafe { LOGGER_LEVEL_SENDER.as_ref().unwrap() };
|
||||
sender.send(level).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
@@ -257,10 +102,10 @@ fn set_logging_level(level: String) -> Result<(), String> {
|
||||
|
||||
#[tauri::command]
|
||||
fn set_tun_fd(instance_id: String, fd: i32) -> Result<(), String> {
|
||||
let mut instance = INSTANCE_MAP
|
||||
.get_mut(&instance_id)
|
||||
.ok_or("instance not found")?;
|
||||
instance.set_tun_fd(fd);
|
||||
let uuid = uuid::Uuid::parse_str(&instance_id).map_err(|e| e.to_string())?;
|
||||
INSTANCE_MANAGER
|
||||
.set_tun_fd(&uuid, fd)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -268,7 +113,12 @@ fn set_tun_fd(instance_id: String, fd: i32) -> Result<(), String> {
|
||||
fn toggle_window_visibility<R: tauri::Runtime>(app: &tauri::AppHandle<R>) {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if window.is_visible().unwrap_or_default() {
|
||||
let _ = window.hide();
|
||||
if window.is_minimized().unwrap_or_default() {
|
||||
let _ = window.unminimize();
|
||||
let _ = window.set_focus();
|
||||
} else {
|
||||
let _ = window.hide();
|
||||
}
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
@@ -302,7 +152,6 @@ pub fn run() {
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
utils::setup_panic_handler();
|
||||
|
||||
let mut builder = tauri::Builder::default();
|
||||
@@ -335,22 +184,27 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_vpnservice::init());
|
||||
|
||||
builder
|
||||
let app = builder
|
||||
.setup(|app| {
|
||||
// for logging config
|
||||
let Ok(log_dir) = app.path().app_log_dir() else {
|
||||
return Ok(());
|
||||
};
|
||||
let config = TomlConfigLoader::default();
|
||||
config.set_file_logger_config(FileLoggerConfig {
|
||||
dir: Some(log_dir.to_string_lossy().to_string()),
|
||||
level: None,
|
||||
file: None,
|
||||
});
|
||||
let Ok(Some(logger_reinit)) = utils::init_logger(config, true) else {
|
||||
let config = LoggingConfigBuilder::default()
|
||||
.file_logger(FileLoggerConfig {
|
||||
dir: Some(log_dir.to_string_lossy().to_string()),
|
||||
level: None,
|
||||
file: None,
|
||||
})
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let Ok(Some(logger_reinit)) = utils::init_logger(&config, true) else {
|
||||
return Ok(());
|
||||
};
|
||||
unsafe { LOGGER_LEVEL_SENDER.replace(logger_reinit) };
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
LOGGER_LEVEL_SENDER.replace(logger_reinit)
|
||||
};
|
||||
|
||||
// for tray icon, menu need to be built in js
|
||||
#[cfg(not(target_os = "android"))]
|
||||
@@ -377,6 +231,7 @@ pub fn run() {
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
parse_network_config,
|
||||
generate_network_config,
|
||||
run_network_instance,
|
||||
retain_network_instance,
|
||||
collect_network_infos,
|
||||
@@ -394,6 +249,20 @@ pub fn run() {
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
.build(tauri::generate_context!())
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
app.run(|_app, _event| {});
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use tauri::RunEvent;
|
||||
app.run(|app, event| match event {
|
||||
RunEvent::Reopen { .. } => {
|
||||
toggle_window_visibility(app);
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"createUpdaterArtifacts": false
|
||||
},
|
||||
"productName": "easytier-gui",
|
||||
"version": "2.0.2",
|
||||
"version": "2.3.2",
|
||||
"identifier": "com.kkrainbow.easytier",
|
||||
"plugins": {},
|
||||
"app": {
|
||||
|
||||
@@ -8,5 +8,6 @@ onBeforeMount(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Toast position="bottom-right" />
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
4
easytier-gui/src/auto-imports.d.ts
vendored
@@ -23,6 +23,7 @@ declare global {
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const event2human: typeof import('./composables/utils')['event2human']
|
||||
const generateMenuItem: typeof import('./composables/tray')['generateMenuItem']
|
||||
const generateNetworkConfig: typeof import('./composables/network')['generateNetworkConfig']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
@@ -134,6 +135,7 @@ declare module 'vue' {
|
||||
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly generateMenuItem: UnwrapRef<typeof import('./composables/tray')['generateMenuItem']>
|
||||
readonly generateNetworkConfig: UnwrapRef<typeof import('./composables/network')['generateNetworkConfig']>
|
||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
@@ -154,8 +156,6 @@ declare module 'vue' {
|
||||
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly num2ipv4: UnwrapRef<typeof import('./composables/utils')['num2ipv4']>
|
||||
readonly num2ipv6: UnwrapRef<typeof import('./composables/utils')['num2ipv6']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import InputGroup from 'primevue/inputgroup'
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||
import { getOsHostname } from '~/composables/network'
|
||||
|
||||
import { NetworkingMethod } from '~/types/network'
|
||||
|
||||
const props = defineProps<{
|
||||
configInvalid?: boolean
|
||||
instanceId?: string
|
||||
}>()
|
||||
|
||||
defineEmits(['runNetwork'])
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const networking_methods = ref([
|
||||
{ value: NetworkingMethod.PublicServer, label: () => t('public_server') },
|
||||
{ value: NetworkingMethod.Manual, label: () => t('manual') },
|
||||
{ value: NetworkingMethod.Standalone, label: () => t('standalone') },
|
||||
])
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
const curNetwork = computed(() => {
|
||||
if (props.instanceId) {
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === props.instanceId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
const protos: { [proto: string]: number } = { tcp: 11010, udp: 11010, wg: 11011, ws: 11011, wss: 11012 }
|
||||
|
||||
function searchUrlSuggestions(e: { query: string }): string[] {
|
||||
const query = e.query
|
||||
const ret = []
|
||||
// if query match "^\w+:.*", then no proto prefix
|
||||
if (query.match(/^\w+:.*/)) {
|
||||
// if query is a valid url, then add to suggestions
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(query)
|
||||
ret.push(query)
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
else {
|
||||
for (const proto in protos) {
|
||||
let item = `${proto}://${query}`
|
||||
// if query match ":\d+$", then no port suffix
|
||||
if (!query.match(/:\d+$/)) {
|
||||
item += `:${protos[proto]}`
|
||||
}
|
||||
ret.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
const publicServerSuggestions = ref([''])
|
||||
|
||||
function searchPresetPublicServers(e: { query: string }) {
|
||||
const presetPublicServers = [
|
||||
'tcp://public.easytier.top:11010',
|
||||
]
|
||||
|
||||
const query = e.query
|
||||
// if query is sub string of presetPublicServers, add to suggestions
|
||||
let ret = presetPublicServers.filter(item => item.includes(query))
|
||||
// add additional suggestions
|
||||
if (query.length > 0) {
|
||||
ret = ret.concat(searchUrlSuggestions(e))
|
||||
}
|
||||
|
||||
publicServerSuggestions.value = ret
|
||||
}
|
||||
|
||||
const peerSuggestions = ref([''])
|
||||
|
||||
function searchPeerSuggestions(e: { query: string }) {
|
||||
peerSuggestions.value = searchUrlSuggestions(e)
|
||||
}
|
||||
|
||||
const inetSuggestions = ref([''])
|
||||
|
||||
function searchInetSuggestions(e: { query: string }) {
|
||||
if (e.query.search('/') >= 0) {
|
||||
inetSuggestions.value = [e.query]
|
||||
} else {
|
||||
const ret = []
|
||||
for (let i = 0; i < 32; i++) {
|
||||
ret.push(`${e.query}/${i}`)
|
||||
}
|
||||
inetSuggestions.value = ret
|
||||
}
|
||||
}
|
||||
|
||||
const listenerSuggestions = ref([''])
|
||||
|
||||
function searchListenerSuggestiong(e: { query: string }) {
|
||||
const ret = []
|
||||
|
||||
for (const proto in protos) {
|
||||
let item = `${proto}://0.0.0.0:`
|
||||
// if query is a number, use it as port
|
||||
if (e.query.match(/^\d+$/)) {
|
||||
item += e.query
|
||||
}
|
||||
else {
|
||||
item += protos[proto]
|
||||
}
|
||||
|
||||
if (item.includes(e.query)) {
|
||||
ret.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.length === 0) {
|
||||
ret.push(e.query)
|
||||
}
|
||||
|
||||
listenerSuggestions.value = ret
|
||||
}
|
||||
|
||||
function validateHostname() {
|
||||
if (curNetwork.value.hostname) {
|
||||
// eslint no-useless-escape
|
||||
let name = curNetwork.value.hostname!.replaceAll(/[^\u4E00-\u9FA5a-z0-9\-]*/gi, '')
|
||||
if (name.length > 32)
|
||||
name = name.substring(0, 32)
|
||||
|
||||
if (curNetwork.value.hostname !== name)
|
||||
curNetwork.value.hostname = name
|
||||
}
|
||||
}
|
||||
|
||||
const osHostname = ref<string>('')
|
||||
|
||||
onMounted(async () => {
|
||||
osHostname.value = await getOsHostname()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column h-full">
|
||||
<div class="flex flex-column">
|
||||
<div class="w-10/12 self-center ">
|
||||
<Panel :header="t('basic_settings')">
|
||||
<div class="flex flex-column gap-y-2">
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<div class="flex align-items-center" for="virtual_ip">
|
||||
<label class="mr-2"> {{ t('virtual_ipv4') }} </label>
|
||||
<Checkbox v-model="curNetwork.dhcp" input-id="virtual_ip_auto" :binary="true" />
|
||||
|
||||
<label for="virtual_ip_auto" class="ml-2">
|
||||
{{ t('virtual_ipv4_dhcp') }}
|
||||
</label>
|
||||
</div>
|
||||
<InputGroup>
|
||||
<InputText
|
||||
id="virtual_ip" v-model="curNetwork.virtual_ipv4" :disabled="curNetwork.dhcp"
|
||||
aria-describedby="virtual_ipv4-help"
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<span>/</span>
|
||||
</InputGroupAddon>
|
||||
<InputNumber v-model="curNetwork.network_length" :disabled="curNetwork.dhcp" inputId="horizontal-buttons" showButtons :step="1" mode="decimal" :min="1" :max="32" fluid class="max-w-20"/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="network_name">{{ t('network_name') }}</label>
|
||||
<InputText id="network_name" v-model="curNetwork.network_name" aria-describedby="network_name-help" />
|
||||
</div>
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="network_secret">{{ t('network_secret') }}</label>
|
||||
<InputText
|
||||
id="network_secret" v-model="curNetwork.network_secret"
|
||||
aria-describedby="network_secret-help"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="nm">{{ t('networking_method') }}</label>
|
||||
<SelectButton v-model="curNetwork.networking_method" :options="networking_methods" :option-label="(v) => v.label()" option-value="value" />
|
||||
<div class="items-center flex flex-row p-fluid gap-x-1">
|
||||
<AutoComplete
|
||||
v-if="curNetwork.networking_method === NetworkingMethod.Manual" id="chips"
|
||||
v-model="curNetwork.peer_urls" :placeholder="t('chips_placeholder', ['tcp://8.8.8.8:11010'])"
|
||||
class="grow" multiple fluid :suggestions="peerSuggestions" @complete="searchPeerSuggestions"
|
||||
/>
|
||||
|
||||
<AutoComplete
|
||||
v-if="curNetwork.networking_method === NetworkingMethod.PublicServer" v-model="curNetwork.public_server_url"
|
||||
:suggestions="publicServerSuggestions" :virtual-scroller-options="{ itemSize: 38 }" class="grow" dropdown :complete-on-focus="true"
|
||||
@complete="searchPresetPublicServers"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Panel :header="t('advanced_settings')" toggleable collapsed>
|
||||
<div class="flex flex-column gap-y-2">
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<div class="flex align-items-center">
|
||||
<Checkbox v-model="curNetwork.latency_first" input-id="use_latency_first" :binary="true" />
|
||||
<label for="use_latency_first" class="ml-2"> {{ t('use_latency_first') }} </label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="hostname">{{ t('hostname') }}</label>
|
||||
<InputText
|
||||
id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
||||
:placeholder="t('hostname_placeholder', [osHostname])" @blur="validateHostname"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-column gap-2 grow p-fluid">
|
||||
<label for="username">{{ t('proxy_cidrs') }}</label>
|
||||
<AutoComplete
|
||||
id="subnet-proxy"
|
||||
v-model="curNetwork.proxy_cidrs" :placeholder="t('chips_placeholder', ['10.0.0.0/24'])"
|
||||
class="w-full" multiple fluid :suggestions="inetSuggestions" @complete="searchInetSuggestions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap ">
|
||||
<div class="flex flex-column gap-2 grow">
|
||||
<label for="username">VPN Portal</label>
|
||||
<ToggleButton
|
||||
v-model="curNetwork.enable_vpn_portal" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48"
|
||||
/>
|
||||
<div v-if="curNetwork.enable_vpn_portal" class="items-center flex flex-row gap-x-4">
|
||||
<div class="min-w-64">
|
||||
<InputGroup>
|
||||
<InputText
|
||||
v-model="curNetwork.vpn_portal_client_network_addr"
|
||||
:placeholder="t('vpn_portal_client_network')"
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<span>/{{ curNetwork.vpn_portal_client_network_len }}</span>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
|
||||
<InputNumber
|
||||
v-model="curNetwork.vpn_portal_listen_port" :allow-empty="false"
|
||||
:format="false" :min="0" :max="65535" class="w-8" fluid
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 grow p-fluid">
|
||||
<label for="listener_urls">{{ t('listener_urls') }}</label>
|
||||
<AutoComplete
|
||||
id="listener_urls" v-model="curNetwork.listener_urls"
|
||||
:suggestions="listenerSuggestions" class="w-full" dropdown :complete-on-focus="true"
|
||||
:placeholder="t('chips_placeholder', ['tcp://1.1.1.1:11010'])"
|
||||
multiple @complete="searchListenerSuggestiong"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="rpc_port">{{ t('rpc_port') }}</label>
|
||||
<InputNumber
|
||||
id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="rpc_port-help"
|
||||
:format="false" :min="0" :max="65535"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="dev_name">{{ t('dev_name') }}</label>
|
||||
<InputText
|
||||
id="dev_name" v-model="curNetwork.dev_name" aria-describedby="dev_name-help" :format="true"
|
||||
:placeholder="t('dev_name_placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<div class="flex pt-4 justify-content-center">
|
||||
<Button
|
||||
:label="t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
||||
@click="$emit('runNetwork', curNetwork)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { NetworkTypes } from 'easytier-frontend-lib'
|
||||
import { addPluginListener } from '@tauri-apps/api/core'
|
||||
import { Utils } from 'easytier-frontend-lib'
|
||||
import { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api'
|
||||
import type { Route } from '~/types/network'
|
||||
|
||||
type Route = NetworkTypes.Route
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
@@ -46,9 +49,9 @@ async function doStartVpn(ipv4Addr: string, cidr: number, routes: string[]) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('start vpn')
|
||||
console.log('start vpn service', ipv4Addr, cidr, routes)
|
||||
const start_ret = await start_vpn({
|
||||
ipv4Addr: `${ipv4Addr}`,
|
||||
ipv4Addr: `${ipv4Addr}/${cidr}`,
|
||||
routes,
|
||||
disallowedApplications: ['com.kkrainbow.easytier'],
|
||||
mtu: 1300,
|
||||
@@ -110,6 +113,7 @@ function getRoutesForVpn(routes: Route[]): string[] {
|
||||
}
|
||||
|
||||
async function onNetworkInstanceChange() {
|
||||
console.error('vpn service watch network instance change ids', JSON.stringify(networkStore.networkInstanceIds))
|
||||
const insts = networkStore.networkInstanceIds
|
||||
if (!insts) {
|
||||
await doStopVpn()
|
||||
@@ -122,19 +126,32 @@ async function onNetworkInstanceChange() {
|
||||
return
|
||||
}
|
||||
|
||||
const virtual_ip = curNetworkInfo?.node_info?.virtual_ipv4
|
||||
const virtual_ip = Utils.ipv4ToString(curNetworkInfo?.my_node_info?.virtual_ipv4.address)
|
||||
if (!virtual_ip || !virtual_ip.length) {
|
||||
await doStopVpn()
|
||||
return
|
||||
}
|
||||
|
||||
// if use no tun mode, stop the vpn service
|
||||
const no_tun = networkStore.isNoTunEnabled(insts[0])
|
||||
if (no_tun) {
|
||||
console.error('no tun mode, stop vpn service')
|
||||
await doStopVpn()
|
||||
return
|
||||
}
|
||||
|
||||
let network_length = curNetworkInfo?.my_node_info?.virtual_ipv4.network_length
|
||||
if (!network_length) {
|
||||
network_length = 24
|
||||
}
|
||||
|
||||
const routes = getRoutesForVpn(curNetworkInfo?.routes)
|
||||
|
||||
const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr
|
||||
const routesChanged = JSON.stringify(routes) !== JSON.stringify(curVpnStatus.routes)
|
||||
|
||||
if (ipChanged || routesChanged) {
|
||||
console.log('virtual ip changed', JSON.stringify(curVpnStatus), virtual_ip)
|
||||
console.info('vpn service virtual ip changed', JSON.stringify(curVpnStatus), virtual_ip)
|
||||
try {
|
||||
await doStopVpn()
|
||||
}
|
||||
@@ -146,7 +163,7 @@ async function onNetworkInstanceChange() {
|
||||
await doStartVpn(virtual_ip, 24, routes)
|
||||
}
|
||||
catch (e) {
|
||||
console.error('start vpn failed, clear all network insts.', e)
|
||||
console.error('start vpn service failed, clear all network insts.', e)
|
||||
networkStore.clearNetworkInstances()
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
}
|
||||
@@ -167,6 +184,7 @@ async function watchNetworkInstance() {
|
||||
}
|
||||
subscribe_running = false
|
||||
})
|
||||
console.error('vpn service watch network instance')
|
||||
}
|
||||
|
||||
export async function initMobileVpnService() {
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import type { NetworkTypes } from 'easytier-frontend-lib'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
import type { NetworkConfig, NetworkInstanceRunningInfo } from '~/types/network'
|
||||
type NetworkConfig = NetworkTypes.NetworkConfig
|
||||
type NetworkInstanceRunningInfo = NetworkTypes.NetworkInstanceRunningInfo
|
||||
|
||||
export async function parseNetworkConfig(cfg: NetworkConfig) {
|
||||
return invoke<string>('parse_network_config', { cfg })
|
||||
}
|
||||
|
||||
export async function generateNetworkConfig(tomlConfig: string) {
|
||||
return invoke<NetworkConfig>('generate_network_config', { tomlConfig })
|
||||
}
|
||||
|
||||
export async function runNetworkInstance(cfg: NetworkConfig) {
|
||||
return invoke('run_network_instance', { cfg })
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { IPv4, IPv6 } from 'ip-num/IPNumber'
|
||||
import type { Ipv4Addr, Ipv6Addr } from '~/types/network'
|
||||
|
||||
export function num2ipv4(ip: Ipv4Addr) {
|
||||
return IPv4.fromNumber(ip.addr)
|
||||
}
|
||||
|
||||
export function num2ipv6(ip: Ipv6Addr) {
|
||||
return IPv6.fromBigInt(
|
||||
(BigInt(ip.part1) << BigInt(96))
|
||||
+ (BigInt(ip.part2) << BigInt(64))
|
||||
+ (BigInt(ip.part3) << BigInt(32))
|
||||
+ BigInt(ip.part4),
|
||||
)
|
||||
}
|
||||
@@ -5,12 +5,11 @@ import ToastService from 'primevue/toastservice'
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto'
|
||||
import { routes } from 'vue-router/auto-routes'
|
||||
import App from '~/App.vue'
|
||||
import { i18n, loadLanguageAsync } from '~/modules/i18n'
|
||||
import EasyTierFrontendLib, { I18nUtils } from 'easytier-frontend-lib'
|
||||
|
||||
import { getAutoLaunchStatusAsync, loadAutoLaunchStatusAsync } from './modules/auto_launch'
|
||||
import '~/styles.css'
|
||||
import 'primeicons/primeicons.css'
|
||||
import 'primeflex/primeflex.css'
|
||||
import 'easytier-frontend-lib/style.css'
|
||||
|
||||
if (import.meta.env.PROD) {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
@@ -29,7 +28,7 @@ if (import.meta.env.PROD) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await loadLanguageAsync(localStorage.getItem('lang') || 'en')
|
||||
await I18nUtils.loadLanguageAsync(localStorage.getItem('lang') || 'en')
|
||||
await loadAutoLaunchStatusAsync(getAutoLaunchStatusAsync())
|
||||
|
||||
const app = createApp(App)
|
||||
@@ -41,18 +40,22 @@ async function main() {
|
||||
|
||||
app.use(router)
|
||||
app.use(createPinia())
|
||||
app.use(i18n, { useScope: 'global' })
|
||||
app.use(EasyTierFrontendLib)
|
||||
// app.use(i18n, { useScope: 'global' })
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Aura,
|
||||
options: {
|
||||
prefix: 'p',
|
||||
darkModeSelector: 'system',
|
||||
cssLayer: false,
|
||||
cssLayer: {
|
||||
name: 'primevue',
|
||||
order: 'tailwind-base, primevue, tailwind-utilities',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
app.use(ToastService)
|
||||
app.use(ToastService as any)
|
||||
app.mount('#app')
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,11 @@ import { exit } from '@tauri-apps/plugin-process'
|
||||
import { open } from '@tauri-apps/plugin-shell'
|
||||
import TieredMenu from 'primevue/tieredmenu'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import Config from '~/components/Config.vue'
|
||||
import { NetworkTypes, Config, Status, Utils, I18nUtils, ConfigEditDialog } from 'easytier-frontend-lib'
|
||||
|
||||
import Status from '~/components/Status.vue'
|
||||
import { isAutostart, setLoggingLevel } from '~/composables/network'
|
||||
import { useTray } from '~/composables/tray'
|
||||
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
||||
import { loadLanguageAsync } from '~/modules/i18n'
|
||||
import { type NetworkConfig, NetworkingMethod } from '~/types/network'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const visible = ref(false)
|
||||
@@ -26,7 +23,7 @@ useTray(true)
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: () => t('show_config'),
|
||||
label: () => activeStep.value == "2" ? t('show_config') : t('edit_config'),
|
||||
icon: 'pi pi-file-edit',
|
||||
command: async () => {
|
||||
try {
|
||||
@@ -65,6 +62,27 @@ const toast = useToast()
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const curNetworkConfig = computed(() => {
|
||||
if (networkStore.curNetworkId) {
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === networkStore.curNetworkId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
const curNetworkInst = computed<NetworkTypes.NetworkInstance | null>(() => {
|
||||
let ret = networkStore.networkInstances.find(n => n.instance_id === curNetworkConfig.value.instance_id)
|
||||
console.log('curNetworkInst', ret)
|
||||
if (ret === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
})
|
||||
|
||||
function addNewNetwork() {
|
||||
networkStore.addNewNetwork()
|
||||
networkStore.curNetwork = networkStore.lastNetwork
|
||||
@@ -82,7 +100,7 @@ networkStore.$subscribe(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
async function runNetworkCb(cfg: NetworkConfig, cb: () => void) {
|
||||
async function runNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
|
||||
if (type() === 'android') {
|
||||
await prepareVpnService()
|
||||
networkStore.clearNetworkInstances()
|
||||
@@ -106,7 +124,7 @@ async function runNetworkCb(cfg: NetworkConfig, cb: () => void) {
|
||||
cb()
|
||||
}
|
||||
|
||||
async function stopNetworkCb(cfg: NetworkConfig, cb: () => void) {
|
||||
async function stopNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
|
||||
// console.log('stopNetworkCb', cfg, cb)
|
||||
cb()
|
||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||
@@ -145,7 +163,7 @@ const setting_menu_items = ref([
|
||||
label: () => t('exchange_language'),
|
||||
icon: 'pi pi-language',
|
||||
command: async () => {
|
||||
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
||||
await I18nUtils.loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
||||
await setTrayMenu([
|
||||
await MenuItemExit(t('tray.exit')),
|
||||
await MenuItemShow(t('tray.show')),
|
||||
@@ -221,7 +239,7 @@ onBeforeMount(async () => {
|
||||
getCurrentWindow().hide()
|
||||
const autoStartIds = networkStore.autoStartInstIds
|
||||
for (const id of autoStartIds) {
|
||||
const cfg = networkStore.networkList.find(item => item.instance_id === id)
|
||||
const cfg = networkStore.networkList.find((item: NetworkTypes.NetworkConfig) => item.instance_id === id)
|
||||
if (cfg) {
|
||||
networkStore.addNetworkInstance(cfg.instance_id)
|
||||
await runNetworkInstance(cfg)
|
||||
@@ -232,31 +250,34 @@ onBeforeMount(async () => {
|
||||
|
||||
onMounted(async () => {
|
||||
if (type() === 'android') {
|
||||
await initMobileVpnService()
|
||||
try {
|
||||
await initMobileVpnService()
|
||||
console.error("easytier init vpn service done")
|
||||
} catch (e: any) {
|
||||
console.error("easytier init vpn service failed", e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function isRunning(id: string) {
|
||||
return networkStore.networkInstanceIds.includes(id)
|
||||
}
|
||||
|
||||
async function saveTomlConfig(tomlConfig: string) {
|
||||
const config = await generateNetworkConfig(tomlConfig)
|
||||
networkStore.replaceCurNetwork(config);
|
||||
toast.add({ severity: 'success', detail: t('config_saved'), life: 3000 })
|
||||
visible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="root" class="flex flex-column">
|
||||
<Dialog v-model:visible="visible" modal header="Config File" :style="{ width: '70%' }">
|
||||
<Panel>
|
||||
<ScrollPanel style="width: 100%; height: 300px">
|
||||
<pre>{{ tomlConfig }}</pre>
|
||||
</ScrollPanel>
|
||||
</Panel>
|
||||
<Divider />
|
||||
<div class="flex gap-2 justify-content-end">
|
||||
<Button type="button" :label="t('close')" @click="visible = false" />
|
||||
</div>
|
||||
</Dialog>
|
||||
<div id="root" class="flex flex-col">
|
||||
<ConfigEditDialog v-model:visible="visible" :cur-network="curNetworkConfig" :readonly="activeStep !== '1'"
|
||||
:save-config="saveTomlConfig" :generate-config="parseNetworkConfig" />
|
||||
|
||||
<Dialog v-model:visible="aboutVisible" modal :header="t('about.title')" :style="{ width: '70%' }">
|
||||
<About />
|
||||
@@ -265,65 +286,55 @@ function isRunning(id: string) {
|
||||
<div>
|
||||
<Toolbar>
|
||||
<template #start>
|
||||
<div class="flex align-items-center">
|
||||
<div class="flex items-center">
|
||||
<Button icon="pi pi-plus" severity="primary" :label="t('add_new_network')" @click="addNewNetwork" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #center>
|
||||
<div class="min-w-40">
|
||||
<Dropdown
|
||||
v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||
:placeholder="t('select_network')" class="w-full"
|
||||
>
|
||||
<Select v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||
:placeholder="t('select_network')" class="w-full">
|
||||
<template #value="slotProps">
|
||||
<div class="flex items-start content-center">
|
||||
<div class="mr-3 flex-column">
|
||||
<div class="mr-4 flex-col">
|
||||
<span>{{ slotProps.value.network_name }}</span>
|
||||
</div>
|
||||
<Tag
|
||||
class="my-auto leading-3" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')"
|
||||
/>
|
||||
<Tag class="my-auto leading-3" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
</template>
|
||||
<template #option="slotProps">
|
||||
<div class="flex flex-col items-start content-center max-w-full">
|
||||
<div class="flex">
|
||||
<div class="mr-3">
|
||||
<div class="mr-4">
|
||||
{{ t('network_name') }}: {{ slotProps.option.network_name }}
|
||||
</div>
|
||||
<Tag
|
||||
class="my-auto leading-3"
|
||||
<Tag class="my-auto leading-3"
|
||||
:severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')"
|
||||
/>
|
||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
<div
|
||||
v-if="slotProps.option.networking_method !== NetworkingMethod.Standalone"
|
||||
class="max-w-full overflow-hidden text-ellipsis"
|
||||
>
|
||||
{{ slotProps.option.networking_method === NetworkingMethod.Manual
|
||||
<div v-if="slotProps.option.networking_method !== NetworkTypes.NetworkingMethod.Standalone"
|
||||
class="max-w-full overflow-hidden text-ellipsis">
|
||||
{{ slotProps.option.networking_method === NetworkTypes.NetworkingMethod.Manual
|
||||
? slotProps.option.peer_urls.join(', ')
|
||||
: slotProps.option.public_server_url }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isRunning(slotProps.option.instance_id) && networkStore.instances[slotProps.option.instance_id].detail && (networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 !== '')"
|
||||
>
|
||||
{{ networkStore.instances[slotProps.option.instance_id].detail
|
||||
? networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 : '' }}
|
||||
v-if="isRunning(slotProps.option.instance_id) && networkStore.instances[slotProps.option.instance_id].detail && (!!networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4)">
|
||||
{{
|
||||
Utils.ipv4InetToString(networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #end>
|
||||
<Button
|
||||
icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu"
|
||||
/>
|
||||
<Button icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
|
||||
<TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
||||
</template>
|
||||
</Toolbar>
|
||||
@@ -341,20 +352,16 @@ function isRunning(id: string) {
|
||||
</StepList>
|
||||
<StepPanels value="1">
|
||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1">
|
||||
<Config
|
||||
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
@run-network="runNetworkCb($event, () => activateCallback('2'))"
|
||||
/>
|
||||
<Config :instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
:cur-network="curNetworkConfig" @run-network="runNetworkCb($event, () => activateCallback('2'))" />
|
||||
</StepPanel>
|
||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2">
|
||||
<div class="flex flex-column">
|
||||
<Status :instance-id="networkStore.curNetworkId" />
|
||||
<div class="flex flex-col">
|
||||
<Status :cur-network-inst="curNetworkInst" />
|
||||
</div>
|
||||
<div class="flex pt-4 justify-content-center">
|
||||
<Button
|
||||
:label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))"
|
||||
/>
|
||||
<div class="flex pt-6 justify-center">
|
||||
<Button :label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))" />
|
||||
</div>
|
||||
</StepPanel>
|
||||
</StepPanels>
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import type { NetworkConfig, NetworkInstance, NetworkInstanceRunningInfo } from '~/types/network'
|
||||
import { DEFAULT_NETWORK_CONFIG } from '~/types/network'
|
||||
import { NetworkTypes } from 'easytier-frontend-lib'
|
||||
|
||||
export const useNetworkStore = defineStore('networkStore', {
|
||||
state: () => {
|
||||
const networkList = [DEFAULT_NETWORK_CONFIG()]
|
||||
const networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
|
||||
return {
|
||||
// for initially empty lists
|
||||
networkList: networkList as NetworkConfig[],
|
||||
networkList: networkList as NetworkTypes.NetworkConfig[],
|
||||
// for data that is not yet loaded
|
||||
curNetwork: networkList[0],
|
||||
|
||||
// uuid -> instance
|
||||
instances: {} as Record<string, NetworkInstance>,
|
||||
instances: {} as Record<string, NetworkTypes.NetworkInstance>,
|
||||
|
||||
networkInfos: {} as Record<string, NetworkInstanceRunningInfo>,
|
||||
networkInfos: {} as Record<string, NetworkTypes.NetworkInstanceRunningInfo>,
|
||||
|
||||
autoStartInstIds: [] as string[],
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
lastNetwork(): NetworkConfig {
|
||||
lastNetwork(): NetworkTypes.NetworkConfig {
|
||||
return this.networkList[this.networkList.length - 1]
|
||||
},
|
||||
|
||||
@@ -28,7 +27,7 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
return this.curNetwork.instance_id
|
||||
},
|
||||
|
||||
networkInstances(): Array<NetworkInstance> {
|
||||
networkInstances(): Array<NetworkTypes.NetworkInstance> {
|
||||
return Object.values(this.instances)
|
||||
},
|
||||
|
||||
@@ -39,7 +38,7 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
|
||||
actions: {
|
||||
addNewNetwork() {
|
||||
this.networkList.push(DEFAULT_NETWORK_CONFIG())
|
||||
this.networkList.push(NetworkTypes.DEFAULT_NETWORK_CONFIG())
|
||||
},
|
||||
|
||||
delCurNetwork() {
|
||||
@@ -49,6 +48,12 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
this.curNetwork = this.networkList[nextCurNetworkIdx]
|
||||
},
|
||||
|
||||
replaceCurNetwork(cfg: NetworkTypes.NetworkConfig) {
|
||||
const curNetworkIdx = this.networkList.indexOf(this.curNetwork)
|
||||
this.networkList[curNetworkIdx] = cfg
|
||||
this.curNetwork = cfg
|
||||
},
|
||||
|
||||
removeNetworkInstance(instanceId: string) {
|
||||
delete this.instances[instanceId]
|
||||
},
|
||||
@@ -66,7 +71,7 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
this.instances = {}
|
||||
},
|
||||
|
||||
updateWithNetworkInfos(networkInfos: Record<string, NetworkInstanceRunningInfo>) {
|
||||
updateWithNetworkInfos(networkInfos: Record<string, NetworkTypes.NetworkInstanceRunningInfo>) {
|
||||
this.networkInfos = networkInfos
|
||||
for (const [instanceId, info] of Object.entries(networkInfos)) {
|
||||
if (this.instances[instanceId] === undefined)
|
||||
@@ -79,17 +84,17 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
},
|
||||
|
||||
loadFromLocalStorage() {
|
||||
let networkList: NetworkConfig[]
|
||||
let networkList: NetworkTypes.NetworkConfig[]
|
||||
|
||||
// if localStorage default is [{}], instanceId will be undefined
|
||||
networkList = JSON.parse(localStorage.getItem('networkList') || '[]')
|
||||
networkList = networkList.map((cfg) => {
|
||||
return { ...DEFAULT_NETWORK_CONFIG(), ...cfg } as NetworkConfig
|
||||
return { ...NetworkTypes.DEFAULT_NETWORK_CONFIG(), ...cfg } as NetworkTypes.NetworkConfig
|
||||
})
|
||||
|
||||
// prevent a empty list from localStorage, should not happen
|
||||
if (networkList.length === 0)
|
||||
networkList = [DEFAULT_NETWORK_CONFIG()]
|
||||
networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
|
||||
|
||||
this.networkList = networkList
|
||||
this.curNetwork = this.networkList[0]
|
||||
@@ -129,6 +134,13 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||
}
|
||||
this.saveAutoStartInstIdsToLocalStorage()
|
||||
},
|
||||
|
||||
isNoTunEnabled(instanceId: string): boolean {
|
||||
const cfg = this.networkList.find((cfg) => cfg.instance_id === instanceId)
|
||||
if (!cfg)
|
||||
return false
|
||||
return cfg.no_tun ?? false
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -45,3 +45,11 @@
|
||||
border-radius: 4px;
|
||||
background-color: #0000005d;
|
||||
}
|
||||
|
||||
.p-password {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-password>input {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { networkInterfaces } from 'node:os'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
|
||||
import { PrimeVueResolver } from '@primevue/auto-import-resolver'
|
||||
import Vue from '@vitejs/plugin-vue'
|
||||
import { internalIpV4Sync } from 'internal-ip'
|
||||
import { containsCidr, parseCidr } from 'cidr-tools'
|
||||
import { gateway4sync } from 'default-gateway'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import VueMacros from 'unplugin-vue-macros/vite'
|
||||
@@ -13,6 +15,20 @@ import { defineConfig } from 'vite'
|
||||
import VueDevTools from 'vite-plugin-vue-devtools'
|
||||
import Layouts from 'vite-plugin-vue-layouts'
|
||||
|
||||
function findIp(gateway: string) {
|
||||
// Look for the matching interface in all local interfaces
|
||||
console.log('gateway', gateway)
|
||||
for (const addresses of Object.values(networkInterfaces())) {
|
||||
if (!addresses)
|
||||
continue
|
||||
for (const { cidr } of addresses) {
|
||||
if (cidr && containsCidr(cidr, gateway)) {
|
||||
return parseCidr(cidr).ip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
@@ -99,10 +115,10 @@ export default defineConfig(async () => ({
|
||||
},
|
||||
hmr: host
|
||||
? {
|
||||
protocol: 'ws',
|
||||
host: internalIpV4Sync(),
|
||||
port: 1430,
|
||||
}
|
||||
protocol: 'ws',
|
||||
host: findIp(gateway4sync().gateway),
|
||||
port: 1430,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
}))
|
||||
|
||||
21
easytier-rpc-build/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "easytier-rpc-build"
|
||||
description = "Protobuf RPC Service Generator for EasyTier"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
homepage = "https://github.com/EasyTier/EasyTier"
|
||||
repository = "https://github.com/EasyTier/EasyTier"
|
||||
authors = ["kkrainbow"]
|
||||
keywords = ["vpn", "p2p", "network", "easytier"]
|
||||
categories = ["network-programming", "command-line-utilities"]
|
||||
rust-version = "1.84.0"
|
||||
license-file = "LICENSE"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
heck = "0.5"
|
||||
prost-build = "0.13"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
internal-namespace = []
|
||||
1
easytier-rpc-build/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE
|
||||
3
easytier-rpc-build/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Introduction
|
||||
|
||||
This is a protobuf rpc service stub generator for [EasyTier](https://github.com/EasyTier/EasyTier) project.
|
||||
@@ -3,8 +3,12 @@ extern crate prost_build;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "internal-namespace")]
|
||||
const NAMESPACE: &str = "crate::proto::rpc_types";
|
||||
|
||||
#[cfg(not(feature = "internal-namespace"))]
|
||||
const NAMESPACE: &str = "easytier::proto::rpc_types";
|
||||
|
||||
/// The service generator to be used with `prost-build` to generate RPC implementations for
|
||||
/// `prost-simple-rpc`.
|
||||
///
|
||||
73
easytier-web/Cargo.toml
Normal file
@@ -0,0 +1,73 @@
|
||||
[package]
|
||||
name = "easytier-web"
|
||||
version = "2.3.2"
|
||||
edition = "2021"
|
||||
description = "Config server for easytier. easytier-core gets config from this and web frontend use it as restful api server."
|
||||
|
||||
[dependencies]
|
||||
easytier = { path = "../easytier" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
anyhow = { version = "1.0" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
dashmap = "6.1"
|
||||
url = "2.2"
|
||||
async-trait = "0.1"
|
||||
|
||||
axum = { version = "0.7", features = ["macros"] }
|
||||
axum-login = { version = "0.16" }
|
||||
password-auth = { version = "1.0.0" }
|
||||
axum-messages = "0.7.0"
|
||||
axum-embed = { version = "0.1.0", optional = true }
|
||||
tower-sessions-sqlx-store = { version = "0.14.1", features = ["sqlite"] }
|
||||
tower-sessions = { version = "0.13.0", default-features = false, features = [
|
||||
"signed",
|
||||
] }
|
||||
tower-http = { version = "0.6", features = ["cors", "compression-full"] }
|
||||
sqlx = { version = "0.8", features = ["sqlite"] }
|
||||
sea-orm = { version = "1.1", features = [
|
||||
"sqlx-sqlite",
|
||||
"runtime-tokio-rustls",
|
||||
"macros",
|
||||
] }
|
||||
sea-orm-migration = { version = "1.1" }
|
||||
|
||||
|
||||
# for captcha
|
||||
rust-embed = { version = "8.5.0", features = ["debug-embed"] }
|
||||
base64 = "0.22"
|
||||
rand = "0.8"
|
||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||
rusttype = "0.9.3"
|
||||
imageproc = "0.23.0"
|
||||
|
||||
|
||||
rust-i18n = "3"
|
||||
sys-locale = "0.3"
|
||||
clap = { version = "4.4.8", features = [
|
||||
"string",
|
||||
"unicode",
|
||||
"derive",
|
||||
"wrap_help",
|
||||
] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
uuid = { version = "1.5.0", features = [
|
||||
"v4",
|
||||
"fast-rng",
|
||||
"macro-diagnostics",
|
||||
"serde",
|
||||
] }
|
||||
|
||||
chrono = { version = "0.4.37", features = ["serde"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
embed = ["dep:axum-embed"]
|
||||
|
||||
# enable thunk-rs when compiling for x86_64 or i686 windows
|
||||
[target.x86_64-pc-windows-msvc.build-dependencies]
|
||||
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||
|
||||
[target.i686-pc-windows-msvc.build-dependencies]
|
||||
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||
7
easytier-web/build.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
// enable thunk-rs when target os is windows and arch is x86_64 or i686
|
||||
#[cfg(target_os = "windows")]
|
||||
if !std::env::var("TARGET").unwrap_or_default().contains("aarch64"){
|
||||
thunk::thunk();
|
||||
}
|
||||
}
|
||||
24
easytier-web/frontend-lib/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
5
easytier-web/frontend-lib/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
||||
13
easytier-web/frontend-lib/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
49
easytier-web/frontend-lib/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "easytier-frontend-lib",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "./dist/easytier-frontend-lib.umd.cjs",
|
||||
"module": "./dist/easytier-frontend-lib.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/easytier-frontend-lib.js",
|
||||
"require": "./dist/easytier-frontend-lib.umd.cjs"
|
||||
},
|
||||
"./*.css": "./dist/*.css"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@primevue/themes": "4.3.3",
|
||||
"@vueuse/core": "^11.1.0",
|
||||
"aura": "link:@primevue\\themes\\aura",
|
||||
"axios": "^1.7.7",
|
||||
"floating-vue": "^5.2",
|
||||
"ip-num": "1.5.1",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "4.3.3",
|
||||
"tailwindcss-primeui": "^0.3.4",
|
||||
"ts-md5": "^1.3.1",
|
||||
"uuid": "^11.0.2",
|
||||
"vue": "^3.5.12",
|
||||
"vue-i18n": "^10.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@modyfi/vite-plugin-yaml": "^1.1.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"tailwindcss": "=3.4.17",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^5.4.10",
|
||||
"vite-plugin-dts": "^4.3.0",
|
||||
"vue-tsc": "^2.1.10"
|
||||
}
|
||||
}
|
||||
820
easytier-web/frontend-lib/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,820 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
vue:
|
||||
specifier: ^3.5.12
|
||||
version: 3.5.12(typescript@5.6.3)
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4(vite@5.4.10)(vue@3.5.12(typescript@5.6.3))
|
||||
typescript:
|
||||
specifier: ~5.6.2
|
||||
version: 5.6.3
|
||||
vite:
|
||||
specifier: ^5.4.10
|
||||
version: 5.4.10
|
||||
vue-tsc:
|
||||
specifier: ^2.1.8
|
||||
version: 2.1.10(typescript@5.6.3)
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/helper-string-parser@7.25.9':
|
||||
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9':
|
||||
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.26.2':
|
||||
resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/types@7.26.0':
|
||||
resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.21.5':
|
||||
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.21.5':
|
||||
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.21.5':
|
||||
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.21.5':
|
||||
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.21.5':
|
||||
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.21.5':
|
||||
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.21.5':
|
||||
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.21.5':
|
||||
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.21.5':
|
||||
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.21.5':
|
||||
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.21.5':
|
||||
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.21.5':
|
||||
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.21.5':
|
||||
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.21.5':
|
||||
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.21.5':
|
||||
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.21.5':
|
||||
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-x64@0.21.5':
|
||||
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.21.5':
|
||||
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/sunos-x64@0.21.5':
|
||||
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.21.5':
|
||||
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.21.5':
|
||||
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.21.5':
|
||||
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.24.3':
|
||||
resolution: {integrity: sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.24.3':
|
||||
resolution: {integrity: sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.24.3':
|
||||
resolution: {integrity: sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.24.3':
|
||||
resolution: {integrity: sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.24.3':
|
||||
resolution: {integrity: sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.24.3':
|
||||
resolution: {integrity: sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.24.3':
|
||||
resolution: {integrity: sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.24.3':
|
||||
resolution: {integrity: sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.24.3':
|
||||
resolution: {integrity: sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.24.3':
|
||||
resolution: {integrity: sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.24.3':
|
||||
resolution: {integrity: sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.24.3':
|
||||
resolution: {integrity: sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.24.3':
|
||||
resolution: {integrity: sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.24.3':
|
||||
resolution: {integrity: sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.24.3':
|
||||
resolution: {integrity: sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.24.3':
|
||||
resolution: {integrity: sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.24.3':
|
||||
resolution: {integrity: sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.24.3':
|
||||
resolution: {integrity: sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/estree@1.0.6':
|
||||
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
||||
|
||||
'@vitejs/plugin-vue@5.1.4':
|
||||
resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
vite: ^5.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@volar/language-core@2.4.8':
|
||||
resolution: {integrity: sha512-K/GxMOXGq997bO00cdFhTNuR85xPxj0BEEAy+BaqqayTmy9Tmhfgmq2wpJcVspRhcwfgPoE2/mEJa26emUhG/g==}
|
||||
|
||||
'@volar/source-map@2.4.8':
|
||||
resolution: {integrity: sha512-jeWJBkC/WivdelMwxKkpFL811uH/jJ1kVxa+c7OvG48DXc3VrP7pplSWPP2W1dLMqBxD+awRlg55FQQfiup4cA==}
|
||||
|
||||
'@volar/typescript@2.4.8':
|
||||
resolution: {integrity: sha512-6xkIYJ5xxghVBhVywMoPMidDDAFT1OoQeXwa27HSgJ6AiIKRe61RXLoik+14Z7r0JvnblXVsjsRLmCr42SGzqg==}
|
||||
|
||||
'@vue/compiler-core@3.5.12':
|
||||
resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==}
|
||||
|
||||
'@vue/compiler-dom@3.5.12':
|
||||
resolution: {integrity: sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.12':
|
||||
resolution: {integrity: sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.12':
|
||||
resolution: {integrity: sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==}
|
||||
|
||||
'@vue/compiler-vue2@2.7.16':
|
||||
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
|
||||
|
||||
'@vue/language-core@2.1.10':
|
||||
resolution: {integrity: sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity@3.5.12':
|
||||
resolution: {integrity: sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==}
|
||||
|
||||
'@vue/runtime-core@3.5.12':
|
||||
resolution: {integrity: sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==}
|
||||
|
||||
'@vue/runtime-dom@3.5.12':
|
||||
resolution: {integrity: sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==}
|
||||
|
||||
'@vue/server-renderer@3.5.12':
|
||||
resolution: {integrity: sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==}
|
||||
peerDependencies:
|
||||
vue: 3.5.12
|
||||
|
||||
'@vue/shared@3.5.12':
|
||||
resolution: {integrity: sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==}
|
||||
|
||||
alien-signals@0.2.0:
|
||||
resolution: {integrity: sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
de-indent@1.0.2:
|
||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
esbuild@0.21.5:
|
||||
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
he@1.2.0:
|
||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
|
||||
magic-string@0.30.12:
|
||||
resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
|
||||
|
||||
minimatch@9.0.5:
|
||||
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
|
||||
nanoid@3.3.7:
|
||||
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
postcss@8.4.47:
|
||||
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rollup@4.24.3:
|
||||
resolution: {integrity: sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.6.3:
|
||||
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
typescript@5.6.3:
|
||||
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
vite@5.4.10:
|
||||
resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
sass-embedded: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.4.0
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
|
||||
vscode-uri@3.0.8:
|
||||
resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
|
||||
|
||||
vue-tsc@2.1.10:
|
||||
resolution: {integrity: sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue@3.5.12:
|
||||
resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.25.9': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9': {}
|
||||
|
||||
'@babel/parser@7.26.2':
|
||||
dependencies:
|
||||
'@babel/types': 7.26.0
|
||||
|
||||
'@babel/types@7.26.0':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.25.9
|
||||
'@babel/helper-validator-identifier': 7.25.9
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.24.3':
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.6': {}
|
||||
|
||||
'@vitejs/plugin-vue@5.1.4(vite@5.4.10)(vue@3.5.12(typescript@5.6.3))':
|
||||
dependencies:
|
||||
vite: 5.4.10
|
||||
vue: 3.5.12(typescript@5.6.3)
|
||||
|
||||
'@volar/language-core@2.4.8':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.8
|
||||
|
||||
'@volar/source-map@2.4.8': {}
|
||||
|
||||
'@volar/typescript@2.4.8':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.8
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.0.8
|
||||
|
||||
'@vue/compiler-core@3.5.12':
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.2
|
||||
'@vue/shared': 3.5.12
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.12':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
|
||||
'@vue/compiler-sfc@3.5.12':
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.2
|
||||
'@vue/compiler-core': 3.5.12
|
||||
'@vue/compiler-dom': 3.5.12
|
||||
'@vue/compiler-ssr': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.12
|
||||
postcss: 8.4.47
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.12':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
|
||||
'@vue/compiler-vue2@2.7.16':
|
||||
dependencies:
|
||||
de-indent: 1.0.2
|
||||
he: 1.2.0
|
||||
|
||||
'@vue/language-core@2.1.10(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.8
|
||||
'@vue/compiler-dom': 3.5.12
|
||||
'@vue/compiler-vue2': 2.7.16
|
||||
'@vue/shared': 3.5.12
|
||||
alien-signals: 0.2.0
|
||||
minimatch: 9.0.5
|
||||
muggle-string: 0.4.1
|
||||
path-browserify: 1.0.1
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
'@vue/reactivity@3.5.12':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.12
|
||||
|
||||
'@vue/runtime-core@3.5.12':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
|
||||
'@vue/runtime-dom@3.5.12':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.12
|
||||
'@vue/runtime-core': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.12
|
||||
'@vue/shared': 3.5.12
|
||||
vue: 3.5.12(typescript@5.6.3)
|
||||
|
||||
'@vue/shared@3.5.12': {}
|
||||
|
||||
alien-signals@0.2.0: {}
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
esbuild@0.21.5:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.21.5
|
||||
'@esbuild/android-arm': 0.21.5
|
||||
'@esbuild/android-arm64': 0.21.5
|
||||
'@esbuild/android-x64': 0.21.5
|
||||
'@esbuild/darwin-arm64': 0.21.5
|
||||
'@esbuild/darwin-x64': 0.21.5
|
||||
'@esbuild/freebsd-arm64': 0.21.5
|
||||
'@esbuild/freebsd-x64': 0.21.5
|
||||
'@esbuild/linux-arm': 0.21.5
|
||||
'@esbuild/linux-arm64': 0.21.5
|
||||
'@esbuild/linux-ia32': 0.21.5
|
||||
'@esbuild/linux-loong64': 0.21.5
|
||||
'@esbuild/linux-mips64el': 0.21.5
|
||||
'@esbuild/linux-ppc64': 0.21.5
|
||||
'@esbuild/linux-riscv64': 0.21.5
|
||||
'@esbuild/linux-s390x': 0.21.5
|
||||
'@esbuild/linux-x64': 0.21.5
|
||||
'@esbuild/netbsd-x64': 0.21.5
|
||||
'@esbuild/openbsd-x64': 0.21.5
|
||||
'@esbuild/sunos-x64': 0.21.5
|
||||
'@esbuild/win32-arm64': 0.21.5
|
||||
'@esbuild/win32-ia32': 0.21.5
|
||||
'@esbuild/win32-x64': 0.21.5
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
he@1.2.0: {}
|
||||
|
||||
magic-string@0.30.12:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
minimatch@9.0.5:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
nanoid@3.3.7: {}
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
postcss@8.4.47:
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rollup@4.24.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.24.3
|
||||
'@rollup/rollup-android-arm64': 4.24.3
|
||||
'@rollup/rollup-darwin-arm64': 4.24.3
|
||||
'@rollup/rollup-darwin-x64': 4.24.3
|
||||
'@rollup/rollup-freebsd-arm64': 4.24.3
|
||||
'@rollup/rollup-freebsd-x64': 4.24.3
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.24.3
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.24.3
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.24.3
|
||||
'@rollup/rollup-linux-arm64-musl': 4.24.3
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.24.3
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.24.3
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.24.3
|
||||
'@rollup/rollup-linux-x64-gnu': 4.24.3
|
||||
'@rollup/rollup-linux-x64-musl': 4.24.3
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.24.3
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.24.3
|
||||
'@rollup/rollup-win32-x64-msvc': 4.24.3
|
||||
fsevents: 2.3.3
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
typescript@5.6.3: {}
|
||||
|
||||
vite@5.4.10:
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.4.47
|
||||
rollup: 4.24.3
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
vscode-uri@3.0.8: {}
|
||||
|
||||
vue-tsc@2.1.10(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.8
|
||||
'@vue/language-core': 2.1.10(typescript@5.6.3)
|
||||
semver: 7.6.3
|
||||
typescript: 5.6.3
|
||||
|
||||
vue@3.5.12(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.12
|
||||
'@vue/compiler-sfc': 3.5.12
|
||||
'@vue/runtime-dom': 3.5.12
|
||||
'@vue/server-renderer': 3.5.12(vue@3.5.12(typescript@5.6.3))
|
||||
'@vue/shared': 3.5.12
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
7
easytier-web/frontend-lib/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
"postcss-nested": {},
|
||||
},
|
||||
}
|
||||
1
easytier-web/frontend-lib/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
easytier-web/frontend-lib/src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
426
easytier-web/frontend-lib/src/components/Config.vue
Normal file
@@ -0,0 +1,426 @@
|
||||
<script setup lang="ts">
|
||||
import InputGroup from 'primevue/inputgroup'
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||
import { SelectButton, Checkbox, InputText, InputNumber, AutoComplete, Panel, Divider, ToggleButton, Button, Password } from 'primevue'
|
||||
import { DEFAULT_NETWORK_CONFIG, NetworkConfig, NetworkingMethod } from '../types/network'
|
||||
import { defineProps, defineEmits, ref, } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = defineProps<{
|
||||
configInvalid?: boolean
|
||||
hostname?: string
|
||||
}>()
|
||||
|
||||
defineEmits(['runNetwork'])
|
||||
|
||||
const curNetwork = defineModel('curNetwork', {
|
||||
type: Object as () => NetworkConfig,
|
||||
default: DEFAULT_NETWORK_CONFIG,
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const networking_methods = ref([
|
||||
{ value: NetworkingMethod.PublicServer, label: () => t('public_server') },
|
||||
{ value: NetworkingMethod.Manual, label: () => t('manual') },
|
||||
{ value: NetworkingMethod.Standalone, label: () => t('standalone') },
|
||||
])
|
||||
|
||||
const protos: { [proto: string]: number } = { tcp: 11010, udp: 11010, wg: 11011, ws: 11011, wss: 11012 }
|
||||
|
||||
function searchUrlSuggestions(e: { query: string }): string[] {
|
||||
const query = e.query
|
||||
const ret = []
|
||||
// if query match "^\w+:.*", then no proto prefix
|
||||
if (query.match(/^\w+:.*/)) {
|
||||
// if query is a valid url, then add to suggestions
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(query)
|
||||
ret.push(query)
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else {
|
||||
for (const proto in protos) {
|
||||
let item = `${proto}://${query}`
|
||||
// if query match ":\d+$", then no port suffix
|
||||
if (!query.match(/:\d+$/)) {
|
||||
item += `:${protos[proto]}`
|
||||
}
|
||||
ret.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
const publicServerSuggestions = ref([''])
|
||||
|
||||
function searchPresetPublicServers(e: { query: string }) {
|
||||
const presetPublicServers = [
|
||||
'tcp://public.easytier.top:11010',
|
||||
]
|
||||
|
||||
const query = e.query
|
||||
// if query is sub string of presetPublicServers, add to suggestions
|
||||
let ret = presetPublicServers.filter(item => item.includes(query))
|
||||
// add additional suggestions
|
||||
if (query.length > 0) {
|
||||
ret = ret.concat(searchUrlSuggestions(e))
|
||||
}
|
||||
|
||||
publicServerSuggestions.value = ret
|
||||
}
|
||||
|
||||
const peerSuggestions = ref([''])
|
||||
|
||||
function searchPeerSuggestions(e: { query: string }) {
|
||||
peerSuggestions.value = searchUrlSuggestions(e)
|
||||
}
|
||||
|
||||
const inetSuggestions = ref([''])
|
||||
|
||||
function searchInetSuggestions(e: { query: string }) {
|
||||
if (e.query.search('/') >= 0) {
|
||||
inetSuggestions.value = [e.query]
|
||||
} else {
|
||||
const ret = []
|
||||
for (let i = 0; i < 32; i++) {
|
||||
ret.push(`${e.query}/${i}`)
|
||||
}
|
||||
inetSuggestions.value = ret
|
||||
}
|
||||
}
|
||||
|
||||
const listenerSuggestions = ref([''])
|
||||
|
||||
function searchListenerSuggestions(e: { query: string }) {
|
||||
const ret = []
|
||||
|
||||
for (const proto in protos) {
|
||||
let item = `${proto}://0.0.0.0:`
|
||||
// if query is a number, use it as port
|
||||
if (e.query.match(/^\d+$/)) {
|
||||
item += e.query
|
||||
}
|
||||
else {
|
||||
item += protos[proto]
|
||||
}
|
||||
|
||||
if (item.includes(e.query)) {
|
||||
ret.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.length === 0) {
|
||||
ret.push(e.query)
|
||||
}
|
||||
|
||||
listenerSuggestions.value = ret
|
||||
}
|
||||
|
||||
|
||||
const exitNodesSuggestions = ref([''])
|
||||
|
||||
function searchExitNodesSuggestions(e: { query: string }) {
|
||||
const ret = []
|
||||
ret.push(e.query)
|
||||
exitNodesSuggestions.value = ret
|
||||
}
|
||||
|
||||
const whitelistSuggestions = ref([''])
|
||||
|
||||
function searchWhitelistSuggestions(e: { query: string }) {
|
||||
const ret = []
|
||||
ret.push(e.query)
|
||||
whitelistSuggestions.value = ret
|
||||
}
|
||||
|
||||
interface BoolFlag {
|
||||
field: keyof NetworkConfig
|
||||
help: string
|
||||
}
|
||||
|
||||
const bool_flags: BoolFlag[] = [
|
||||
{ field: 'latency_first', help: 'latency_first_help' },
|
||||
{ field: 'use_smoltcp', help: 'use_smoltcp_help' },
|
||||
{ field: 'enable_kcp_proxy', help: 'enable_kcp_proxy_help' },
|
||||
{ field: 'disable_kcp_input', help: 'disable_kcp_input_help' },
|
||||
{ field: 'enable_quic_proxy', help: 'enable_quic_proxy_help' },
|
||||
{ field: 'disable_quic_input', help: 'disable_quic_input_help' },
|
||||
{ field: 'disable_p2p', help: 'disable_p2p_help' },
|
||||
{ field: 'bind_device', help: 'bind_device_help' },
|
||||
{ field: 'no_tun', help: 'no_tun_help' },
|
||||
{ field: 'enable_exit_node', help: 'enable_exit_node_help' },
|
||||
{ field: 'relay_all_peer_rpc', help: 'relay_all_peer_rpc_help' },
|
||||
{ field: 'multi_thread', help: 'multi_thread_help' },
|
||||
{ field: 'proxy_forward_by_system', help: 'proxy_forward_by_system_help' },
|
||||
{ field: 'disable_encryption', help: 'disable_encryption_help' },
|
||||
{ field: 'disable_udp_hole_punching', help: 'disable_udp_hole_punching_help' },
|
||||
{ field: 'enable_magic_dns', help: 'enable_magic_dns_help' },
|
||||
{ field: 'enable_private_mode', help: 'enable_private_mode_help' },
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="frontend-lib">
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="flex flex-col">
|
||||
<div class="w-11/12 self-center ">
|
||||
<Panel :header="t('basic_settings')">
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<div class="flex items-center" for="virtual_ip">
|
||||
<label class="mr-2"> {{ t('virtual_ipv4') }} </label>
|
||||
<Checkbox v-model="curNetwork.dhcp" input-id="virtual_ip_auto" :binary="true" />
|
||||
|
||||
<label for="virtual_ip_auto" class="ml-2">
|
||||
{{ t('virtual_ipv4_dhcp') }}
|
||||
</label>
|
||||
</div>
|
||||
<InputGroup>
|
||||
<InputText id="virtual_ip" v-model="curNetwork.virtual_ipv4" :disabled="curNetwork.dhcp"
|
||||
aria-describedby="virtual_ipv4-help" />
|
||||
<InputGroupAddon>
|
||||
<span>/</span>
|
||||
</InputGroupAddon>
|
||||
<InputNumber v-model="curNetwork.network_length" :disabled="curNetwork.dhcp"
|
||||
inputId="horizontal-buttons" showButtons :step="1" mode="decimal" :min="1" :max="32" fluid
|
||||
class="max-w-20" />
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="network_name">{{ t('network_name') }}</label>
|
||||
<InputText id="network_name" v-model="curNetwork.network_name" aria-describedby="network_name-help" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="network_secret">{{ t('network_secret') }}</label>
|
||||
<Password id="network_secret" v-model="curNetwork.network_secret"
|
||||
aria-describedby="network_secret-help" toggleMask :feedback="false" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="nm">{{ t('networking_method') }}</label>
|
||||
<SelectButton v-model="curNetwork.networking_method" :options="networking_methods"
|
||||
:option-label="(v) => v.label()" option-value="value" />
|
||||
<div class="items-center flex flex-row p-fluid gap-x-1">
|
||||
<AutoComplete v-if="curNetwork.networking_method === NetworkingMethod.Manual" id="chips"
|
||||
v-model="curNetwork.peer_urls" :placeholder="t('chips_placeholder', ['tcp://8.8.8.8:11010'])"
|
||||
class="grow" multiple fluid :suggestions="peerSuggestions" @complete="searchPeerSuggestions" />
|
||||
|
||||
<AutoComplete v-if="curNetwork.networking_method === NetworkingMethod.PublicServer"
|
||||
v-model="curNetwork.public_server_url" :suggestions="publicServerSuggestions"
|
||||
:virtual-scroller-options="{ itemSize: 38 }" class="grow" dropdown :complete-on-focus="true"
|
||||
@complete="searchPresetPublicServers" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Panel :header="t('advanced_settings')" toggleable collapsed>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label> {{ t('flags_switch') }} </label>
|
||||
<div class="flex flex-row flex-wrap">
|
||||
|
||||
<div class="basis-[20rem] flex items-center" v-for="flag in bool_flags">
|
||||
<Checkbox v-model="curNetwork[flag.field]" :input-id="flag.field" :binary="true" />
|
||||
<label :for="flag.field" class="ml-2"> {{ t(flag.field) }} </label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t(flag.help)"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="hostname">{{ t('hostname') }}</label>
|
||||
<InputText id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
||||
:placeholder="t('hostname_placeholder', [props.hostname])" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-col gap-2 grow p-fluid">
|
||||
<label for="username">{{ t('proxy_cidrs') }}</label>
|
||||
<AutoComplete id="subnet-proxy" v-model="curNetwork.proxy_cidrs"
|
||||
:placeholder="t('chips_placeholder', ['10.0.0.0/24'])" class="w-full" multiple fluid
|
||||
:suggestions="inetSuggestions" @complete="searchInetSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap ">
|
||||
<div class="flex flex-col gap-2 grow">
|
||||
<label for="username">VPN Portal</label>
|
||||
<ToggleButton v-model="curNetwork.enable_vpn_portal" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48" />
|
||||
<div v-if="curNetwork.enable_vpn_portal" class="items-center flex flex-row gap-x-4">
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-col gap-2 basis-8/12 grow">
|
||||
<InputGroup>
|
||||
<InputText v-model="curNetwork.vpn_portal_client_network_addr"
|
||||
:placeholder="t('vpn_portal_client_network')" />
|
||||
<InputGroupAddon>
|
||||
<span>/{{ curNetwork.vpn_portal_client_network_len }}</span>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 basis-3/12 grow">
|
||||
<InputNumber v-model="curNetwork.vpn_portal_listen_port" :allow-empty="false" :format="false"
|
||||
:min="0" :max="65535" fluid />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 grow p-fluid">
|
||||
<label for="listener_urls">{{ t('listener_urls') }}</label>
|
||||
<AutoComplete id="listener_urls" v-model="curNetwork.listener_urls" :suggestions="listenerSuggestions"
|
||||
class="w-full" dropdown :complete-on-focus="true"
|
||||
:placeholder="t('chips_placeholder', ['tcp://1.1.1.1:11010'])" multiple
|
||||
@complete="searchListenerSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="rpc_port">{{ t('rpc_port') }}</label>
|
||||
<InputNumber id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="rpc_port-help"
|
||||
:format="false" :min="0" :max="65535" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-col gap-2 grow p-fluid">
|
||||
<label for="">{{ t('rpc_portal_whitelists') }}</label>
|
||||
<AutoComplete id="rpc_portal_whitelists" v-model="curNetwork.rpc_portal_whitelists"
|
||||
:placeholder="t('chips_placeholder', ['127.0.0.0/8'])" class="w-full" multiple fluid
|
||||
:suggestions="inetSuggestions" @complete="searchInetSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<label for="dev_name">{{ t('dev_name') }}</label>
|
||||
<InputText id="dev_name" v-model="curNetwork.dev_name" aria-describedby="dev_name-help" :format="true"
|
||||
:placeholder="t('dev_name_placeholder')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<div class="flex">
|
||||
<label for="mtu">{{ t('mtu') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('mtu_help')"></span>
|
||||
</div>
|
||||
<InputNumber id="mtu" v-model="curNetwork.mtu" aria-describedby="mtu-help" :format="false"
|
||||
:placeholder="t('mtu_placeholder')" :min="400" :max="1380" fluid />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||
<div class="flex">
|
||||
<label for="relay_network_whitelist">{{ t('relay_network_whitelist') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center"
|
||||
v-tooltip="t('relay_network_whitelist_help')"></span>
|
||||
</div>
|
||||
<ToggleButton v-model="curNetwork.enable_relay_network_whitelist" on-icon="pi pi-check"
|
||||
off-icon="pi pi-times" :on-label="t('off_text')" :off-label="t('on_text')" class="w-48" />
|
||||
<div v-if="curNetwork.enable_relay_network_whitelist" class="items-center flex flex-row gap-x-4">
|
||||
<div class="min-w-64 w-full">
|
||||
<AutoComplete id="relay_network_whitelist" v-model="curNetwork.relay_network_whitelist"
|
||||
:placeholder="t('relay_network_whitelist')" class="w-full" multiple fluid
|
||||
:suggestions="whitelistSuggestions" @complete="searchWhitelistSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap ">
|
||||
<div class="flex flex-col gap-2 grow">
|
||||
<div class="flex">
|
||||
<label for="routes">{{ t('manual_routes') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('manual_routes_help')"></span>
|
||||
</div>
|
||||
<ToggleButton v-model="curNetwork.enable_manual_routes" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48" />
|
||||
<div v-if="curNetwork.enable_manual_routes" class="items-center flex flex-row gap-x-4">
|
||||
<div class="min-w-64 w-full">
|
||||
<AutoComplete id="routes" v-model="curNetwork.routes"
|
||||
:placeholder="t('chips_placeholder', ['192.168.0.0/16'])" class="w-full" multiple fluid
|
||||
:suggestions="inetSuggestions" @complete="searchInetSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap ">
|
||||
<div class="flex flex-col gap-2 grow">
|
||||
<div class="flex">
|
||||
<label for="socks5_port">{{ t('socks5') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('socks5_help')"></span>
|
||||
</div>
|
||||
<ToggleButton v-model="curNetwork.enable_socks5" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48" />
|
||||
<div v-if="curNetwork.enable_socks5" class="items-center flex flex-row gap-x-4">
|
||||
<div class="min-w-64 w-full">
|
||||
<InputNumber id="socks5_port" v-model="curNetwork.socks5_port" aria-describedby="rpc_port-help"
|
||||
:format="false" :allow-empty="false" :min="0" :max="65535" class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-col gap-2 grow p-fluid">
|
||||
<div class="flex">
|
||||
<label for="exit_nodes">{{ t('exit_nodes') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('exit_nodes_help')"></span>
|
||||
</div>
|
||||
<AutoComplete id="exit_nodes" v-model="curNetwork.exit_nodes"
|
||||
:placeholder="t('chips_placeholder', ['192.168.8.8'])" class="w-full" multiple fluid
|
||||
:suggestions="exitNodesSuggestions" @complete="searchExitNodesSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-col gap-2 grow p-fluid">
|
||||
<div class="flex">
|
||||
<label for="mapped_listeners">{{ t('mapped_listeners') }}</label>
|
||||
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('mapped_listeners_help')"></span>
|
||||
</div>
|
||||
<AutoComplete id="mapped_listeners" v-model="curNetwork.mapped_listeners"
|
||||
:placeholder="t('chips_placeholder', ['tcp://123.123.123.123:11223'])" class="w-full" multiple fluid
|
||||
:suggestions="peerSuggestions" @complete="searchPeerSuggestions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<div class="flex pt-6 justify-center">
|
||||
<Button :label="t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
||||
@click="$emit('runNetwork', curNetwork)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
103
easytier-web/frontend-lib/src/components/ConfigEditDialog.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { NetworkConfig } from '../types/network';
|
||||
import { Divider, Button, Dialog, Textarea } from 'primevue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
generateConfig: {
|
||||
type: Object as () => (config: NetworkConfig) => Promise<string>,
|
||||
required: true,
|
||||
},
|
||||
saveConfig: {
|
||||
type: Object as () => (config: string) => Promise<void>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const curNetwork = defineModel('curNetwork', {
|
||||
type: Object as () => NetworkConfig | undefined,
|
||||
required: true,
|
||||
})
|
||||
|
||||
const visible = defineModel('visible', {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
})
|
||||
watch([visible, curNetwork], async ([newVisible, newCurNetwork]) => {
|
||||
if (!newVisible) {
|
||||
tomlConfig.value = '';
|
||||
return;
|
||||
}
|
||||
if (!newCurNetwork) {
|
||||
tomlConfig.value = '';
|
||||
return;
|
||||
}
|
||||
const config = newCurNetwork;
|
||||
try {
|
||||
errorMessage.value = '';
|
||||
tomlConfig.value = await props.generateConfig(config);
|
||||
} catch (e) {
|
||||
errorMessage.value = 'Failed to generate config: ' + (e instanceof Error ? e.message : String(e));
|
||||
tomlConfig.value = '';
|
||||
}
|
||||
})
|
||||
onMounted(async () => {
|
||||
if (!visible.value) {
|
||||
return;
|
||||
}
|
||||
if (!curNetwork.value) {
|
||||
tomlConfig.value = '';
|
||||
return;
|
||||
}
|
||||
const config = curNetwork.value;
|
||||
try {
|
||||
tomlConfig.value = await props.generateConfig(config);
|
||||
errorMessage.value = '';
|
||||
} catch (e) {
|
||||
errorMessage.value = 'Failed to generate config: ' + (e instanceof Error ? e.message : String(e));
|
||||
tomlConfig.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
const handleConfigSave = async () => {
|
||||
if (props.readonly) return;
|
||||
try {
|
||||
await props.saveConfig(tomlConfig.value);
|
||||
visible.value = false;
|
||||
} catch (e) {
|
||||
errorMessage.value = 'Failed to save config: ' + (e instanceof Error ? e.message : String(e));
|
||||
}
|
||||
};
|
||||
|
||||
const tomlConfig = ref<string>('')
|
||||
const tomlConfigRows = ref<number>(1);
|
||||
const errorMessage = ref<string>('');
|
||||
|
||||
watch(tomlConfig, (newValue) => {
|
||||
tomlConfigRows.value = newValue.split('\n').length;
|
||||
errorMessage.value = '';
|
||||
});
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<Dialog v-model:visible="visible" modal :header="t('config_file')" :style="{ width: '70%' }">
|
||||
<pre v-if="errorMessage"
|
||||
class="mb-2 p-2 rounded text-sm overflow-auto bg-red-100 text-red-700 max-h-40">{{ errorMessage }}</pre>
|
||||
<div class="flex w-full" style="max-height: 60vh; overflow-y: auto;">
|
||||
<Textarea v-model="tomlConfig" class="w-full h-full font-mono flex flex-col resize-none" :rows="tomlConfigRows"
|
||||
spellcheck="false" :readonly="props.readonly"></Textarea>
|
||||
</div>
|
||||
<Divider />
|
||||
<div class="flex gap-2 justify-end">
|
||||
<Button v-if="!props.readonly" type="button" :label="t('save')" @click="handleConfigSave" />
|
||||
<Button type="button" :label="t('close')" @click="visible = false" />
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { EventType } from '~/types/network'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { EventType } from '../types/network'
|
||||
import { computed } from 'vue';
|
||||
import { Fieldset } from 'primevue';
|
||||
|
||||
const props = defineProps<{
|
||||
event: {
|
||||
@@ -1,41 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { useTimeAgo } from '@vueuse/core'
|
||||
import { IPv4, IPv6 } from 'ip-num/IPNumber'
|
||||
import type { NodeInfo, PeerRoutePair } from '~/types/network'
|
||||
import { IPv4 } from 'ip-num/IPNumber'
|
||||
import { NetworkInstance, type NodeInfo, type PeerRoutePair } from '../types/network'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { ipv4InetToString, ipv4ToString, ipv6ToString } from '../modules/utils';
|
||||
import { DataTable, Column, Tag, Chip, Button, Dialog, ScrollPanel, Timeline, Divider, Card, } from 'primevue';
|
||||
|
||||
const props = defineProps<{
|
||||
instanceId?: string
|
||||
curNetworkInst: NetworkInstance | null,
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const curNetwork = computed(() => {
|
||||
if (props.instanceId) {
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === props.instanceId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
const curNetworkInst = computed(() => {
|
||||
return networkStore.networkInstances.find(n => n.instance_id === curNetwork.value.instance_id)
|
||||
})
|
||||
|
||||
const peerRouteInfos = computed(() => {
|
||||
if (curNetworkInst.value) {
|
||||
const my_node_info = curNetworkInst.value.detail?.my_node_info
|
||||
if (props.curNetworkInst) {
|
||||
const my_node_info = props.curNetworkInst.detail?.my_node_info
|
||||
return [{
|
||||
route: {
|
||||
ipv4_addr: my_node_info?.virtual_ipv4,
|
||||
hostname: my_node_info?.hostname,
|
||||
version: my_node_info?.version,
|
||||
},
|
||||
}, ...(curNetworkInst.value.detail?.peer_route_pairs || [])]
|
||||
}, ...(props.curNetworkInst.detail?.peer_route_pairs || [])]
|
||||
}
|
||||
|
||||
return []
|
||||
@@ -116,14 +103,18 @@ function ipFormat(info: PeerRoutePair) {
|
||||
const ip = info.route.ipv4_addr
|
||||
if (typeof ip === 'string')
|
||||
return ip
|
||||
return ip ? `${num2ipv4(ip.address)}/${ip.network_length}` : ''
|
||||
return ip ? `${IPv4.fromNumber(ip.address.addr)}/${ip.network_length}` : ''
|
||||
}
|
||||
|
||||
function tunnelProto(info: PeerRoutePair) {
|
||||
return [...new Set(info.peer?.conns.map(c => c.tunnel?.tunnel_type))].join(',')
|
||||
}
|
||||
|
||||
const myNodeInfo = computed(() => {
|
||||
if (!curNetworkInst.value)
|
||||
if (!props.curNetworkInst)
|
||||
return {} as NodeInfo
|
||||
|
||||
return curNetworkInst.value.detail?.my_node_info
|
||||
return props.curNetworkInst.detail?.my_node_info
|
||||
})
|
||||
|
||||
interface Chip {
|
||||
@@ -132,16 +123,16 @@ interface Chip {
|
||||
}
|
||||
|
||||
const myNodeInfoChips = computed(() => {
|
||||
if (!curNetworkInst.value)
|
||||
if (!props.curNetworkInst)
|
||||
return []
|
||||
|
||||
const chips: Array<Chip> = []
|
||||
const my_node_info = curNetworkInst.value.detail?.my_node_info
|
||||
const my_node_info = props.curNetworkInst.detail?.my_node_info
|
||||
if (!my_node_info)
|
||||
return chips
|
||||
|
||||
// TUN Device Name
|
||||
const dev_name = curNetworkInst.value.detail?.dev_name
|
||||
const dev_name = props.curNetworkInst.detail?.dev_name
|
||||
if (dev_name) {
|
||||
chips.push({
|
||||
label: `TUN Device Name: ${dev_name}`,
|
||||
@@ -151,7 +142,7 @@ const myNodeInfoChips = computed(() => {
|
||||
|
||||
// virtual ipv4
|
||||
chips.push({
|
||||
label: `Virtual IPv4: ${my_node_info.virtual_ipv4}`,
|
||||
label: `Virtual IPv4: ${ipv4InetToString(my_node_info.virtual_ipv4)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
|
||||
@@ -159,7 +150,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const local_ipv4s = my_node_info.ips?.interface_ipv4s
|
||||
for (const [idx, ip] of local_ipv4s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv4 ${idx}: ${num2ipv4(ip)}`,
|
||||
label: `Local IPv4 ${idx}: ${ipv4ToString(ip)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -168,7 +159,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const local_ipv6s = my_node_info.ips?.interface_ipv6s
|
||||
for (const [idx, ip] of local_ipv6s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv6 ${idx}: ${num2ipv6(ip)}`,
|
||||
label: `Local IPv6 ${idx}: ${ipv6ToString(ip)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -185,11 +176,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const public_ipv6 = my_node_info.ips?.public_ipv6
|
||||
if (public_ipv6) {
|
||||
chips.push({
|
||||
label: `Public IPv6: ${IPv6.fromBigInt((BigInt(public_ipv6.part1) << BigInt(96))
|
||||
+ (BigInt(public_ipv6.part2) << BigInt(64))
|
||||
+ (BigInt(public_ipv6.part3) << BigInt(32))
|
||||
+ BigInt(public_ipv6.part4),
|
||||
)}`,
|
||||
label: `Public IPv6: ${ipv6ToString(public_ipv6)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -198,7 +185,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const listeners = my_node_info.listeners
|
||||
for (const [idx, listener] of listeners?.entries()) {
|
||||
chips.push({
|
||||
label: `Listener ${idx}: ${listener}`,
|
||||
label: `Listener ${idx}: ${listener.url}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -308,28 +295,30 @@ function showVpnPortalConfig() {
|
||||
}
|
||||
|
||||
function showEventLogs() {
|
||||
const detail = curNetworkInst.value?.detail
|
||||
const detail = props.curNetworkInst?.detail
|
||||
if (!detail)
|
||||
return
|
||||
|
||||
dialogContent.value = detail.events
|
||||
dialogContent.value = detail.events.map((event: string) => JSON.parse(event))
|
||||
dialogHeader.value = 'event_log'
|
||||
dialogVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" class="w-2/3 h-auto">
|
||||
<div class="frontend-lib">
|
||||
<Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" class="w-full h-auto max-h-full"
|
||||
:baseZIndex="2000">
|
||||
<ScrollPanel v-if="dialogHeader === 'vpn_portal_config'">
|
||||
<pre>{{ dialogContent }}</pre>
|
||||
</ScrollPanel>
|
||||
<Timeline v-else :value="dialogContent">
|
||||
<template #opposite="slotProps">
|
||||
<small class="text-surface-500 dark:text-surface-400">{{ useTimeAgo(Date.parse(slotProps.item[0])) }}</small>
|
||||
<small class="text-surface-500 dark:text-surface-400">{{ useTimeAgo(Date.parse(slotProps.item.time))
|
||||
}}</small>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
<HumanEvent :event="slotProps.item[1]" />
|
||||
<HumanEvent :event="slotProps.item.event" />
|
||||
</template>
|
||||
</Timeline>
|
||||
</Dialog>
|
||||
@@ -339,7 +328,7 @@ function showEventLogs() {
|
||||
Run Network Error
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="flex flex-column gap-y-5">
|
||||
<div class="flex flex-col gap-y-5">
|
||||
<div class="text-red-500">
|
||||
{{ curNetworkInst.error_msg }}
|
||||
</div>
|
||||
@@ -353,12 +342,9 @@ function showEventLogs() {
|
||||
{{ t('my_node_info') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="flex w-full flex-column gap-y-5">
|
||||
<div class="flex w-full flex-col gap-y-5">
|
||||
<div class="m-0 flex flex-row justify-center gap-x-5">
|
||||
<div
|
||||
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid green"
|
||||
>
|
||||
<div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid green">
|
||||
<div class="font-bold">
|
||||
{{ t('peer_count') }}
|
||||
</div>
|
||||
@@ -367,10 +353,7 @@ function showEventLogs() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid purple"
|
||||
>
|
||||
<div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid purple">
|
||||
<div class="font-bold">
|
||||
{{ t('upload') }}
|
||||
</div>
|
||||
@@ -379,10 +362,7 @@ function showEventLogs() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid fuchsia"
|
||||
>
|
||||
<div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid fuchsia">
|
||||
<div class="font-bold">
|
||||
{{ t('download') }}
|
||||
</div>
|
||||
@@ -392,11 +372,9 @@ function showEventLogs() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row align-items-center flex-wrap w-full max-h-40 overflow-scroll">
|
||||
<Chip
|
||||
v-for="(chip, i) in myNodeInfoChips" :key="i" :label="chip.label" :icon="chip.icon"
|
||||
class="mr-2 mt-2 text-sm"
|
||||
/>
|
||||
<div class="flex flex-row items-center flex-wrap w-full max-h-40 overflow-scroll">
|
||||
<Chip v-for="(chip, i) in myNodeInfoChips" :key="i" :label="chip.label" :icon="chip.icon"
|
||||
class="mr-2 mt-2 text-sm" />
|
||||
</div>
|
||||
|
||||
<div v-if="myNodeInfo" class="m-0 flex flex-row justify-center gap-x-5 text-sm">
|
||||
@@ -418,10 +396,8 @@ function showEventLogs() {
|
||||
<Column :field="ipFormat" :header="t('virtual_ipv4')" />
|
||||
<Column :header="t('hostname')">
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
v-if="!slotProps.data.route.cost || !slotProps.data.route.feature_flag.is_public_server"
|
||||
v-tooltip="slotProps.data.route.hostname"
|
||||
>
|
||||
<div v-if="!slotProps.data.route.cost || !slotProps.data.route.feature_flag.is_public_server"
|
||||
v-tooltip="slotProps.data.route.hostname">
|
||||
{{
|
||||
slotProps.data.route.hostname }}
|
||||
</div>
|
||||
@@ -429,13 +405,14 @@ function showEventLogs() {
|
||||
<Tag v-if="slotProps.data.route.feature_flag.is_public_server" severity="info" value="Info">
|
||||
{{ t('status.server') }}
|
||||
</Tag>
|
||||
<Tag v-if="slotProps.data.route.no_relay_data" severity="warn" value="Warn">
|
||||
<Tag v-if="slotProps.data.route.feature_flag.avoid_relay_data" severity="warn" value="Warn">
|
||||
{{ t('status.relay') }}
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column :field="routeCost" :header="t('route_cost')" />
|
||||
<Column :field="tunnelProto" :header="t('tunnel_proto')" />
|
||||
<Column :field="latencyMs" :header="t('latency')" />
|
||||
<Column :field="txBytes" :header="t('upload_bytes')" />
|
||||
<Column :field="rxBytes" :header="t('download_bytes')" />
|
||||
3
easytier-web/frontend-lib/src/components/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Config } from './Config.vue';
|
||||
export { default as Status } from './Status.vue';
|
||||
export { default as ConfigEditDialog } from './ConfigEditDialog.vue';
|
||||
51
easytier-web/frontend-lib/src/easytier-frontend-lib.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import './style.css'
|
||||
|
||||
import type { App } from 'vue';
|
||||
import { Config, Status, ConfigEditDialog } from "./components";
|
||||
import Aura from '@primevue/themes/aura'
|
||||
import PrimeVue from 'primevue/config'
|
||||
|
||||
import I18nUtils from './modules/i18n'
|
||||
import * as NetworkTypes from './types/network'
|
||||
import HumanEvent from './components/HumanEvent.vue';
|
||||
|
||||
// do not use primevue tooltip, it has serious memory leak issue
|
||||
// https://github.com/primefaces/primevue/issues/5856
|
||||
// import Tooltip from 'primevue/tooltip';
|
||||
import { vTooltip } from 'floating-vue';
|
||||
|
||||
import * as Api from './modules/api';
|
||||
import * as Utils from './modules/utils';
|
||||
|
||||
export default {
|
||||
install: (app: App): void => {
|
||||
app.use(I18nUtils.i18n, { useScope: 'global' })
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Aura,
|
||||
options: {
|
||||
prefix: 'p',
|
||||
darkModeSelector: 'system',
|
||||
cssLayer: {
|
||||
name: 'primevue',
|
||||
order: 'tailwind-base, primevue, tailwind-utilities'
|
||||
}
|
||||
},
|
||||
},
|
||||
zIndex: {
|
||||
modal: 1100, //dialog, drawer
|
||||
overlay: 1200, //select, popover
|
||||
menu: 1300, //overlay menus
|
||||
tooltip: 1400 //tooltip
|
||||
}
|
||||
});
|
||||
|
||||
app.component('Config', Config);
|
||||
app.component('ConfigEditDialog', ConfigEditDialog);
|
||||
app.component('Status', Status);
|
||||
app.component('HumanEvent', HumanEvent);
|
||||
app.directive('tooltip', vTooltip as any);
|
||||
}
|
||||
};
|
||||
|
||||
export { Config, ConfigEditDialog, Status, I18nUtils, NetworkTypes, Api, Utils };
|
||||
205
easytier-web/frontend-lib/src/locales/cn.yaml
Normal file
@@ -0,0 +1,205 @@
|
||||
network: 网络
|
||||
networking_method: 网络方式
|
||||
public_server: 公共服务器
|
||||
manual: 手动
|
||||
standalone: 独立
|
||||
virtual_ipv4: 虚拟IPv4地址
|
||||
virtual_ipv4_dhcp: DHCP
|
||||
network_name: 网络名称
|
||||
network_secret: 网络密码
|
||||
public_server_url: 公共服务器地址
|
||||
peer_urls: 对等节点地址
|
||||
proxy_cidrs: 子网代理CIDR
|
||||
enable_vpn_portal: 启用VPN门户
|
||||
vpn_portal_listen_port: 监听端口
|
||||
vpn_portal_client_network: 客户端子网
|
||||
dev_name: TUN接口名称
|
||||
advanced_settings: 高级设置
|
||||
basic_settings: 基础设置
|
||||
listener_urls: 监听地址
|
||||
rpc_port: RPC端口
|
||||
rpc_portal_whitelists: RPC白名单
|
||||
config_network: 配置网络
|
||||
running: 运行中
|
||||
error_msg: 错误信息
|
||||
detail: 详情
|
||||
add_new_network: 添加新网络
|
||||
del_cur_network: 删除当前网络
|
||||
select_network: 选择网络
|
||||
network_instances: 网络实例
|
||||
instance_id: 实例ID
|
||||
network_infos: 网络信息
|
||||
parse_network_config: 解析网络配置
|
||||
retain_network_instance: 保留网络实例
|
||||
collect_network_infos: 收集网络信息
|
||||
settings: 设置
|
||||
exchange_language: Switch to English
|
||||
logging: 日志
|
||||
logging_level_info: 信息
|
||||
logging_level_debug: 调试
|
||||
logging_level_warn: 警告
|
||||
logging_level_trace: 跟踪
|
||||
logging_level_off: 关闭
|
||||
logging_open_dir: 打开日志目录
|
||||
logging_copy_dir: 复制日志路径
|
||||
disable_auto_launch: 关闭开机自启
|
||||
enable_auto_launch: 开启开机自启
|
||||
exit: 退出
|
||||
chips_placeholder: 例如: {0}, 按回车添加
|
||||
hostname_placeholder: '留空默认为主机名: {0}'
|
||||
dev_name_placeholder: 注意:当多个网络同时使用相同的TUN接口名称时,将会在设置TUN的IP时产生冲突,留空以自动生成随机名称
|
||||
off_text: 点击关闭
|
||||
on_text: 点击开启
|
||||
show_config: 显示配置
|
||||
edit_config: 编辑配置文件
|
||||
config_file: 配置文件
|
||||
close: 关闭
|
||||
save: 保存
|
||||
config_saved: 配置已保存
|
||||
|
||||
use_latency_first: 延迟优先模式
|
||||
my_node_info: 当前节点信息
|
||||
peer_count: 已连接
|
||||
upload: 上传
|
||||
download: 下载
|
||||
show_vpn_portal_config: 显示VPN门户配置
|
||||
vpn_portal_config: VPN门户配置
|
||||
show_event_log: 显示事件日志
|
||||
event_log: 事件日志
|
||||
peer_info: 节点信息
|
||||
hostname: 主机名
|
||||
route_cost: 路由
|
||||
tunnel_proto: 协议
|
||||
latency: 延迟
|
||||
upload_bytes: 上传
|
||||
download_bytes: 下载
|
||||
loss_rate: 丢包率
|
||||
|
||||
flags_switch: 功能开关
|
||||
|
||||
latency_first: 开启延迟优先模式
|
||||
latency_first_help: 忽略中转跳数,选择总延迟最低的路径
|
||||
|
||||
use_smoltcp: 使用用户态协议栈
|
||||
use_smoltcp_help: 使用用户态 TCP/IP 协议栈,避免操作系统防火墙问题导致无法子网代理 / KCP代理。
|
||||
|
||||
enable_kcp_proxy: 启用 KCP 代理
|
||||
enable_kcp_proxy_help: 将 TCP 流量转为 KCP 流量,降低传输延迟,提升传输速度。
|
||||
|
||||
disable_kcp_input: 禁用 KCP 输入
|
||||
disable_kcp_input_help: 禁用 KCP 入站流量,其他开启 KCP 代理的节点仍然使用 TCP 连接到本节点。
|
||||
|
||||
enable_quic_proxy: 启用 QUIC 代理
|
||||
enable_quic_proxy_help: 将 TCP 流量转为 QUIC 流量,降低传输延迟,提升传输速度。
|
||||
|
||||
disable_quic_input: 禁用 QUIC 输入
|
||||
disable_quic_input_help: 禁用 QUIC 入站流量,其他开启 QUIC 代理的节点仍然使用 TCP 连接到本节点。
|
||||
|
||||
disable_p2p: 禁用 P2P
|
||||
disable_p2p_help: 禁用 P2P 模式,所有流量通过手动指定的服务器中转。
|
||||
|
||||
bind_device: 仅使用物理网卡
|
||||
bind_device_help: 仅使用物理网卡,避免 EasyTier 通过其他虚拟网建立连接。
|
||||
|
||||
no_tun: 无 TUN 模式
|
||||
no_tun_help: 不使用 TUN 网卡,适合无管理员权限时使用。本节点仅允许被访问。访问其他节点需要使用 SOCK5
|
||||
|
||||
enable_exit_node: 启用出口节点
|
||||
enable_exit_node_help: 允许此节点成为出口节点
|
||||
|
||||
relay_all_peer_rpc: 转发RPC包
|
||||
relay_all_peer_rpc_help: |
|
||||
允许转发所有对等节点的RPC数据包,即使对等节点不在转发网络白名单中。
|
||||
这可以帮助白名单外网络中的对等节点建立P2P连接。
|
||||
|
||||
multi_thread: 启用多线程
|
||||
multi_thread_help: 使用多线程运行时
|
||||
|
||||
proxy_forward_by_system: 系统转发
|
||||
proxy_forward_by_system_help: 通过系统内核转发子网代理数据包,禁用内置NAT
|
||||
|
||||
disable_encryption: 禁用加密
|
||||
disable_encryption_help: 禁用对等节点通信的加密,默认为false,必须与对等节点相同
|
||||
|
||||
disable_udp_hole_punching: 禁用UDP打洞
|
||||
disable_udp_hole_punching_help: 禁用UDP打洞功能
|
||||
|
||||
enable_magic_dns: 启用魔法DNS
|
||||
enable_magic_dns_help: |
|
||||
启用魔法DNS,允许通过EasyTier的DNS服务器访问其他节点的虚拟IPv4地址, 如 node1.et.net。
|
||||
|
||||
enable_private_mode: 启用私有模式
|
||||
enable_private_mode_help: |
|
||||
启用私有模式,则不允许使用了与本网络不相同的网络名称和密码的节点通过本节点进行握手或中转。
|
||||
|
||||
relay_network_whitelist: 网络白名单
|
||||
relay_network_whitelist_help: |
|
||||
仅转发白名单网络的流量,支持通配符字符串。多个网络名称间可以使用英文空格间隔。
|
||||
如果该参数为空,则禁用转发。默认允许所有网络。
|
||||
例如:'*'(所有网络),'def*'(以def为前缀的网络),'net1 net2'(只允许net1和net2)
|
||||
|
||||
manual_routes: 自定义路由
|
||||
manual_routes_help: 手动分配路由CIDR,将禁用子网代理和从对等节点传播的wireguard路由。例如:192.168.0.0/16
|
||||
|
||||
socks5: socks5服务器
|
||||
socks5_help: |
|
||||
启用 socks5 服务器,允许 socks5 客户端访问虚拟网络. 格式: <端口>,例如:1080
|
||||
|
||||
exit_nodes: 出口节点列表
|
||||
exit_nodes_help: 转发所有流量的出口节点,虚拟IPv4地址,优先级由列表顺序决定
|
||||
|
||||
mtu: MTU
|
||||
mtu_help: |
|
||||
TUN设备的MTU,默认为非加密时为1380,加密时为1360。范围:400-1380
|
||||
mtu_placeholder: 留空为默认值1380
|
||||
|
||||
mapped_listeners: 监听映射
|
||||
mapped_listeners_help: |
|
||||
手动指定监听器的公网地址,其他节点可以使用该地址连接到本节点。
|
||||
例如:tcp://123.123.123.123:11223,可以指定多个。
|
||||
|
||||
status:
|
||||
version: 内核版本
|
||||
local: 本机
|
||||
server: 服务器
|
||||
relay: 中继
|
||||
|
||||
run_network: 运行网络
|
||||
stop_network: 停止网络
|
||||
network_running: 运行中
|
||||
network_stopped: 已停止
|
||||
dhcp_experimental_warning: 实验性警告!使用DHCP时如果组网环境中发生IP冲突,将自动更改IP。
|
||||
|
||||
tray:
|
||||
show: 显示 / 隐藏
|
||||
exit: 退出
|
||||
|
||||
about:
|
||||
title: 关于
|
||||
version: 版本
|
||||
author: 作者
|
||||
homepage: 主页
|
||||
license: 许可证
|
||||
description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||
check_update: 检查更新
|
||||
|
||||
event:
|
||||
Unknown: 未知
|
||||
TunDeviceReady: Tun设备就绪
|
||||
TunDeviceError: Tun设备错误
|
||||
PeerAdded: 对端添加
|
||||
PeerRemoved: 对端移除
|
||||
PeerConnAdded: 对端连接添加
|
||||
PeerConnRemoved: 对端连接移除
|
||||
ListenerAdded: 监听器添加
|
||||
ListenerAddFailed: 监听器添加失败
|
||||
ListenerAcceptFailed: 监听器接受连接失败
|
||||
ConnectionAccepted: 连接已接受
|
||||
ConnectionError: 连接错误
|
||||
Connecting: 正在连接
|
||||
ConnectError: 连接错误
|
||||
VpnPortalClientConnected: VPN门户客户端已连接
|
||||
VpnPortalClientDisconnected: VPN门户客户端已断开连接
|
||||
DhcpIpv4Changed: DHCP IPv4地址更改
|
||||
DhcpIpv4Conflicted: DHCP IPv4地址冲突
|
||||
PortForwardAdded: 端口转发添加
|
||||
205
easytier-web/frontend-lib/src/locales/en.yaml
Normal file
@@ -0,0 +1,205 @@
|
||||
network: Network
|
||||
networking_method: Networking Method
|
||||
public_server: Public Server
|
||||
manual: Manual
|
||||
standalone: Standalone
|
||||
virtual_ipv4: Virtual IPv4
|
||||
virtual_ipv4_dhcp: DHCP
|
||||
network_name: Network Name
|
||||
network_secret: Network Secret
|
||||
public_server_url: Public Server URL
|
||||
peer_urls: Peer URLs
|
||||
proxy_cidrs: Subnet Proxy CIDRs
|
||||
enable_vpn_portal: Enable VPN Portal
|
||||
vpn_portal_listen_port: VPN Portal Listen Port
|
||||
vpn_portal_client_network: Client Sub Network
|
||||
dev_name: TUN interface name
|
||||
advanced_settings: Advanced Settings
|
||||
basic_settings: Basic Settings
|
||||
listener_urls: Listener URLs
|
||||
rpc_port: RPC Port
|
||||
rpc_portal_whitelists: RPC Whitelist
|
||||
config_network: Config Network
|
||||
running: Running
|
||||
error_msg: Error Message
|
||||
detail: Detail
|
||||
add_new_network: New Network
|
||||
del_cur_network: Delete Current Network
|
||||
select_network: Select Network
|
||||
network_instances: Network Instances
|
||||
instance_id: Instance ID
|
||||
network_infos: Network Infos
|
||||
parse_network_config: Parse Network Config
|
||||
retain_network_instance: Retain Network Instance
|
||||
collect_network_infos: Collect Network Infos
|
||||
settings: Settings
|
||||
exchange_language: 切换中文
|
||||
logging: Logging
|
||||
logging_level_info: Info
|
||||
logging_level_debug: Debug
|
||||
logging_level_warn: Warn
|
||||
logging_level_trace: Trace
|
||||
logging_level_off: Off
|
||||
logging_open_dir: Open Log Directory
|
||||
logging_copy_dir: Copy Log Path
|
||||
disable_auto_launch: Disable Launch on Reboot
|
||||
enable_auto_launch: Enable Launch on Reboot
|
||||
exit: Exit
|
||||
use_latency_first: Latency First Mode
|
||||
chips_placeholder: 'e.g: {0}, press Enter to add'
|
||||
hostname_placeholder: 'Leave blank and default to host name: {0}'
|
||||
dev_name_placeholder: 'Note: When multiple networks use the same TUN interface name at the same time, there will be a conflict when setting the TUN''s IP. Leave blank to automatically generate a random name.'
|
||||
off_text: Press to disable
|
||||
on_text: Press to enable
|
||||
show_config: Show Config
|
||||
edit_config: Edit Config File
|
||||
config_file: Config File
|
||||
close: Close
|
||||
save: Save
|
||||
config_saved: Configuration saved
|
||||
my_node_info: My Node Info
|
||||
peer_count: Connected
|
||||
upload: Upload
|
||||
download: Download
|
||||
show_vpn_portal_config: Show VPN Portal Config
|
||||
vpn_portal_config: VPN Portal Config
|
||||
show_event_log: Show Event Log
|
||||
event_log: Event Log
|
||||
peer_info: Peer Info
|
||||
route_cost: Route Cost
|
||||
tunnel_proto: Protocol
|
||||
hostname: Hostname
|
||||
latency: Latency
|
||||
upload_bytes: Upload
|
||||
download_bytes: Download
|
||||
loss_rate: Loss Rate
|
||||
|
||||
flags_switch: Feature Switch
|
||||
|
||||
latency_first: Enable Latency-First Mode
|
||||
latency_first_help: Ignore hop count and select the path with the lowest total latency
|
||||
|
||||
use_smoltcp: Use User-Space Protocol Stack
|
||||
use_smoltcp_help: Use a user-space TCP/IP stack to avoid issues with operating system firewalls blocking subnet or KCP proxy functionality.
|
||||
|
||||
enable_kcp_proxy: Enable KCP Proxy
|
||||
enable_kcp_proxy_help: Convert TCP traffic to KCP traffic to reduce latency and boost transmission speed.
|
||||
|
||||
disable_kcp_input: Disable KCP Input
|
||||
disable_kcp_input_help: Disable inbound KCP traffic, while nodes with KCP proxy enabled continue to connect using TCP.
|
||||
|
||||
enable_quic_proxy: Enable QUIC Proxy
|
||||
enable_quic_proxy_help: Convert TCP traffic to QUIC traffic to reduce latency and boost transmission speed.
|
||||
|
||||
disable_quic_input: Disable QUIC Input
|
||||
disable_quic_input_help: Disable inbound QUIC traffic, while nodes with QUIC proxy enabled continue to connect using TCP.
|
||||
|
||||
disable_p2p: Disable P2P
|
||||
disable_p2p_help: Disable P2P mode; route all traffic through a manually specified relay server.
|
||||
|
||||
bind_device: Bind to Physical Device Only
|
||||
bind_device_help: Use only the physical network interface to prevent EasyTier from connecting via virtual networks.
|
||||
|
||||
no_tun: No TUN Mode
|
||||
no_tun_help: Do not use a TUN interface, suitable for environments without administrator privileges. This node is only accessible; accessing other nodes requires SOCKS5.
|
||||
|
||||
enable_exit_node: Enable Exit Node
|
||||
enable_exit_node_help: Allow this node to be an exit node
|
||||
|
||||
relay_all_peer_rpc: Relay RPC Packets
|
||||
relay_all_peer_rpc_help: |
|
||||
Relay all peer rpc packets, even if the peer is not in the relay network whitelist.
|
||||
This can help peers not in relay network whitelist to establish p2p connection.
|
||||
|
||||
multi_thread: Multi Thread
|
||||
multi_thread_help: Use multi-thread runtime
|
||||
|
||||
proxy_forward_by_system: System Forward
|
||||
proxy_forward_by_system_help: Forward packet to proxy networks via system kernel, disable internal nat for network proxy
|
||||
|
||||
disable_encryption: Disable Encryption
|
||||
disable_encryption_help: Disable encryption for peers communication, default is false, must be same with peers
|
||||
|
||||
disable_udp_hole_punching: Disable UDP Hole Punching
|
||||
disable_udp_hole_punching_help: Disable udp hole punching
|
||||
|
||||
enable_magic_dns: Enable Magic DNS
|
||||
enable_magic_dns_help: |
|
||||
Enable magic dns, all nodes in the network can access each other by domain name, e.g.: node1.et.net.
|
||||
|
||||
enable_private_mode: Enable Private Mode
|
||||
enable_private_mode_help: |
|
||||
Enable private mode, nodes with different network names or passwords from this network are not allowed to perform handshake or relay through this node.
|
||||
|
||||
relay_network_whitelist: Network Whitelist
|
||||
relay_network_whitelist_help: |
|
||||
Only forward traffic from the whitelist networks, supporting wildcard strings, multiple network names can be separated by spaces.
|
||||
If this parameter is empty, forwarding is disabled. By default, all networks are allowed.
|
||||
e.g.: '*' (all networks), 'def*' (networks with the prefix 'def'), 'net1 net2' (only allow net1 and net2)
|
||||
|
||||
manual_routes: Manual Route
|
||||
manual_routes_help: |
|
||||
Assign routes cidr manually, will disable subnet proxy and wireguard routes propagated from peers. e.g.:192.168.0.0/16
|
||||
|
||||
socks5: Socks5 Server
|
||||
socks5_help: |
|
||||
Enable socks5 server, allow socks5 client to access virtual network. format: <port>, e.g.: 1080
|
||||
|
||||
exit_nodes: Exit Nodes
|
||||
exit_nodes_help: Exit nodes to forward all traffic to, a virtual ipv4 address, priority is determined by the order of the list
|
||||
|
||||
mtu: MTU
|
||||
mtu_help: |
|
||||
MTU of the TUN device, default is 1380 for non-encryption, 1360 for encryption. Range:400-1380
|
||||
mtu_placeholder: Leave blank as default value 1380
|
||||
|
||||
mapped_listeners: Map Listeners
|
||||
mapped_listeners_help: |
|
||||
Manually specify the public address of the listener, other nodes can use this address to connect to this node.
|
||||
e.g.: tcp://123.123.123.123:11223, can specify multiple.
|
||||
|
||||
status:
|
||||
version: Version
|
||||
local: Local
|
||||
server: Server
|
||||
relay: Relay
|
||||
|
||||
run_network: Run Network
|
||||
stop_network: Stop Network
|
||||
network_running: running
|
||||
network_stopped: stopped
|
||||
dhcp_experimental_warning: Experimental warning! if there is an IP conflict in the network when using DHCP, the IP will be automatically changed.
|
||||
|
||||
tray:
|
||||
show: Show / Hide
|
||||
exit: Exit
|
||||
|
||||
about:
|
||||
title: About
|
||||
version: Version
|
||||
author: Author
|
||||
homepage: Homepage
|
||||
license: License
|
||||
description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.'
|
||||
check_update: Check Update
|
||||
|
||||
event:
|
||||
Unknown: Unknown
|
||||
TunDeviceReady: TunDeviceReady
|
||||
TunDeviceError: TunDeviceError
|
||||
PeerAdded: PeerAdded
|
||||
PeerRemoved: PeerRemoved
|
||||
PeerConnAdded: PeerConnAdded
|
||||
PeerConnRemoved: PeerConnRemoved
|
||||
ListenerAdded: ListenerAdded
|
||||
ListenerAddFailed: ListenerAddFailed
|
||||
ListenerAcceptFailed: ListenerAcceptFailed
|
||||
ConnectionAccepted: ConnectionAccepted
|
||||
ConnectionError: ConnectionError
|
||||
Connecting: Connecting
|
||||
ConnectError: ConnectError
|
||||
VpnPortalClientConnected: VpnPortalClientConnected
|
||||
VpnPortalClientDisconnected: VpnPortalClientDisconnected
|
||||
DhcpIpv4Changed: DhcpIpv4Changed
|
||||
DhcpIpv4Conflicted: DhcpIpv4Conflicted
|
||||
PortForwardAdded: PortForwardAdded
|
||||
241
easytier-web/frontend-lib/src/modules/api.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import { Md5 } from 'ts-md5'
|
||||
import { UUID } from './utils';
|
||||
import { NetworkConfig } from '../types/network';
|
||||
|
||||
export interface ValidateConfigResponse {
|
||||
toml_config: string;
|
||||
}
|
||||
|
||||
// 定义接口返回的数据结构
|
||||
export interface LoginResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface RegisterResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
// 定义请求体数据结构
|
||||
export interface Credential {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RegisterData {
|
||||
credentials: Credential;
|
||||
captcha: string;
|
||||
}
|
||||
|
||||
export interface Summary {
|
||||
device_count: number;
|
||||
}
|
||||
|
||||
export interface ListNetworkInstanceIdResponse {
|
||||
running_inst_ids: Array<UUID>,
|
||||
disabled_inst_ids: Array<UUID>,
|
||||
}
|
||||
|
||||
export interface GenerateConfigRequest {
|
||||
config: NetworkConfig;
|
||||
}
|
||||
|
||||
export interface GenerateConfigResponse {
|
||||
toml_config?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface ParseConfigRequest {
|
||||
toml_config: string;
|
||||
}
|
||||
|
||||
export interface ParseConfigResponse {
|
||||
config?: NetworkConfig;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class ApiClient {
|
||||
private client: AxiosInstance;
|
||||
private authFailedCb: Function | undefined;
|
||||
|
||||
constructor(baseUrl: string, authFailedCb: Function | undefined = undefined) {
|
||||
this.client = axios.create({
|
||||
baseURL: baseUrl + '/api/v1',
|
||||
withCredentials: true, // 如果需要支持跨域携带cookie
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
this.authFailedCb = authFailedCb;
|
||||
|
||||
// 添加请求拦截器
|
||||
this.client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
return config;
|
||||
}, (error: any) => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 添加响应拦截器
|
||||
this.client.interceptors.response.use((response: AxiosResponse) => {
|
||||
console.debug('Axios Response:', response);
|
||||
return response.data; // 假设服务器返回的数据都在data属性中
|
||||
}, (error: any) => {
|
||||
if (error.response) {
|
||||
let response: AxiosResponse = error.response;
|
||||
if (response.status == 401 && this.authFailedCb) {
|
||||
console.error('Unauthorized:', response.data);
|
||||
this.authFailedCb();
|
||||
} else {
|
||||
// 请求已发出,但是服务器响应的状态码不在2xx范围
|
||||
console.error('Response Error:', error.response.data);
|
||||
}
|
||||
} else if (error.request) {
|
||||
// 请求已发出,但是没有收到响应
|
||||
console.error('Request Error:', error.request);
|
||||
} else {
|
||||
// 发生了一些问题导致请求未发出
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
// 注册
|
||||
public async register(data: RegisterData): Promise<RegisterResponse> {
|
||||
try {
|
||||
data.credentials.password = Md5.hashStr(data.credentials.password);
|
||||
const response = await this.client.post<RegisterResponse>('/auth/register', data);
|
||||
console.log("register response:", response);
|
||||
return { success: true, message: 'Register success', };
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
return { success: false, message: 'Failed to register, error: ' + JSON.stringify(error.response?.data), };
|
||||
}
|
||||
return { success: false, message: 'Unknown error, error: ' + error, };
|
||||
}
|
||||
}
|
||||
|
||||
// 登录
|
||||
public async login(data: Credential): Promise<LoginResponse> {
|
||||
try {
|
||||
data.password = Md5.hashStr(data.password);
|
||||
const response = await this.client.post<any>('/auth/login', data);
|
||||
console.log("login response:", response);
|
||||
return { success: true, message: 'Login success', };
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
if (error.response?.status === 401) {
|
||||
return { success: false, message: 'Invalid username or password', };
|
||||
} else {
|
||||
return { success: false, message: 'Unknown error, status code: ' + error.response?.status, };
|
||||
}
|
||||
}
|
||||
return { success: false, message: 'Unknown error, error: ' + error, };
|
||||
}
|
||||
}
|
||||
|
||||
public async logout() {
|
||||
await this.client.get('/auth/logout');
|
||||
if (this.authFailedCb) {
|
||||
this.authFailedCb();
|
||||
}
|
||||
}
|
||||
|
||||
public async change_password(new_password: string) {
|
||||
await this.client.put('/auth/password', { new_password: Md5.hashStr(new_password) });
|
||||
}
|
||||
|
||||
public async check_login_status() {
|
||||
try {
|
||||
await this.client.get('/auth/check_login_status');
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async list_session() {
|
||||
const response = await this.client.get('/sessions');
|
||||
return response;
|
||||
}
|
||||
|
||||
public async list_machines(): Promise<Array<any>> {
|
||||
const response = await this.client.get<any, Record<string, Array<any>>>('/machines');
|
||||
return response.machines;
|
||||
}
|
||||
|
||||
public async list_deivce_instance_ids(machine_id: string): Promise<ListNetworkInstanceIdResponse> {
|
||||
const response = await this.client.get<any, ListNetworkInstanceIdResponse>('/machines/' + machine_id + '/networks');
|
||||
return response;
|
||||
}
|
||||
|
||||
public async update_device_instance_state(machine_id: string, inst_id: string, disabled: boolean): Promise<undefined> {
|
||||
await this.client.put<string>('/machines/' + machine_id + '/networks/' + inst_id, {
|
||||
disabled: disabled,
|
||||
});
|
||||
}
|
||||
|
||||
public async get_network_info(machine_id: string, inst_id: string): Promise<any> {
|
||||
const response = await this.client.get<any, Record<string, any>>('/machines/' + machine_id + '/networks/info/' + inst_id);
|
||||
return response.info.map;
|
||||
}
|
||||
|
||||
public async get_network_config(machine_id: string, inst_id: string): Promise<any> {
|
||||
const response = await this.client.get<any, Record<string, any>>('/machines/' + machine_id + '/networks/config/' + inst_id);
|
||||
return response;
|
||||
}
|
||||
|
||||
public async validate_config(machine_id: string, config: any): Promise<ValidateConfigResponse> {
|
||||
const response = await this.client.post<any, ValidateConfigResponse>(`/machines/${machine_id}/validate-config`, {
|
||||
config: config,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async run_network(machine_id: string, config: any): Promise<undefined> {
|
||||
await this.client.post<string>(`/machines/${machine_id}/networks`, {
|
||||
config: config,
|
||||
});
|
||||
}
|
||||
|
||||
public async delete_network(machine_id: string, inst_id: string): Promise<undefined> {
|
||||
await this.client.delete<string>(`/machines/${machine_id}/networks/${inst_id}`);
|
||||
}
|
||||
|
||||
public async get_summary(): Promise<Summary> {
|
||||
const response = await this.client.get<any, Summary>('/summary');
|
||||
return response;
|
||||
}
|
||||
|
||||
public captcha_url() {
|
||||
return this.client.defaults.baseURL + '/auth/captcha';
|
||||
}
|
||||
|
||||
public async generate_config(config: GenerateConfigRequest): Promise<GenerateConfigResponse> {
|
||||
try {
|
||||
const response = await this.client.post<any, GenerateConfigResponse>('/generate-config', config);
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
return { error: error.response?.data };
|
||||
}
|
||||
return { error: 'Unknown error: ' + error };
|
||||
}
|
||||
}
|
||||
|
||||
public async parse_config(config: ParseConfigRequest): Promise<ParseConfigResponse> {
|
||||
try {
|
||||
const response = await this.client.post<any, ParseConfigResponse>('/parse-config', config);
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
return { error: error.response?.data };
|
||||
}
|
||||
return { error: 'Unknown error: ' + error };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiClient;
|
||||
@@ -1,6 +1,9 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import type { Locale } from 'vue-i18n'
|
||||
|
||||
import EnLocale from '../locales/en.yaml'
|
||||
import CnLocale from '../locales/cn.yaml'
|
||||
|
||||
// Import i18n resources
|
||||
// https://vitejs.dev/guide/features.html#glob-import
|
||||
export const i18n = createI18n({
|
||||
@@ -10,10 +13,10 @@ export const i18n = createI18n({
|
||||
messages: {},
|
||||
})
|
||||
|
||||
const localesMap = Object.fromEntries(
|
||||
Object.entries(import.meta.glob('../../locales/*.yml'))
|
||||
.map(([path, loadLocale]) => [path.match(/([\w-]*)\.yml$/)?.[1], loadLocale]),
|
||||
) as Record<Locale, () => Promise<{ default: Record<string, string> }>>
|
||||
const localesMap = {
|
||||
"en": EnLocale,
|
||||
"cn": CnLocale,
|
||||
} as Record<string, any>
|
||||
|
||||
export const availableLocales = Object.keys(localesMap)
|
||||
|
||||
@@ -38,13 +41,19 @@ export async function loadLanguageAsync(lang: string): Promise<Locale> {
|
||||
let messages
|
||||
|
||||
try {
|
||||
messages = await localesMap[lang]()
|
||||
messages = localesMap[lang]
|
||||
}
|
||||
catch {
|
||||
messages = await localesMap.en()
|
||||
messages = localesMap.en
|
||||
}
|
||||
|
||||
i18n.global.setLocaleMessage(lang, messages.default)
|
||||
i18n.global.setLocaleMessage(lang, messages)
|
||||
loadedLanguages.push(lang)
|
||||
return setI18nLanguage(lang)
|
||||
}
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
localesMap,
|
||||
loadLanguageAsync,
|
||||
}
|
||||
108
easytier-web/frontend-lib/src/modules/utils.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { IPv4, IPv6 } from 'ip-num/IPNumber'
|
||||
import { Ipv4Addr, Ipv4Inet, Ipv6Addr } from '../types/network'
|
||||
|
||||
export function ipv4ToString(ip: Ipv4Addr) {
|
||||
return IPv4.fromNumber(ip.addr).toString()
|
||||
}
|
||||
|
||||
export function ipv4InetToString(ip: Ipv4Inet | undefined) {
|
||||
if (ip?.address === undefined) {
|
||||
return 'undefined'
|
||||
}
|
||||
return `${ipv4ToString(ip.address)}/${ip.network_length}`
|
||||
}
|
||||
|
||||
export function ipv6ToString(ip: Ipv6Addr) {
|
||||
return IPv6.fromBigInt(
|
||||
(BigInt(ip.part1) << BigInt(96))
|
||||
+ (BigInt(ip.part2) << BigInt(64))
|
||||
+ (BigInt(ip.part3) << BigInt(32))
|
||||
+ BigInt(ip.part4),
|
||||
)
|
||||
}
|
||||
|
||||
function toHexString(uint64: bigint, padding = 9): string {
|
||||
let hexString = uint64.toString(16);
|
||||
while (hexString.length < padding) {
|
||||
hexString = '0' + hexString;
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
function uint32ToUuid(part1: number, part2: number, part3: number, part4: number): string {
|
||||
// 将两个 uint64 转换为 16 进制字符串
|
||||
const part1Hex = toHexString(BigInt(part1), 8);
|
||||
const part2Hex = toHexString(BigInt(part2), 8);
|
||||
const part3Hex = toHexString(BigInt(part3), 8);
|
||||
const part4Hex = toHexString(BigInt(part4), 8);
|
||||
|
||||
// 构造 UUID 格式字符串
|
||||
const uuid = `${part1Hex.substring(0, 8)}-${part2Hex.substring(0, 4)}-${part2Hex.substring(4, 8)}-${part3Hex.substring(0, 4)}-${part3Hex.substring(4, 8)}${part4Hex.substring(0, 12)}`;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
export interface UUID {
|
||||
part1: number;
|
||||
part2: number;
|
||||
part3: number;
|
||||
part4: number;
|
||||
}
|
||||
|
||||
export function UuidToStr(uuid: UUID): string {
|
||||
return uint32ToUuid(uuid.part1, uuid.part2, uuid.part3, uuid.part4);
|
||||
}
|
||||
|
||||
export interface DeviceInfo {
|
||||
hostname: string;
|
||||
public_ip: string;
|
||||
running_network_count: number;
|
||||
report_time: string;
|
||||
easytier_version: string;
|
||||
running_network_instances?: Array<string>;
|
||||
machine_id: string;
|
||||
}
|
||||
|
||||
export function buildDeviceInfo(device: any): DeviceInfo {
|
||||
let dev_info: DeviceInfo = {
|
||||
hostname: device.info?.hostname,
|
||||
public_ip: device.client_url,
|
||||
running_network_instances: device.info?.running_network_instances.map((instance: any) => UuidToStr(instance)),
|
||||
running_network_count: device.info?.running_network_instances.length,
|
||||
report_time: device.info?.report_time,
|
||||
easytier_version: device.info?.easytier_version,
|
||||
machine_id: UuidToStr(device.info?.machine_id),
|
||||
};
|
||||
|
||||
return dev_info;
|
||||
}
|
||||
|
||||
// write a class to run a function periodically and can be stopped by calling stop(), use setTimeout to trigger the function
|
||||
export class PeriodicTask {
|
||||
private interval: number;
|
||||
private task: (() => Promise<void>) | undefined;
|
||||
private timer: any;
|
||||
|
||||
constructor(task: () => Promise<void>, interval: number) {
|
||||
this.interval = interval;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
_runTaskHelper(nextInterval: number) {
|
||||
this.timer = setTimeout(async () => {
|
||||
if (this.task) {
|
||||
await this.task();
|
||||
this._runTaskHelper(this.interval);
|
||||
}
|
||||
}, nextInterval);
|
||||
}
|
||||
|
||||
start() {
|
||||
this._runTaskHelper(0);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.task = undefined;
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||